海思AI芯片(Hi3519A/3559A)方案学习(二十一)extern "C"和C/C++ 混合编程

前言

在重构sample sdk代码,并编译成静态或动态库来提供给上层调用的API时,不可避免会遇到c++调用c或c调用c++的问题。看我博客的网友也提到了这个问题,所以藉这个机会来把C和C++混合编程(即相互调用)的问题透彻的弄清楚。

问题背景

C和C++直接相互调用 之所以会出问题的原因是, C++里面有函数重载的概念,从而导致编译出来的函数名会带上参数类型,而C编译出来的函数名简单的就是其本身。

下面定义了两个简单的c、c++头文件和实现文件

/*test_c.h*/
#ifndef TEST_C
#define TEST_C

int add(int a, int b);

#endif
/*test_c.c*/
#include "test_c.h"

int add(int a, int b)
{
    int c;
    c = a + b;
    return c;
}

 

/*test_c++.h*/
#ifndef TEST_C_PLUSPLUS
#define TEST_C_PLUSPLUS

int substract(int a, int b);

#endif
/*test_c++.cpp*/
#include "test_c++.h"

int substract(int a, int b)
{
    int c;
    c = a - b;
    return c;
}

 分别使用两个命令生成obj文件。

gcc -c test_c.c
gcc -c test_c++.cpp

然后使用命令: nm test_c.o | grep add  和  nm test_c++.o | grep substract 得到如下结果:

add _Z9substractii。  这个结果进一步验证了前面所说的,即C和C++编译出来的函数名会不一样。

C调用C++ 

添加一个C测试文件main.c,并调用add和substract API。

/*main.c*/
#include <stdio.h>
#include "test_c.h"
#include "test_c++.h"

int main(void)
{
    int x = 3;
    int y = 5;

    printf("add = %d \n", add(x, y));
    printf("sub = %d \n", substract(x, y));

    return 0;
}

使用命令如下来将它们编译成一个可执行文件test_c:

gcc test_c.c test_c++.cpp main.c -o test_c

但这个时候会报链接错误: main.c:(.text+0x43): undefined reference to 'substract' 。明明我们在test_c++.cpp里面定义了substract(),但是main.c不识别。 这也说明了 c直接调用c++代码出现错误。

解决办法就是引入 extern "C"。 关于它有两个重要注意点:

1)extern "C"是c++语法,只能被c++编译器认识,其目的就是告诉C++编译器,其被extern "C"所包含的函数得以C方式(即简单函数名)去链接。

2) 为了避免C编译器来编译它来产生编译错误, 保险起见,会将 extern "C" {和}用#ifdef __cplusplus... #endif修饰起来。这里很容易犯得一个错误就是 cplusplus前面是两根下划线"_"。

回到上面得错误,我们得在test_c++.cpp编译时,得用c方式去来生成substract函数。所以test_cpp.h更新如下,并用__cplusplus保护起来,避免main.c编译时报错。

/*test_c++.h*/
#ifndef TEST_C_PLUSPLUS
#define TEST_C_PLUSPLUS

#ifdef __cplusplus
extern "C" {
#endif

int substract(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

再次编译链接: gcc test_c.c test_c++.cpp main.c -o test_c没有问题,并执行./test_c 结果也完全正确。

C++调用C

添加一个C++测试文件main.cpp来调用add和substract。

/*main.cpp*/
#include <stdio.h>
#include "test_c.h"
#include "test_c++.h"

int main(void)
{
    int x = 3;
    int y = 5;

    printf("add = %d \n", add(x, y));
    printf("sub = %d \n", substract(x, y));

    return 0;
}

使用类似命令来编译链接: gcc test_c.c test_c++.cpp main.cpp -o test_cpp,结果也出现main.cpp里面找不到定义在test_c.c里面得add函数。

同样地,必须在test_c.h里面添加extern ”C“来告诉main.cpp编译链接时得以c方式来寻找add函数。同时使用__cplusplus修饰避免 test_c.c编译失败。

/*test_c.h*/
#ifndef TEST_C
#define TEST_C

#ifdef __cplusplus
extern "C" {
#endif

int add(int a, int b);

#ifdef __cplusplus
}
#endif
#endif

使用编译链接命令:gcc test_c.c test_c++.cpp main.cpp -o test_cpp 没有问题,并执行./test_cpp 结果也完全正确。

结论

无论是C++调用C api 还是C调用C++ API, 必须先在API所对应得头文件对API进行 extern "C"声明,并用#ifdef __cplusplus进行保护。 

 

 

 

 

 

 

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ltshan139

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值