好的,我有这段代码:
str.c:
char *str = "example";
main.c中:
int str(void);
int main(void)
{
str();
return 0;
}
生成文件:
CC = gcc
CFLAGS = -Wall -g
all: main
main: main.o str.o
main.o: main.c
str.o: str.c
clean:
rm -rf *.o main
乍一看,有人可能会说这是一个未定义的参考错误或类似的东西。但编译器只接受此代码。
问题是,为什么我没有收到编译错误?
很明显,该功能没有正确定义。如果我使用gdb打破str()调用,我会收到一个警告类型未定义函数str。。
答案 0 :(得分:6)
首先(正如其他已经指出的那样)如果人们在“构建”可执行文件main
时可能会出现错误,那么在链接阶段,即因为int str(void)
是未在任何翻译单元(.c文件)中定义,因此编译器未将其放在任何目标文件(.o)中。
但遗憾的是,您已定义char * str = "..."
。
C(与其他语言相反,例如C ++)不会注释函数,它不会创建函数签名,而只会创建函数的名称,即符号名称。它也适用于所有其他对象。
因此,链接器别无选择,只能检查可用符号的名称,如果它们匹配1:1则链接它们。它无法进行双重检查,因为编译器没有将任何验证信息放入目标文件中。
如果在要链接的对象内某处有{{1>}的实现(定义,而不仅仅是声明),那么链接器将会我注意到没有1:1的匹配,所以可能会发生一些可能的事情。
答案 1 :(得分:1)
代码编译没有问题。
int str(void); //Compiler needs this to know how to prepare future calls to this function
int main(void) {
str(); //Here compiler knows about str function and how it looks like
return 0;
}
当在 main.c 中调用str()
时,编译器将准备调用,因为在main之前你有函数原型,编译器知道如何调用它并且会更进一步,他是用它来打算。
链接器最终会尝试将所有功能放在一起,然后你会收到未找到功能这样的错误,因为str()
函数从未实际编译过。
答案 2 :(得分:1)
如果您提供函数原型int str(void);
,编译器会很高兴,但是,链接器将尝试解析引用,您将从中获得未解析的引用。
错误将来自何处。
原型是您对编译器的承诺,即函数在某处定义。链接器确保您保留了这一承诺。