考虑这个非常简单的设置:
foo.c的内容:
#include "bar.h"
int socket[128];
int main(int argc, char *argv[])
{
bar();
return 0;
}
酒吧的内容
void bar(void);
酒吧的内容:
#include <sys/socket.h>
void bar(void)
{
socket(AF_INET,SOCK_DGRAM,0);
}
编译:
gcc -Wall -pedantic foo.c bar.c -o foobar
跑
./foobar
当这个程序编译和链接没有错误时,当它运行时,它在调用 bar.c 中的 socket() 时会导致分段错误。将 foo.c 中的全局变量名称更改为"socket"以外的名称可以解决此问题。但我不明白为什么?这至少不应该是一个链接器错误吗?
链接器的常见行为是从库中获取对象模块,当且仅当该模块定义了在正在构造的可执行文件中使用但未解析的符号时。
这是一个重要且必要的行为,用于避免程序中的符号与作者未使用的库模块中的符号之间的冲突。后来的语言,如C++,通过分隔命名空间(如std::
)来解决这个问题。但是,一般来说,我们必须处理这样一个事实,即许多作者自然地将名称用于他们自己的例程或对象(如与库例程冲突的read
或write
)。链接器必须允许这样做。
因此,当您的程序定义socket
时,链接器允许该定义,并且不会从库中获取它。链接器不知道其中一个模块中的代码使用socket
作为函数,而另一个模块中的定义用于对象。
socket
是指向数组的指针。
在另一个文件中,您认为socket
是指向函数的指针,例如socket.h
声明。
链接器可以链接这些类型,因为链接器不知道类型。
您必须有一个公共标头,在其中声明导出的符号才能捕获此类内容。
如果链接器在链接中使用的库中找到socket
符号,它将插入此符号的地址。否则,它将与系统的 posix 库中的socket
链接。