Для тех, кто интересуется массивом нулевой длины или идиомами «гибкого массива», вероятно, стоит потратить минуту, чтобы объяснить их. Эта идиома такая же старая, как и сама С.
Предположим, вы хотите передать структуру, состоящую из заголовка и переменного объема данных. Неизвестно, пока не будет выделена структура, сколько данных необходимо будет добавить к ней.
оригинальный говор был объявить структуру так:
/* 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 счастливым.
Не «живите с» предупреждениями. Размер 'struct' неизвестен. Если вы не знаете размер массива, определите его как указатель и выделите память по мере необходимости. –
Согласен. Я не хочу жить с предупреждениями, я хочу их исправить. Однако использование неполного массива в качестве последнего элемента структуры * * поддерживается в C-99. Использование массивов с нулевой длиной было продолжением как в GNU C, так и в Windows C на протяжении многих лет. https://en.wikipedia.org/wiki/Flexible_array_member –
Следует также добавить: это более старый код, за который я отвечаю за поддержку. Я не писал его, и я не могу его изменить. –