关于标识符链接的混淆

Confusion regarding the linkage of identifiers

我正在阅读 C 标准 N1570 并且遇到了一些关于链接的误解。如 6.2.2. Linkages of objects 中指定:

5 If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

所以我猜测在具有文件范围的对象的标识符声明中extern和没有存储-class说明符没有区别。

让我们考虑以下示例:

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 被声明为具有外部链接(文件范围,无存储 class 说明符)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

标准如何解释这种行为?由于标识符在这两种情况下具有相同的链接,我希望链接器的行为也相同。

第一种情况int a,是暂定定义

在第二种情况下,缺少 a 定义 ,只有声明。这就是链接器抱怨的原因。

引用 C11,章节 §6.9.2

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.