考虑这个非常简单的设置:
foo.c的内容:
#include "bar.h"
int socket[128];
int main(int argc, char *argv[])
{
bar();
return 0;
}
bar.h的内容
void bar(void);
bar.c的内容:
#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
是指向函数cf的指针。宣布socket.h
。
链接器可以链接这些链接,因为链接器不知道类型。
您必须有一个公共标题,您可以在其中声明导出的符号以捕获这些内容。
如果链接器在您用于链接的库中找到符号socket
,它将插入此地址。否则,它将与系统的posix库中的socket
链接。