Ответ 1
C99 отличается от C11
Правила изменяются между C99 и C11 (а правила C11 соответствуют правилам С++, как я понимаю). Обратите внимание, что в обоих стандартах ¶3 находится в разделе "Ограничения", а ¶5 находится в разделе "Семантика". Это важно для сообщений об ошибках - нарушения ограничений требуют диагностики.
ISO/IEC 9899: 1999 §6.7 Декларации
¶3 Если идентификатор не имеет связи, должно быть не более одного объявления идентификатора (в спецификаторе декларатора или типа) с той же областью действия и в том же пространстве имен, кроме для тегов, указанных в 6.7.2.3.
5 Объявление определяет интерпретацию и атрибуты набора идентификаторов. Определение идентификатора является объявлением для этого идентификатора, который:
- для объекта, заставляет хранилище зарезервировать для этого объекта;
- для функции включает тело функции; 98)
- для константы перечисления или имени typedef, является (единственным) объявлением Идентификатор.
ISO/IEC 9899: 2011 §6.7 Декларации
¶3 Если идентификатор не имеет связи, должно быть не более одного объявления идентификатора (в спецификаторе декларатора или типа) с той же областью действия и в том же пространстве имен, кроме что:
- имя typedef может быть переопределено для обозначения того же типа, что и в настоящее время, при условии, что тип не является измененным типом;Теги
- могут быть обновлены, как указано в 6.7.2.3.
¶5 Объявление определяет интерпретацию и атрибуты набора идентификаторов. Определение идентификатора является объявлением для этого идентификатора, который:
- для объекта, заставляет хранилище зарезервировать для этого объекта;
- для функции включает тело функции; 119)
- для константы перечисления, является (единственным) объявлением идентификатора;
- для имени typedef, является первым (или единственным) объявлением идентификатора.
Lundin отметил, что Комитет стандартного C содержит n1360, документ с одной страницей, в котором подробно объясняется, почему это изменение было внесено. В основном: С++ делает это; некоторые компиляторы уже делают это; это не трудно сделать и не подрывать что-либо, чтобы разрешить (потребовать) его.
GCC не всегда обеспечивает стандарт
Если ваш код компилируется, он компилируется в соответствии с правилами C11 или правилами С++. Он не компилируется в соответствии с (строгими) правилами C99.
Обратите внимание, что достаточно последние версии GCC позволяют переопределить, если вы не настаиваете на другом, но также обратите внимание на отчет John Bollinger, что GCC 4.4.7 (на неопознанной платформе) не позволяет переопределять вообще в режиме C99.
Рассмотрим файл retypedef.c
:
int main(void)
{
typedef int x;
typedef int x;
x y = 0;
return y;
}
Компиляция на Mac OS X 10.9.5 с GCC 4.9.1, я получаю:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -c retypedef.c
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -pedantic -c retypedef.c
$ gcc -O3 -g -std=c99 -Wall -Wextra -Werror -c retypedef.c
$ gcc -O3 -g -std=c99 -Wall -Wextra -Werror -pedantic -c retypedef.c
retypedef.c: In function ‘main’:
retypedef.c:4:17: error: redefinition of typedef ‘x’ [-Werror=pedantic]
typedef int x;
^
retypedef.c:3:17: note: previous declaration of ‘x’ was here
typedef int x;
^
cc1: all warnings being treated as errors
$
Он не жалуется, если не используется -pedantic
, и только тогда, когда запрашивается C99 (он соответствует стандарту, чтобы переопределить a typedef
в той же области действия на C11).