我正在阅读C标准N1570,并且对链接有一些误解。根据{{1}}中的规定:
5如果函数标识符的声明没有 存储类说明符,它的链接被确定为完全一样 用存储类说明符extern声明。 如果 对象标识符的声明具有文件范围并且没有 存储类说明符,其链接是外部的。
因此,我猜想6.2.2. Linkages of objects
与具有文件作用域的对象的标识符声明中没有存储类说明符之间没有区别。
让我们考虑以下示例:
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
标准如何解释这种行为?由于在两种情况下标识符具有相同的链接,所以我希望链接器行为也相同。
答案 0 :(得分:2)
在第一种情况下,int a
是一个临时定义。
在第二种情况下,缺少a
的定义,仅存在声明。这就是链接器抱怨的原因。
引用C11
,第6.9.2章
具有文件范围而没有初始化程序的对象的标识符声明,以及 没有存储类说明符或具有存储类说明符
static
的情况下, 临时定义。如果翻译单元包含一个或多个临时定义, 标识符,并且转换单元不包含该标识符的外部定义,然后 该行为与翻译单元中包含的文件作用域声明完全相同 标识符,具有转换单元末尾的复合类型,带有初始化程序 等于0。