A-A+

C/C++ 程序混编(extern “C”的使用)

2018年07月19日 C/C++, 编程语言 暂无评论 阅读 499 次

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码(也可以方便C代码调用C++代码)。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。

不同的语言链接性是不同的,那么也决定了它们编译后的链接符号的不同,比如一个函数void fun(double d),C语言会把它编译成类似_fun这样的符号,C链接器只要找到该函数符号就可以链接成功,它假设参数类型信息是正确的。而C++会把这个函数编译成类似_fun_double或_xxx_funDxxx这样的符号,在符号上增加了类型信息,这也是C++可以实现重载的原因。
那么,对于用C编译器编译成的库,用C++直接链接势必会出现不能识别符号的问题,是的,需要extern "C"的时刻来了,它就是干这个用的。extern "C" 的作用就是让编译器知道要以C语言的方式编译和连接封装函数。

这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。

在嵌入式编程中,我们也难免用到其他的C++库,这就需要好好了解这方面。

C++调用C函数

1).做一个C动态库:

// hello.c:
#include <stdio.h>;
void hello()
{
    printf("hello\n");
}

编译成动态库

[root@coredump test]# gcc --shared -o libhello.so hello.c
[root@coredump test]# cp libhello.so /lib/

2).写个C++程序去调用它:

// test.cpp
#include <iostream>
#ifdef __cplusplus
extern "C" { // 告诉编译器下列代码要以C链接约定的模式进行链接
#endif
    void hello();
#ifdef __cplusplus
}
#endif
int main()
{
    hello();
    return 0;
}

编译并运行:

[root@coredump test]# g++ test.cpp -o test -lhello
[root@coredump test]# ./test
hello
[root@coredump test]#

3).__cplusplus宏的条件编译:

为什么要加这个条件编译呢?

extern "C"语法在C编译环境下是不允许的,只能在C++环境中使用。

C调用C++库

C++调用C库看上去也不是那么困难,因为C++本身就有向前(向C)兼容的特性,再加上纯天然的extern "C"约定,使得一切都是那么自然。

# hello.cpp
#include "hello.h"
#include <iostream>

using namespace std;

#ifdef __cplusplus
extern "C" {
#endif
// 里面的函数接口需为C兼容,变量返回值类型为c类型
int hello()
{
    cout << "Hello World!" << endl;
}
#ifdef __cplusplus
}
#endif

# hello.h

#ifdef __cplusplus
extern "C" {
#endif
// 里面的函数接口需为C兼容,变量返回值类型为c类型
int hello();
#ifdef __cplusplus
}
#endif

编译成动态链接库

g++ -shared -fPIC libhello.so hello.cpp

在c程序中使用

// test.c

#include <stdio.h>
#include "hello.h"

int main()
{
  hello();
  return 0;
}

编译并运行:

[root@coredump test]# gcc test.c -lhello -o test
[root@coredump test]# ./test
Hello World!

 

给我留言