未定义的函数引用被编译器接受
Undefined reference to function accepted by compiler
好的,我有这个代码:
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() 调用,我会收到警告类型 Function str not defined.。
编译器
代码编译没有问题。
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之前你有函数原型,编译器知道如何调用它并且会走得更远,他对此很满意。
链接器
链接器最后会尝试将所有函数放在一起,然后您会收到类似 Function not found 的错误,因为 str()
function 从未真正编译过.
如果您提供函数原型,编译器会很高兴 int str(void);
,但是,链接器将尝试解析该引用,您将从中获得未解析的引用。
这就是错误的来源。
原型是您对编译器的承诺,该函数已在某处定义。链接器确保您信守诺言。
首先(正如其他人已经指出的那样)如果在 "building" 可执行文件 main
时可能会出现错误,那么在 linking 阶段,即因为int str(void)
未在任何翻译单元(.c 文件)中定义,因此编译器未将其放置在任何目标文件 (.o) 中。
但很遗憾,您已经 char * str = "..."
定义了。
C(与其他语言相反,例如 C++)不注释函数,它不创建函数签名,而只是函数的名称,符号名称。它对所有其他对象也是如此。
因此 link 用户别无选择,只能检查可用符号的名称,以及它们是否匹配 1:1 到 link。它无法进行双重检查,因为编译器没有将任何验证信息放入目标文件中。
如果你有一个 int str(void)
的实现(一个 定义 ,而不是一个 声明 )反对 link,那么 link 人会注意到没有 1:1 匹配,所以可能会发生一些可疑的事情。
好的,我有这个代码:
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() 调用,我会收到警告类型 Function str not defined.。
编译器
代码编译没有问题。
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之前你有函数原型,编译器知道如何调用它并且会走得更远,他对此很满意。
链接器
链接器最后会尝试将所有函数放在一起,然后您会收到类似 Function not found 的错误,因为 str()
function 从未真正编译过.
如果您提供函数原型,编译器会很高兴 int str(void);
,但是,链接器将尝试解析该引用,您将从中获得未解析的引用。
这就是错误的来源。
原型是您对编译器的承诺,该函数已在某处定义。链接器确保您信守诺言。
首先(正如其他人已经指出的那样)如果在 "building" 可执行文件 main
时可能会出现错误,那么在 linking 阶段,即因为int str(void)
未在任何翻译单元(.c 文件)中定义,因此编译器未将其放置在任何目标文件 (.o) 中。
但很遗憾,您已经 char * str = "..."
定义了。
C(与其他语言相反,例如 C++)不注释函数,它不创建函数签名,而只是函数的名称,符号名称。它对所有其他对象也是如此。
因此 link 用户别无选择,只能检查可用符号的名称,以及它们是否匹配 1:1 到 link。它无法进行双重检查,因为编译器没有将任何验证信息放入目标文件中。
如果你有一个 int str(void)
的实现(一个 定义 ,而不是一个 声明 )反对 link,那么 link 人会注意到没有 1:1 匹配,所以可能会发生一些可疑的事情。