一、目的
相信从事嵌入开发的小伙伴肯定遇到过使用第三库的情景,有时候可能是C++中调用C库,有时候可能又是C中调用C++库;如果你遇到过,那你肯定知道extern "C"的作用.
本篇的目的就是给大家介绍C/C++互相调用的原理和实践。
二、介绍
首先我们需要明确以下几点:
c++支持重载,c++中函数编译后名字可能会带上一些参数信息
c不支持重载,c中函数编译后在符号表中的名字没有任何改动
假设我们有这样一个头文件print_hello.h
#ifndef PRINT_HELLO_H
#define PRINT_HELLO_H
#ifdef __cplusplus
extern "C" {
#endif
void print_hello(const char *info);
#ifdef __cplusplus
}
#endif
#endif
其中下面代码片段的作用为了避免重复包含导致编译错误
#ifndef PRINT_HELLO_H
#define PRINT_HELLO_H
#endif
下面的代码片段
#ifdef __cplusplus
extern "C" {
#endif
void print_hello(const char *info);
#ifdef __cplusplus
}
#endif
我们将print_hello函数根据当前使用的编译器决定是否包含在extern "C"声明中。
extern "C" {
}
extern/static关键字用于声明变量或者函数的可见性,extern声明的变量或者函数代表除了本模块外其他模块也可见;static声明的变量或者函数只有本模块可见。
我们可以通过extern声明变量或者函数多次,但是只能定义一次。
__cplusplus只有是g++编译器时才会定义。
关键字"C"是指按照C的language linkage进行编译链接。
三、实战
print_hello.c源代码
#include "print_hello.h"
#include <stdio.h>
void print_hello(const char *info) {
printf("hello %s\n", info);
}
#ifndef PRINT_HELLO_H
#define PRINT_HELLO_H
void print_hello(const char *info);
#endif
编译后查看函数符号
gcc -c print_hello.c -o print_hello.o
readelf -a print_hello.o
从上图我们可以看到gcc编译后的print_hello函数符号名还是print_hello。
下面我们再用g++编译一下看下输出结果
g++ -c print_hello.c -o print_hello.o
readelf -a print_hello.o
从上图我们可以看到g++编译后的print_hello函数符号名变成了_Z11print_helloPKc
下面我们来看一下通过extern "C" 声明编译后的函数名称
#ifndef PRINT_HELLO_H
#define PRINT_HELLO_H
#ifdef __cplusplus
extern "C" {
#endif
void print_hello(const char *info);
#ifdef __cplusplus
}
#endif
#endif
g++ -c print_hello.c -o print_hello.o
readelf -a print_hello.o
再次看一下函数符号信息
从上图我们可以确认的确跟用gcc编译后的效果一样。
那我们再来确认一下通过g++编译后的print_hello函数能不能被c调用
main.c
#include "print_hello.h"
int main() {
print_hello("world");
return 0;
}
gcc main.c print_hello.o -o main
readelf -a main
从上图可以确认c的main程序中的确链接上print_hello函数。
以上,就是extern "C"在c/c++互相调用的作用