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

Внешняя, внутренняя и никакая связь или почему это не работает?

В соответствии со стандартом 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 (не все) производят вышеупомянутую ошибку.

4b9b3361

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