gcc编译undefined reference to本质原因

项目中的LVS用到keepalived和ipvsadm等三方件,在suse11和suse12上编译最新版本的过程中遇到的最多的错误便是 undefined reference to xxx。由于对背后的原理基本没啥理解,所以遇到问题的解决办法就是把错误信息拿去google,baidu搜。当遇到的问题越来越偏僻时,这种做法不仅学不到多少东西,也无法快速的解决问题。所以下定决心从头学起,刚好对此也非常感兴趣,学习这些知识对学习操作系统一定非常有帮助。

动手实践之前,先来一点理论知识的指导:
gcc程序的编译过程和链接原理

为了加深印象与理解,按自己的理解总结一下,gcc的编译选项:

  • -o 指定输出文件名为file,这个名称不能跟源文件名同名。不指定时,-E会输出到标准输出(一般为屏幕)。-S输出到file.s。-c 输出到file.o。不使用以上选项时,输出到a.out。
  • -E Preprocess only; do not compile, assemble or link;只预处理
  • -S Compile only; do not assemble or link;只编译(得到的是汇编代码,mov,push这种
  • -c Compile and assemble, but do not link; 编译和汇编。得到的是二进制。我理解此时的二进制已经和操作系统的具体指令相关了。
  • 不指定参数,默认进行编译,汇编,链接。得到是可执行二进制。

了解了原理,接下来进行实践加深理解。
参考:
Linux makefile – undefined reference to 问题解决方法

测试代码如下:

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>

int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>

void test()
{
        printf("I am test!\n");
}

直接进行编译:

copbint@debian2:~/workspace/test1$ gcc -o main main.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~
/tmp/ccXDYxeL.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status

加上test.c便正确:

copbint@debian2:~/workspace/test1$ gcc -o main main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~

虽然能够链接成功,但是还是会有警告。经过测试,原来是在编译的过程中,发出了警告。

copbint@debian2:~/workspace/test1$ gcc -S  main.c test.c
main.c: In function ‘main’:
main.c:5:2: warning: implicit declaration of function ‘test’ [-Wimplicit-function-declaration]
  test();
  ^~~~

由此可以理解,在编译的过程中,如果找不到函数的实现,只会抛出implicit declaration of的警告。如果在链接的过程中,找不到函数的实现,则会导致错误。
在main.c中加入test()的声明,再实验如下:

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
void test();
int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.c
#include <stdio.h>

void test()
{
        printf("I am test!\n");
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
copbint@debian2:~/workspace/test1$ gcc -o main  main.c
/tmp/ccISqKfs.o: In function `main':
main.c:(.text+0x1c): undefined reference to `test'
collect2: error: ld returned 1 exit status

由此可以得出结论:
undefined reference to xxx是由于gcc在链接的过程中找不到函数的实现而导致的错误。如果是找不到函数的声明,会在编译的过程出抛出警告。

那么,如果在代码中使用未定义的变量,在编译(不链接)的过程中是否像未定义的函数一样,仅仅是抛出警告而不会导致错误呢?

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
int main()
{
        test_h_var = 3;
}
copbint@debian2:~/workspace/test1$ gcc -c main.c
main.c: In function ‘main’:
main.c:5:2: error: ‘test_h_var’ undeclared (first use in this function)
  test_h_var = 3;
  ^~~~~~~~~~
main.c:5:2: note: each undeclared identifier is reported only once for each function it appears  in

在编译的过程中,与使用未定义的函数不同,使用未定义的变量会直接导致错误。

在稍大一点的软件项目中,如果用到一个函数,就手动的去声明一次,会有工作量大且难以维护的问题。头文件就是为了解决这个问题,如为test.c添加一个test.h。然后在main.c中引用。

copbint@debian2:~/workspace/test1$ cat main.c
#include <stdio.h>
#include "test.h"
int main()
{
        test();
}
copbint@debian2:~/workspace/test1$ cat test.h
void test();

使用<>括起来的头文件,gcc会去系统路径下查找(具体路径还不了解),而”“括起来的头文件,gcc会尝试在当前目录搜索。

头文件除了声明函数,还有定义变量,宏,结构体等的作用。所以,在实际应用过程中,头文件必不可少。


在suse11上编译keepalived1.3.5的过程中,遇到大量 undefined reference to xxx错误,但是却并未看到implicit declaration of的告警信息。说明是在链接的过程中未找到对应函数的实现体。那么什么原因会导致这种情况呢?

未完待续……

猜你喜欢

转载自blog.csdn.net/qq_31567335/article/details/82433216