Подтвердить что ты не робот

Почему идентификаторы typedef можно объявлять несколько раз?

Из стандарта C99, 6.7 (5):

Объявление определяет интерпретацию и атрибуты набора идентификаторов. Определение идентификатора - это декларация для этого идентификатора, которая: для объекта, заставляет хранилище быть зарезервированным для этого объекта; для функции включает тело функции; для константы перечисления или имени typedef, является (единственным) объявлением идентификатора.

Если идентификаторы с typedef являются фактическими определениями, то почему их разрешено объявлять более одного раза? Пример:

int main()
{
  typedef int x;
  typedef int x;
}

Выше компилируется программа без ошибок. Как это возможно? Я ожидал, что программа даст мне ошибку с множественным определением.

4b9b3361

Ответ 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).

Ответ 2

Ваша программа компилируется без ошибок, потому что ваш компилятор является слабым (или компилируется для другого стандарта). Если я скомпилирую ваш код с gcc 4.4.7, то он действительно сообщает об ошибке переопределения x.

Ответ 3

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

void foo(int a);

void foo(int a);

int main()
{
    foo(42);
}

void foo(int a)
{
    printf("%d\n", a);
}

Это позволяет нескольким заголовкам объявлять функцию или структуру и разрешать включение двух или более таких заголовков в одну и ту же единицу перевода.

typedefs аналогичны прототипированию функции или структуры - они объявляют идентификаторы, которые имеют некоторое значение времени компиляции, но не имеют значения времени выполнения. Например, следующее не будет компилироваться:

int main()
{
    typedef int x;
    typedef int x;
    x = 42;
}

потому что x не называет переменную; это всего лишь псевдоним времени компиляции для имени int.