Конфликт типа и именования переменных в C - программирование

Конфликт типа и именования переменных в 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 ведут себя одинаково. Но мне бы очень хотелось иметь ключ к этой проблеме.

4b9b3361

Ответ 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 Области применения идентификаторов.