未定义的函数引用被编译器接受

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 匹配,所以可能会发生一些可疑的事情。