考虑这个非常简单的设置:
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'以外的名称可以解决此问题。但是我不明白为什么。至少应该不是链接器错误吗?
答案 0 :(得分:2)
链接器的常见行为是,仅当该模块定义了在构造的可执行文件中使用但未解析的符号时,才从库中获取对象模块。
这是避免程序中的符号与作者未使用的库模块中的符号之间发生冲突的重要且必要的行为。后来的语言(例如C ++)通过分隔名称空间(例如std::
)解决了此问题。但是,通常来说,我们必须处理这样一个事实,即许多作者自然将名称用于自己的例程或与库例程冲突的对象,例如read
或write
。链接器必须允许这样做。
因此,当您的程序定义socket
时,链接程序将允许该定义,而不会从库中获取它。链接器不知道您其中一个模块中的代码使用socket
作为函数,而另一个模块中的定义是针对对象的。
答案 1 :(得分:1)
socket
是指向数组的指针。
在另一个文件中,您认为socket
是指向函数cf的指针。 socket.h
的声明。
链接器可以链接它们,因为链接器不知道类型。
您必须具有一个公共标头,在其中声明导出的符号才能捕获此类错误。
如果链接器在您用于链接的库中找到符号socket
,它将插入其地址。否则,它将与系统的posix库中的socket
链接。