Путаница в отношении связи идентификаторов - PullRequest
0 голосов
/ 24 ноября 2018

Я читаю Стандарт C N1570 и столкнулся с некоторым недоразумением относительно связи.Как указано в 6.2.2. Linkages of objects:

5 Если объявление идентификатора для функции не имеет спецификатора класса хранения, его связь определяется точно так же, как если бы она была объявлена ​​с помощью спецификатора класса хранения extern., Если объявление идентификатора для объекта имеет область файла и не имеет спецификатора класса хранения, его связь является внешней.

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

Давайте рассмотрим следующий пример:

test.h:

#ifndef _TEST_H
#define _TEST_H

int a;

void increment();

#endif //_TEST_H

test.c:

#include "test.h"

void increment(){
    a += 2;
}

main.c:

#include <stdio.h>
#include "test.h"

int main(int argc, char const *argv[])
{
    increment();
    printf("a = %d\n", a);
}

Поскольку объявлено, что a имеет внешнюю связь (область файла, без спецификатора класса хранения)a = 2 печатается, как и ожидалось.

Поэтому я заменил объявление a на спецификатор extern и не ожидал никакой разницы (согласно приведенному выше 6.2.2#5):

test.h:

#ifndef _TEST_H
#define _TEST_H

extern int a; // <---- Note extern here

void increment();

#endif //_TEST_H

Но теперь компоновщик жалуется:

CMakeFiles/bin.dir/main.c.o: In function `main':
main.c:37: undefined reference to `a'
liblibtest.a(test.c.o): In function `increment':
test.c:4: undefined reference to `a'
test.c:4: undefined reference to `a'
collect2: error: ld returned 1 exit status

Как Стандарт объясняет это поведение?Поскольку идентификаторы имеют одинаковую связь в обоих случаях, я ожидал, что поведение компоновщика также будет одинаковым.

1 Ответ

0 голосов
/ 24 ноября 2018

В первом случае int a, является предварительным определением .

Во втором случае определение для a отсутствует, только объявлениетам.Вот почему компоновщик жалуется.

Цитирование C11, глава §6.9.2

Объявление идентификатора для объекта, который имеет область действия файла без инициализатора и без хранилищаспецификатор класса или со спецификатором класса хранения static, составляет предварительное определение .Если модуль перевода содержит одно или несколько предварительных определений для идентификатора, а модуль перевода не содержит внешнего определения для этого идентификатора, то поведение точно такое, как если бы модуль перевода содержал объявление области файла для этого идентификатора с составным типом какконца блока перевода, с инициализатором, равным 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...