extern是可选的吗?

时间:2013-02-22 14:03:18

标签: c compilation linker extern

我确信我会发疯,但请考虑以下C代码:

// file1.c
int first;

void f(void)
{ first = 2; }

// file2.c
#include <stdio.h>

int first;
void f();
int main(void)
{
    first = 1;
    f();
    printf("%d", first);
}

这两个文件由于某种原因将编译并链接在一起,并打印2。我一直认为,除非我用first标记extern的一个或另一个(但不是两个)定义,否则这将无法编译,这实际上是{{的全部要点1}}!

2 个答案:

答案 0 :(得分:10)

它只会编译因为first声明两次,实际上内存中没有两个位置,只有一个位置。只需先使用int first=4;初始化一个,然后使用int first=5;初始化另一个,您的链接器会显示错误,例如GCC:

b.o:b.c:(.data+0x0): multiple definition of `_first'
a.o:a.c:(.data+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

答案 1 :(得分:6)

在正常情况下(没有额外的gcc标志),您可以将此代码编译为:

gcc file1.c file2.c

将会发生的事情是编译器会看到你有两个名为相同的全局变量,并且都没有被初始化。然后,它会将未初始化的全局变量放在代码 ** 的“common”部分中。换句话说,它只有1个“第一”变量的副本。这是因为gcc的默认值为-fcommon

如果您要使用-fno-common标记进行编译,那么您现在会收到您正在考虑的错误:

/tmp/ccZNeN8c.o:(.bss+0x0): multiple definition of `first'
/tmp/cc09s2r7.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

要解决此问题,您需要将extern添加到除一个变量之外的所有变量中。

警告 的:
现在假设您有两个不同大小的全局未初始化数组:

// file1.c
int first[10];

// file2.c
int first[20];

好吧猜测一下,用gcc -Wall file1.c file2.c编译它们会产生没有警告或错误,并且变量很常见,即使它的大小不同!!!

 //objdump from file1.c:
 0000000000000028       O *COM* 0000000000000020 first

 //objdump from file2.c:
 0000000000000050       O *COM* 0000000000000020 first

这是全局变量的危险之一。


** 如果您查看* .o文件的objdump(您必须使用gcc -c进行编译才能生成它们),您会看到{{1}放置在公共(first)部分:

*COM*