问题原理
之前一直以为gcc编译的文件就是C型的目标文件,和源文件的后缀名无关. 这次在做一个小实验的时候发现目标文件的形式是由后缀名决定的.
start.h
void start();
start.c
#include <stdio.h>
void start(){
printf("start\n");
}
test.cc
#include <stdio.h>
#include <string.h>
#include "start.h"
int main(){
printf("main\n");
start();
}
➜ personal_use gcc -c start.c test.cc
➜ personal_use ld start.o test.o -lc
ld: warning: cannot find entry symbol _start; defaulting to 00000000004002c0
test.o: In function `main':
test.cc:(.text+0x11): undefined reference to `start()'
➜ personal_use nm start.o
U _GLOBAL_OFFSET_TABLE_
U puts
0000000000000000 T start
➜ personal_use nm test.o
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U puts
U _Z5startv
可以看到虽然都是用gcc编译的,但是test.cc 的目标文件明显是cpp形式的(它的start函数的symbol是被修饰的), 而start.c 则是c形式的目标文件.
所以在进行链接的时候,test.cc会报错找不到_Z5startv这个symbol , 因为test.o 里有的symbol 名字叫start .
总结一下,问题的本质事实上是c和cpp对符号的处理不同,cpp会进行符号修饰, 而c不会, 导致源码里同一个函数在变为符号时会不同,引发歧义.
解决方法
-
最简单的就是把他们改成都是c文件或都是cpp文件, 当然也可以都用g++进行编译链接(都变为cpp型目标文件或c型目标文件)
-
可以把test.cc 的文件修改一下
#include <stdio.h> #include <string.h> #ifdef __cplusplus extern "C" { #endif #include "start.h" #ifdef __cplusplus } #endif int main(){ printf("main\n"); start(); }
这样在test.cc里声明的start函数使用的就是c型的symbol,不会产生歧义.