Кто-то использует имя структуры как имя переменной. Что действительно говорит код?

Сегодня утром мы обнаружили старый кусок кода, вызывающий сбои в вызове библиотеки.

struct   fred
{
    int     a;
    int     b;
    int     c;
};

fred     fred[MAX_SIZE+1];

memset( fred, 0, sizeof(fred) * MAX_SIZE+1 );

Похоже, что sizeof (fred), возможно, был размером полного массива, а не размером структуры, поскольку он переписывал большую часть памяти.

Тот факт, что он был скомпилирован без предупреждения в нескольких разных системах, казался странным.

Есть ли правильная семантика для этого случая, когда тип и имя переменной сталкиваются? или это что-то вроде поведения undefined? или просто дефект?

Ответ 1

Номер один был бы, не делайте этого, поскольку это запутывает - но вы уже это обнаружили.

Переменная скрывает имя структуры, но вы можете использовать struct fred для обозначения типа.

например.

fred     fred[MAX_SIZE+1];

memset( fred, 0, sizeof(struct fred) * (MAX_SIZE+1) );

В качестве альтернативы, почему бы просто не использовать размер полного объекта. Таким образом, ваш вызов memset является надежным перед лицом изменений размера или типа массива. Вы можете сделать:

memset( fred, 0, sizeof fred );

У вас должны быть круглые скобки при использовании идентификатора типа с sizeof, но он не нужен, когда вы используете объект.

Ответ 2

Разве это не должно быть sizeof (fred) * (MAX_SIZE + 1), так как ваш массив MAX_SIZE + 1 long?

Ответ 3

Последнее объявление имеет приоритет:

[C++03: 9.1/2]: Определение класса вводит имя класса в область, где оно определено, и скрывает любой класс, объект, функцию или другое объявление этого имени в охватывающей области (3.3). Если имя класса объявлено в области, где также объявлен объект, функция или перечислитель с тем же именем, тогда, когда оба объявления находятся в области видимости, класс может ссылаться только с использованием специфицированного спецификатора типа (3.4.4).

Спецификатор специфицированного типа - это когда вы вставляете struct или class в начало типа; это, тем не менее, строго говоря, и в силу вышеприведенного правила, поиск никогда не был действительно двусмысленным в первую очередь.

Итак:

void foo()
{
   struct bar {};
   bar bar[5];

   memset(bar, 0, sizeof(bar));
   //             ^^^^^^^^^^^
   //                  5

   memset(bar, 0, sizeof(struct bar));
   //             ^^^^^^^^^^^^^^^^^^
   //                  1
}

// (NB. Exact sizes may differ; 1 and 5 given as relative examples only)

Тот факт, что все это четко определено, является одной из причин того, что вы не получили предупреждения. Тем не менее, я надеюсь, что интеллектуальный компилятор определит ваш код как возможную ошибку программиста -— рационализация того, почему какая-то данная реализация делает или не дает какого-либо определенного предупреждения в некоторых случаях, не связанных с мандатом, в значительной степени является глупостью.

Ответ 4

Когда вы определяете переменную, она скрывает имя типа, поэтому да, когда вы делаете sizeof(fred), вы получаете размер массива, а не размер структуры. это довольно легко проверить, просто распечатав sizeof(fred).

Короткий ответ, однако, справедлив: "не делай этого".

Ответ 5

За исключением случаев размера времени выполнения, типичный идиоматический способ использования memset (а также memcpy, malloc и т.д.) заключается в выполнении

memset(dst_ptr, 0, sizeof *dst_ptr);

или, что эквивалентно,

memset(&dst_object, 0, sizeof dst_object);

Вот как это должно было быть использовано и в этом случае

memset(&fred, 0, sizeof fred);

и проблема с конфликтом имен не возникнет. Вариант memset(fred, 0, sizeof fred) также будет работать.