2015-03-18 3 views
4

Следующий пример демонстрирует проблему:Сегментация ошибка при переходе к Гото над VLA массива

#include <cstdio> 

int main() 
{ 
     unsigned int remaining=1; 

     goto loop; 

     while(remaining) { 
       unsigned char tmp[remaining]; 
       printf("&tmp: %p\n",tmp); 
loop: 
       remaining = 512;//or something else; 
     } 
} 

Первоначально инициализация «оставшейся» переменной была немного долго, и я использовал goto для его инициализации в одной строке. Однако теперь этот пример дает ошибку сегментации на линии printf.

Похоже, что массив не инициализирован должным образом.

Даже GDB не может напечатать адрес массива TMP в:

Program received signal SIGSEGV, Segmentation fault. 
0x00000000004005b8 in main() at test.cpp:11 
11   printf("&tmp: %p\n",tmp); 
(gdb) p tmp 
$1 = 0xfffffffffffffe00 <error: Cannot access memory at address 0xfffffffffffffe00> 

Моя версия GCC:

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 

компиляции с:

g++ -o testc test.cpp 

Если удалить Гото, или замените переменный массив с фиксированным массивом, ошибка сегментации исчезла. Что происходит на самом деле?

Это ошибка gcc? Если комбинация goto и вариационных массивов не допускается, должно быть предупреждение?

+1

Составьте свой пример с недавним звоном или GCC 4.9 * и они не допустят, чтобы ваш код идти.. – XapaJIaMnu

ответ

5

массивы переменной длины (VLA) являются особенностью C99, что GCC поддерживает, как an extension in C++ и C99 прыжок через декларации VLA не определено поведение, из проекта C99 стандартного раздела 6.8.6.1Гото заявление:

Оператор goto не должен выходить за пределы идентификатора, имеющего измененный тип, который входит в объем этого идентификатора.

clang и gcc 4.9 фактически делает эту ошибку и говорит:

error: goto into protected scope 
    goto loop; 
    ^

note: jump bypasses initialization of variable length array 
      unsigned char tmp[remaining]; 
         ^

gcc bug report: Jumps into VLA or VM scope not rejected for C++ См.

правила для прыжков мимо декларации автоматической переменной в C++ рассматривается в разделе 6.7[stmt.dcl], который говорит:

можно передать в блок, но не в способ, которым обходит объявления с инициализацией. Программа, которая перескакивает87 от , точка, в которой переменная с автоматической продолжительностью хранения не находится в , область до точки, где она находится в области, плохо сформирована, если переменная имеет скалярный тип, тип класса с тривиальным конструктором по умолчанию и тривиальный деструктор, cv-квалифицированная версия одного из этих типов или массив одного из предыдущих типов и объявляется без инициализатора (8.5).[Пример:

void f() { 
    // ... 
    goto lx; // ill-formed: jump into scope of a 
ly: 
    X a = 1; 
    // ... 
lx: 
    goto ly; // OK, jump implies destructor 
      // call for a followed by construction 
      // again immediately following label ly 
} 

-end пример]

+0

Хорошее начало, но это не доказывает, что расширение GCC считает это неопределенным поведением. Если это не документировано где-то, что расширение VLA C++ ведет себя так же, как и на C99. Конечно, на самом деле сам C++ запрещает этот переход, поэтому я бы назвал его ошибкой GCC, чтобы он мог это сделать (но потом перерывы), когда это объявление VLA, которое вы перепрыгиваете. –

+0

gcc 4.9. * Также вызывает аналогичную ошибку. Это, скорее всего, более ранний gcc, который не фиксирует его. – XapaJIaMnu

+0

@XapaJIaMnu Я изначально тестировался с '4.8', но вы правильно' 4.9'. –