extern是可选的吗? 我确信我疯了,但是考虑下面的C代码: // file1.c int first; void f(void) { first = 2; } //file2.c #包括 int优先; 无效f(); 内部主(空) { 第一个=1; f(); printf(“%d”,第一个); }

extern是可选的吗? 我确信我疯了,但是考虑下面的C代码: // file1.c int first; void f(void) { first = 2; } //file2.c #包括 int优先; 无效f(); 内部主(空) { 第一个=1; f(); printf(“%d”,第一个); },c,compilation,linker,extern,C,Compilation,Linker,Extern,出于某种原因,这两个文件将编译并链接在一起,并打印2。我一直认为,除非我首先用extern标记的一个或另一个(但不是两个)定义,否则这不会编译,事实上这就是extern的全部要点 它之所以编译,是因为first只声明了两次,实际上内存中没有两个位置,只有一个。只需先用int first=4初始化一个和另一个int first=5和您的链接器将向您显示错误,例如GCC: b.o:b.c:(.data+0x0): multiple definition of `_first' a.o:a.c:(.d

出于某种原因,这两个文件将编译并链接在一起,并打印
2
。我一直认为,除非我首先用
extern
标记
的一个或另一个(但不是两个)定义,否则这不会编译,事实上这就是
extern
的全部要点

它之所以编译,是因为
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
在正常情况下(没有额外的gcc标志),您可以将此代码编译为:

gcc file1.c file2.c
将要发生的是,编译器将看到您有两个名为同一事物的全局变量,并且两个都没有初始化。然后,它将把未初始化的全局变量放在代码**的“公共”部分。换句话说,它只有“first”变量的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
编译以生成它们),您将看到
第一个
放在公共(
*COM*
)部分:


当您将函数声明为extern时,链接器将在另一个对象中搜索它。否则它将在同一个文件中查找它。这甚至不应该链接。你确定你正在显示所有的编译器和链接器警告吗?@KerrekSB Links与GCC完全一致。从
f
main
打印
&first
会产生相同的地址。@larsmans:是的。有趣。所以varibale只是“暂时定义”的,除非它被初始化?是的,这太疯狂了。我想知道这是否是一个gcc/linux链接器优化。我敢肯定这以前不起作用(在“过去起作用”的地方可能是几年前。也许我应该在虚拟机中安装一个旧的linux,只是为了检查是否正常?)不,这是错误的。C标准说它不应该编译,gcc(以及链接器)有一个优化,只要你不赋值,它就可以工作。@ChrisJefferson你说的和我说的一样
 //objdump from file1.c:
 0000000000000028       O *COM* 0000000000000020 first

 //objdump from file2.c:
 0000000000000050       O *COM* 0000000000000020 first
mike@mike-VirtualBox:~/C$ objdump -t file2.o

a.o:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*  0000000000000000 file2.c
0000000000000000 l    d  .text  0000000000000000 .text
0000000000000000 l    d  .data  0000000000000000 .data
0000000000000000 l    d  .bss   0000000000000000 .bss
0000000000000000 l    d  .rodata    0000000000000000 .rodata
0000000000000000 l    d  .note.GNU-stack    0000000000000000 .note.GNU-stack
0000000000000000 l    d  .eh_frame  0000000000000000 .eh_frame
0000000000000000 l    d  .comment   0000000000000000 .comment
0000000000000004       O *COM*  0000000000000004 first
0000000000000000 g     F .text  0000000000000039 main
0000000000000000         *UND*  0000000000000000 f
0000000000000000         *UND*  0000000000000000 printf