C函数和C++函数相互调用的问题(★firecat推荐★)

258 阅读6分钟

blog.csdn.net/xiexingshis…\

it.zuocheng.net/c-cpp-mix-p…\

1、如何C++程序要调用 已编译后的C函数,该怎么办?

(1)假设某个C函数的声明如下:

void foo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像 _foo_int_int之类的名字用来支持重载和类型安全连接。由于编译后的名字不同,C++程序不能直接调用C函数。C++提供了一个C连接交换指定符号extern "C"来解决这个问题

如:

extern "C"

{

void foo(int x, int y);

...//其它函数

}

或者(推荐使用,当C++类函数需要调用C的函数)

extern "C"

{

#include "myheader.h"

..//其它头文件

}

这就告诉C++编译器,函数foo是个c连接,应该到库中找名字_foo而不是找_foo_int_int。

另外说明一点:C++编译器开发商已经对C标准库的头文件作了extern "C"处理,所以我们可以用#include 在C++程序中直接引用这些头文件。

\

(2)如何理解extern "C"

extern "C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是extern的, 其次,被它修饰的目标是"C"的,让我们来详细介绍解读这两重含义

被extern "C"限定的函数或变量是extern类型的

extern是c/c++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句

extern int a;

仅仅是一个变量的声明,其实并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作业一种全局变量只能被定义一次,否则会出现连接错误。

通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明,例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需要包含模块A的头文件即可。这样,模块B中调用 模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错,它会在连接阶段中从模块A编译生成的目标代码中找到此函数

     与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern "C"修饰。

     被extern "C"修饰的变量和函数是按照C语言方式编译和连接的

\

未加extern"C" 声明时的编译方式

       首先看看C++编译器对类似C的函数是怎样编译的。

      作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:

void foo(int x ,int y);

该函数被C编译器编译后在符号库中的名字为_foo, 而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为"mangled name")。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的,例如,在C++中,函数void foo(int x, int y)与void foo(int x, float y)编译生成的符号是不相同的,后者为_foo_int_float。同样的,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

\

extern "C"的惯用法

(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

extern "C"

{

   #include "cExample.h"

}

而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。因此,我们经常会做如下处理:
#ifdef _cpluscplus
#define GENERAL_EXTERN extern "C"

#else

#define GENERAL_EXTERN  extern

\

2、在c中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在c语言不能直接引用 声明了extern "C"的该头文件,应该仅将c文件中将C++中定义的extern"C"函数声明为extern 类型

\

3、实例解说

这个例子,示例了两点

  • C 如何调用C++对象里的函数
  • C++ 如何调用C的函数

共两个文件,test.c 和 main.cpp,代码解释如下:

在main.cpp (C++ 代码) 里定义了一个类MyMath,类里有个成员函数sum ;如何让C能调用这个c++的函数MyMath::sum呢?

即在main.cpp 中添加extern C后,声明定义一个C的函数call_MyMath_sum。在test.c 中先声明这个函数,然后通过调用call_MyMath_sum,达到调用C++ MyMath::sum的作用。

在test.c 中,定义了一个sum 的函数。如何让C++能调用这个c的函数sum呢? 这么做的,在main.cpp中 extend "C"后声明它,然后在main函数中直接调用就可以了。

代码有点绕,C和C++调来调去的,不过仔细看就容易明白。

起关键作用的就是 extent “C” 这个关键语句,它的作用是告诉C++编译器,把后面的语句当作C语言进行处理。

代码如下

C语言中的函数,其中调用了C++中的call_MyMath_sum:

test.c

int call_MyMath_sum (int, int); // 此函数定义在main.cpp中
int sum(int a , int b) {
    return call_MyMath_sum(a,b);
}

C++语言中的函数:

main.cpp

# include <iostream>
using namespace std;
extern "C" { 
    int sum(int , int);  // 声明sum函数,已经在test.c 中定义过
} 

class MyMath { 
  public : 
    static int sum(int , int); 
};

int MyMath::sum(int a, int b) { 
    return (a + b); 
} 

extern "C" int call_MyMath_sum (int a , int b) {  // 定义call_MyMath_sum , 使其可以被c的代码调用
    return (MyMath::sum(a,b)); 
} 
int main(void) { 
    cout< <sum(5,6); return 0;  // 此sum是 在test.c中定义的
} 

如何编译:

# Makefile 
main.o:
    g++ -c -o main.o main.cpp  # 注意是g++
test.o:
    gcc -c -o test.o test.c  # 注意是gcc
main: main.o test.o
    g++ -o main main.o test.o # 最后链接用的是g++
all: main
clean:
    rm -f test.o main.o

执行 make 得到可执行文件main

\