Конфликт типа и именования переменных в C
Недавно я наткнулся на странную конструкцию кода, которая приводит компиляторы C в странное состояние. Я хотел бы получить объяснение, почему это происходит.
Вот мой небольшой фрагмент кода, который демонстрирует проблему:
#include <stdlib.h>
typedef int type_t;
int main (void)
{
int a = 10, b = 100;
type_t *type_t = &a; // We name a variable with type name
type_t *type_c = &b; // We define a variable with type_t
return EXIT_SUCCESS;
}
И вот сообщение об ошибке, отображаемое gcc
:
#> gcc -Wall -Wextra -o sample sample.c
sample.c: In function ‘main:
sample.c:11:11: error: ‘type_c undeclared (first use in this function); did you mean ‘type_t?
type_t *type_c = &b;
^~~~~~
type_t
sample.c:11:11: note: each undeclared identifier is reported only once for each function it appears in
Или с clang
:
#> clang -Wall -Wextra -o sample sample.c
sample.c:11:11: error: use of undeclared identifier 'type_c'; did you mean 'type_t'?
type_t *type_c = &b;
^~~~~~
type_t
sample.c:10:11: note: 'type_t' declared here
type_t *type_t = &a;
^
sample.c:11:10: error: invalid operands to binary expression ('type_t *' (aka 'int *') and 'type_t *')
type_t *type_c = &b;
~~~~~~ ^~~~~~~
2 errors generated.
Обратите внимание, что если мы изменим код следующим образом, он прекрасно скомпилируется:
int main (void)
{
int a = 10, b = 100;
type_t *type_c = &b; // We define a variable with type_t
type_t *type_t = &a; // We name a variable with type name
return EXIT_SUCCESS;
}
Итак, мой вопрос сейчас!
Кажется, что ошибка type_t
тем фактом, что l-значение оператора type_t
'=' ошибочно считается умножением между type_t
и type_c
. Поскольку type_c
неизвестен, это объясняет сообщение об ошибке.
Но почему у нас такая путаница с l-значением? type_t
не должно быть однозначно, что type_t
ссылается на тип, а не на переменную?
Я предполагаю, что это не проблема реализации, так как gcc
и clang
ведут себя одинаково. Но мне бы очень хотелось иметь ключ к этой проблеме.
Ответы
Ответ 1
Это ожидаемое и правильное поведение. В C существует несколько пространств имен: метки, элементы, теги и обычные идентификаторы. Имя typedef
находится в пространстве имен обычных идентификаторов, как и имена переменных.
У тебя есть:
type_t *type_t = &a; // We name a variable with type name
type_t *type_c = &b; // We define a variable with type_t
Здесь есть три вхождения type_t
. Первый - это имя typedef; нет проблем. Второе - это имя новой переменной; нет проблем, но он скрывает (затеняет) имя typedef; Вы больше не можете ссылаться на тип type_t
в текущем блоке кода. Третье вхождение относится к переменной; Вы умножаете целочисленный указатель на неопределенную переменную (и пытаетесь использовать результат как l-значение для получения адреса b
), что неправильно на многих уровнях.
Когда вы изменяете порядок этих строк, он работает нормально, потому что type_c
объявлен как type_t *
OK; тогда type_t
определяется как переменная типа type_t *
, но дальнейшие ссылки на тип type_t
в текущем блоке не могут быть сделаны (любая такая ссылка относится к переменной, а не к типу).
Обратите внимание, что если бы typedef
находился внутри функции, вы бы не смогли type_t *type_t = &a;
его с помощью type_t *type_t = &a;
, См. C11 §6.2.1 Области применения идентификаторов.