对编译器

时间:2017-06-09 14:05:17

标签: c compiler-errors compilation linker-errors

好的,我有这段代码:

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。

3 个答案:

答案 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);,编译器会很高兴,但是,链接器将尝试解析引用,您将从中获得未解析的引用。 错误将来自何处。 原型是您对编译器的承诺,即函数在某处定义。链接器确保您保留了这一承诺。