在我的项目(https://github.com/zzt93/os-lab1)中,我遇到一个全局变量与一个函数具有相同的名称,但是在错误或警告的情况下编译它会产生错误。
一个简单的程序几乎可以重现这个问题:
//a.c
struct {
int t;
int *s;
} empty, full;
int main(){
printf("full is at %p", &full);
printf("empty is at %p", &empty);
empty.t = 1;
return 0;
}
//b.c
int empty() {
return 1;
}
使用gcc -o res.out -Wall -g -Wextra a.c b.c
编译它们
只会产生这样的警告(通知:在我的项目中,它甚至不会产生错误):
/ usr / bin / ld:警告:/tmp/ccq70SCM.o中符号
empty
的对齐1在/tmp/ccVCOeWq.o中小于16/ usr / bin / ld:警告:符号
empty
的大小从/tmp/ccVCOeWq.o中的16更改为/tmp/ccq70SCM.o中的11/ usr / bin / ld:警告:/tmp/ccq70SCM.o中符号
empty
的类型从1更改为2
似乎需要struct empty
和function empty
作为同一个。
反编译它,您可以清楚地看到链接器链接function empty
的地址而不是struct empty
。所以尝试运行res.out
会导致段错误。
40054e: be 73 05 40 00 mov $0x400573,%esi
400553: bf 12 06 40 00 mov $0x400612,%edi
400558: b8 00 00 00 00 mov $0x0,%eax
40055d: e8 ae fe ff ff callq 400410 <printf@plt>
400562: c7 05 07 00 00 00 01 movl $0x1,0x7(%rip) # 400573 <empty>
400569: 00 00 00
40056c: b8 00 00 00 00 mov $0x0,%eax
400571: 5d pop %rbp
400572: c3 retq
0000000000400573 <empty>:
400573: 55 push %rbp
400574: 48 89 e5 mov %rsp,%rbp
400577: b8 01 00 00 00 mov $0x1,%eax
40057c: 5d pop %rbp
40057d: c3 retq
40057e: 66 90 xchg %ax,%ax
为什么链接器选择函数而不是结构?我认为这是一个错误吗?
为什么为struct的声明添加一个静态可以防止这个错误? - 我们了解static
使该变量在此文件外部不可见,但请注意我向struct empty
添加静态而非function empty
解决问题。
修改:
奇怪的是,在res.out的符号表中,只有一个empty
Name Value Class Type Size Line Section
empty |0000000000400573| T | FUNC|000000000000000b| |.text
我正在使用
gcc version 4.9.2
答案 0 :(得分:4)
添加static
可以防止错误,因为static
在应用于函数或全局变量时会使符号不会导出到链接器 - 简单来说,它使它对该文件“私有”
如果不使用static
,链接器将看到两个定义,但类型不匹配。但是,由于编译是逐个文件应用的,因此链接器无法知道正确的变量类型 - 它必须相信您完成了工作并且没有说谎。
这就是头文件很重要的原因 - 它确保类型在不同的文件中匹配。