我们一般在 c++
中使用 c
语言的库时,都会引用 c
库的头文件。例如string.h
,c
库头文件中我们经常会看到这样的代码。
#ifdef __cplusplus
extern "C"{
#endif
......
#ifdef __cplusplus
}
#endif
__cplusplus
定义着 c++
编译器的版本,如果没有定义则表示当前编译器不是 c++
。
上述代码的意思是如果编译代码的是 c++
编译器,那么就将中间包含的内容以 c
语言的语法编译,为什么要这么做呢,下面我举个例子
add.h
#ifndef _ADD_H
#define _ADD_H
int add(int a, int b);
#endif // _ADD_H
add.c
int add(int a, int b)
{
return a + b;
}
main.cpp
#include <iostream>
#include "add.h"
using namespace std;
int main(int argc, char *argv[])
{
cout << add(3, 5) << endl;
return 0;
}
执行编译
g++ main.cpp add.c -o main
这样的编译是没问题的,因为我们用g++
编译了所有文件,都按照c++
的编译规则。
那么如果add.c
是c
库的形式或者是用gcc
编译的二进制文件呢?
g++ main.cpp add.o -o main
/tmp/ccGrFT1M.o:在函数‘main’中:
main.cpp:(.text+0xf):对‘add(int, int)’未定义的引用
collect2: error: ld returned 1 exit status
编译器告诉我说没有 add
这个函数的引用,但是在add.c
里面明明定义了,为什么说没有呢。
这就要从c++
和c
语言对函数编译时的区别说起了,c++
函数编译时因为有重载的语法,为了使每个函数在编译期都能绑定到唯一的函数,会将每个函数都进行重命名,比如上面的add
就会编译成_add_int_int
这样的符号。
main.cpp
文件中包含了 add.h
,编译时就会将 add
函数编译成 _add_int_int
然后链接,而使用gcc
编译的add.o
,就会将add
函数只编译成_add
, 所以g++在链接时就说找不到引用。
解决办法是将c头文件中加入
#ifdef __cplusplus
extern "C"{
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif
这样在extern "C"{}
中包裹的函数都会按照c
编译器的方式去编译,只会编译出 _add
这样的函数符号,这样在链接过程中才可以正确的找到和库中对应的函数。
注:_add_int_int
只是一种命名方式,只要能解决重载函数的唯一性问题都可以,这就要看不同的编译器如何实现了
注:可以看一下c
库的头文件(stdio.h
等),都有这样的代码,为了保证c++程序可以正确调用c
库中的函数