【问题标题】:Is extern optional?外部是可选的吗?
【发布时间】:2013-02-22 14:03:18
【问题描述】:

我确定我快疯了,但请考虑以下 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。我一直觉得除非我用extern 标记first 的一个或另一个(但不是两个)定义,否则这不会编译,而这实际上是extern 的全部意义!

【问题讨论】:

  • 当您将函数声明为外部时,链接器将在另一个对象中搜索它。否则它将在同一个文件中查找它。
  • 这甚至不应该链接。您确定要显示所有编译器和链接器警告吗?
  • @KerrekSB 与 GCC 完美链接。从f 和main 打印&amp;first 会产生相同的地址。
  • @larsmans:是的。有趣的。所以变量只是“暂时定义”,除非它被初始化?
  • 是的,这太疯狂了。我想知道它是否是 gcc/linux 链接器优化。我敢肯定这过去不工作(“过去工作”可能是几年前。也许我应该在 VM 中安装一个旧的 linux,只是为了进行健全性检查?)

标签: c compilation linker extern


【解决方案1】:

它只编译是因为first 只声明了两次,实际上内存中没有两个地方,而只有一个。只需先用int first=4; 初始化一个,然后用int first=5; 初始化另一个,你的链接器就会向你显示错误,例如海合会:

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

【讨论】:

  • 不,这是错误的。 C 标准说它不应该编译,gcc(连同链接器)有一个优化,只要你不分配值,它就可以工作。
  • @ChrisJefferson 你在说我在说什么
【解决方案2】:

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

gcc file1.c file2.c

接下来会发生什么,编译器会看到你有两个命名相同的全局变量,但都没有初始化。然后它会将您未初始化的全局变量放在代码的“公共”部分**。换句话说,它将只有一个“第一个”变量的副本。这是因为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 编译才能生成它们),您会看到 first 放在公共(*COM*) 部分:

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

【讨论】:

    猜你喜欢
    • 2012-02-12
    • 2017-01-03
    • 2012-02-18
    • 1970-01-01
    • 2017-09-29
    • 2016-12-28
    • 2021-07-10
    相关资源
    最近更新 更多