2016-08-24 3 views
1

у меня есть структура, которая выглядит вроде как это:Visual Studio обрабатывает неполные массивы нулевой длиной массивов

typedef struct foo { 
    int this; 
    int that; 
    int length; 
    int info[];  // legal for last element of a struct 
} Foo; 

Когда я компилирую, я получаю предупреждение, как это:

C4200 nonstandard extension used: zero-sized array in struct/union 

Я просто живу с предупреждением, или есть какое-то свойство, которое я могу настроить, чтобы сообщить Visual Studio использовать C-99?

+5

Не «живите с» предупреждениями. Размер 'struct' неизвестен. Если вы не знаете размер массива, определите его как указатель и выделите память по мере необходимости. –

+0

Согласен. Я не хочу жить с предупреждениями, я хочу их исправить. Однако использование неполного массива в качестве последнего элемента структуры * * поддерживается в C-99. Использование массивов с нулевой длиной было продолжением как в GNU C, так и в Windows C на протяжении многих лет. https://en.wikipedia.org/wiki/Flexible_array_member –

+0

Следует также добавить: это более старый код, за который я отвечаю за поддержку. Я не писал его, и я не могу его изменить. –

ответ

1

Visual Studio 2015 [почти] полностью реализует C99, но все еще рассматривает все функции C99 как языковые расширения (например, отключение языковых расширений также отключает поддержку C99). Некоторые из этих функций вызывают ложные предупреждения, подобные тем, которые вы наблюдали.

Пока поддержка C99 остается в этом полуофициальном статусе «расширения», просто игнорируйте или отключите такие предупреждения.

Обратите внимание, что VS2015 Update 3 больше не выпускает это предупреждение для такого кода на C.

0

Когда вы создаете массив неопределенной длины (в вашем случае в структуре), неизвестно, сколько памяти должно быть выделено для него. Поэтому при использовании переменной «информация» неизвестно, где в кэш-памяти компьютера программа пишет или читает. Это может привести к сбою программы. Обычно, когда вы хотите использовать массив неопределенной длины, используется указатель. Память может быть выделена для этого указателя, когда известно, насколько велик массив.

Компилятор предупреждает вас о проблеме, описанной выше, но позволяет выполнить код (по крайней мере, это делается в Microsoft Visual studio 2013).

Надеюсь, это вам поможет!

+1

Код в OP - вполне законная и хорошо известная функция языка C. Это не «массив неопределенной длины». Это член гибкого массива C99. И нет, он не может справиться с сбоем программы, если вы используете его правильно. – AnT

0

Для тех, кто интересуется массивом нулевой длины или идиомами «гибкого массива», вероятно, стоит потратить минуту, чтобы объяснить их. Эта идиома такая же старая, как и сама С.

Предположим, вы хотите передать структуру, состоящую из заголовка и переменного объема данных. Неизвестно, пока не будет выделена структура, сколько данных необходимо будет добавить к ней.

оригинальный говор был объявить структуру так:

/* Variation 1 */ 
struct mydata { 
    int type; 
    int datalen; 
    char data[1]; 
}; 

Тогда предположим, что мы хотели, чтобы вернуть один из этих объектов:

struct mydata * 
get_some_data() 
{ 
    int len; 
    struct mydata *rval; 
    len = find_out_how_much_data(); 
    /* Allocate the struct AND enough extra space to hold the data */ 
    rval = malloc(sizeof(*rval) + len - 1); 
    read_data(&rval->data[0], len); 
    return rval; 
} 

И вызывающий абонент получит доступ к его следующим образом:

void caller() 
{ 
    struct mydata *foo = get_some_data(); 
    /* Start accessing foo->datalen bytes of data starting at 
    * foo->data[0] 
    */ 
    free(foo);  /* And free it all */ 
} 

Пункт этой идиомы состоит в том, что декларация char data[1] on - ложь, поскольку данные, несомненно, будут длиннее, чем это, но компилятор C не проводит проверку диапазона, поэтому все круто.

Но обратите внимание на выражение len - 1 в malloc. Это необходимо, потому что объявление данных, имеющих длину 1, вводит ошибку «один за другим» во все и приглашает кодеров делать ошибки.

Так как GNU и Microsoft добавила расширение языка, что позволяет объявить массив нулевой длины:

/* Variation 2 */ 
struct mydata { 
    int type; 
    int datalen; 
    char data[0]; 
}; 

В то время как на поверхности, это нонсенс, он подгоняет аккуратно с идиома используется здесь , Теперь мы можем просто сделать:

rval = malloc(sizeof(*rval) + len); 

и код намного чище.

C99 формализовал эту идиому, признав, что длина массива является ложью, но возможность иметь дополнительные данные в конце структуры очень удобна.Итак, теперь вы заявляете:

/* Variation C99 */ 
struct mydata { 
    int type; 
    int datalen; 
    char data[]; 
}; 

и все кодировано точно так же, как с расширением Gnu/Microsoft.

К сожалению, похоже, что Microsoft не приняла стандарты C99 в свой компилятор, поэтому независимо от того, что вы делаете, варианты 2 и C99 генерируют предупреждение. Похоже, что мой единственный выбор - либо жить с предупреждающим сообщением, либо добавлять прагму для его подавления.

Пользователи Linux могут развлекаться, выполняя gr -r '\[0\]' /usr/include и видя, как много мест используют массивы нулевой длины. Это очень часто используемая идиома.

Что касается моей собственной проблемы: структура, с которой я работаю, на самом деле является частью ioctl. Драйвер уже написан, и я не могу его изменить. Самое большее, что я могу сделать, - переопределить массив от нулевой длины до гибкой. К сожалению, ни один из вариантов не делает компилятор MSVC счастливым.

+0

«... и видя, как много мест используют массивы нулевой длины. Это очень часто используемая идиома». Это сломанная идиома, которая распространяется из-за лени и/или некомпетентности некоторых программистов Linux (и компилятор GCC, обслуживающий эту лень). Правильная форма этой идиомы на языке pre-C99 подразумевает использование '[1]' для гибкого элемента (как в вашем исходном примере), а не '[0]'. – AnT

+1

Ваш аргумент '- 1' недействителен, поскольку альтернативный (правильный) способ вычисления размера в варианте« [1] »будет« offsetof (mydata, data) + len'. Видеть? Нет необходимости вычитать это '1'. Удивительно, но значительное число так называемых «профессиональных» программистов блаженно не знают о такой стандартной функции языка, как макрос «offsetof» (к тому, чтобы попытаться повторно реализовать его вручную, несмотря на то, что он уже легко доступен). – AnT

+0

Возможно. Я никогда не использовал эту идиому. Я всегда использовал что-то вроде 'char * datap = & mydatap-> data [0];' или (в случае, когда 'struct mydata' вообще не имел элемента' data') 'char * datap = ((char *) mydatap) + sizeof (* mydatap); '. –

Смежные вопросы