Внешняя, внутренняя и никакая связь или почему это не работает?
В соответствии со стандартом C:
В наборе единиц перевода и библиотеках, составляющих всю программу, каждый объявление определенного идентификатора с внешняя связь обозначает тот же объект или функция. В пределах одной единицы перевода каждое объявление идентификатора с внутренний связь обозначает тот же объект или функцию. Каждое объявление идентификатора с нет связь обозначает уникальный объект.
В моем примере у нас есть три отдельных объявления с каждым идентификатором, имеющим другую ссылку. Поэтому почему это не работает?
static int a; //a_Internal
int main(void) {
int a; //a_Local
{
extern int a; //a_External
}
return 0;
}
Ошибка:
В функции "main": Строка 9: ошибка: переменная, ранее объявленная "статическая" переопределенная "extern"
Почему компилятор настаивает на том, что я обновляюсь, а не пытаюсь получить доступ к внешнему объекту в другом файле?
Действительный пример С++ для справки:
static void f();
static int i = 0; // #1
void g() {
extern void f(); // internal linkage
int i; // #2 i has no linkage
{
extern void f(); // internal linkage
extern int i; // #3 external linkage
}
}
Как Clang, так и VC, похоже, в порядке с моим примером; только некоторые версии GCC (не все) производят вышеупомянутую ошибку.
Ответы
Ответ 1
§6.2.2, 7 говорит:
Если в пределах единицы перевода появляется тот же идентификатор с обоими внутренняя и внешняя связь, поведение undefined.
Итак, ваша программа undefined поведение.
§6.2.2, 4 говорит, что
extern int a; //a_External
имеет внешнюю связь, поскольку предыдущее объявление, видимое в области int a; //a_Local
, не имеет привязки. Но
static int a; //a_Internal
объявляет a
внутренней связью. Следовательно, оно undefined за §6.2.2, 7.
Ответ 2
Компилятор передает эту ошибку, поскольку внутри области a_External
все еще доступно a_Internal
, поэтому вы обновляете a_Internal
от static
до extern
в a_External
из-за столкновения имен a
. Эту проблему можно решить, используя разные имена переменных, например:
static int a1; //a_Internal
int main(void) {
int a2; //a_Local
{
extern int a3; //a_External
}
return 0;
}
Ответ 3
Стандарт C говорит:
В наборе единиц перевода каждое объявление определенного идентификатор с внешней связью обозначает тот же объект (объект или функция). В пределах одной единицы перевода каждая декларация идентификатор с внутренней связью обозначает тот же объект.
В наборе единиц перевода мы не можем иметь несколько разных внешних объектов с тем же именем, поэтому должны совпадать типы каждой декларации, которая обозначает этот единственный внешний объект. Мы можем проверить, согласны ли типы в одной единице перевода, это делается во время компиляции. Мы не можем проверить, согласуются ли типы между разными единицами перевода ни во время компиляции, ни при времени ссылки.
Для идентификатора, объявленного с помощью спецификатора класса хранения extern в область, в которой видна предварительная декларация этого идентификатора, 31) если в предыдущем объявлении указывается внутренняя или внешняя связь, привязка идентификатора к более позднему объявлению такая же, как и ссылка, указанная в предыдущей декларации. Если предыдущее объявление не было видимо, или если в предыдущем объявлении не указывается связь, тогда идентификатор имеет внешнюю связь.
static int a; //a_Internal
int main(void) {
int a; //No linkage
{
extern int a; //a_External
}
return 0;
}
Здесь предыдущее объявление идентификатора a не имеет привязки, поэтому extern int a
имеет внешнюю связь. Это означает, что мы должны определить int a в другой единицы перевода. Однако GCC решил отклонить этот код с ранее объявленной переменной static
redeclared 'extern', вероятно, потому, что мы имеем поведение undefined в соответствии со стандартом C
.