Кто-то использует имя структуры как имя переменной. Что действительно говорит код?
Сегодня утром мы обнаружили старый кусок кода, вызывающий сбои в вызове библиотеки.
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)
также будет работать.