目录(?)[-]
http://fangzhzh.info/blog/mixing-c-and-cpp-programming.html
在最近的arm开发中,因为一些特定的需求,需要使用从C文件中调用以前C++程序中的类和方法。参考网上的一些资料,本文1将相关的知识点简要总结如下:c&c++混合编程的注意事项,在c++函数中调用c函数的方法,在c函数中调用c++函数的方法,在c函数中调用C++对象及其中数据的方法。并在本文的末尾给出C和C++混合编程的例子,例子分别以Makefile和VC6.0工程的形式展现。
本部分是从较高的角度需要注意的问题:
建议使用同一个供货商的编译器:即如果使用的gnu,那么C编译器是gcc,C++编译器是g++。
包含一个标准的头文件(如<cstdio>),不需要任何特别的处理。
// c++ 代码 #include <cstdio> // 头文件包含没啥特别处理 int main() { std::printf("Hello world\n");// 函数调用没啥特别处理 return 0; }
如果,你认为std::printf中的std::很奇怪,那么最好的方式就是“习惯它”。换句话说,这是使用标准库函数的标准方法,所以习惯这种用法就好了。
但是,如果你想要使用C++编译器来编译C代码的话,就不用使用这么奇怪的std::printf()了。这种情况下,你需要包含<stdio.h>,而不是<cstdio>,神奇的命名空间会替你解决所有事情。代码如下:
//使用C++编译器编译的C代码 #include <stdio.h> // 头文件包含没啥特别处理 int main() { printf("Hello world\n");// 函数调用没啥特别处理 return 0; }
补充:如果使用非标准库的头文件的话,关注点会有所改变,非标准库的使用有以下两种情况:不能修改头文件和能够修改头文件。
如果在C++系统中使用一个非系统提供的c头文件时,需要将#include代码用extern “C”{}包含起来,这个声明告诉C++编译器,包含在内的是C函数的头文件。
//C++ 代码 extern "C" { //get declaration for f(int i, char c, float x) #include "my-c-code.h" } int main() { f(7, 'x', 3.14); //正常调用 }
补充:可能出现的不同情况系统提供的标准头文件(比如<cstdio>)和能够修改头文件。
如果需要包含一个非系统的C头文件,同时,你拥有修改此文件的权限,那么强烈建议在文件的内部加上extern “C” {…},以使用C++程序员能够方便的使用。当然,作为C编译器,是不理解什么叫extern “C”的,因此,需要将extern “C”使用#ifdef包起来。
#ifdef __cplusplus extern "C" { #endif
#ifdef __cplusplus } #endif
// this is c++ code //get the declaration of f(int i, char c, float x) #include "my-c-mode.h" int main() { f(7, 'x', 3.14); //正常调用 }
补充:可能出现的不同情况系统提供的标准头文件(比如<cstdio>)和不能修改头文件。
现在也许你有一个单独的C函数,但是呢,出于某种原因,你并想包含声明此函数的头文件。那么你可以在C++代码中声明此单独的函数,代用extern “C” 的语法。你应该如下方式使用:
extern "C" void f(int i, char c, float x);
如果是许多程序的话,可以用大括号包含:
extern "C" { void f(int i, char c, float x); int g(char* s, char const* s2); double sqrtOfSumOfSquares(double a, double b); }
经此声明,在C++函数中调用这些C函数就变的很简单:
int main() { f(7, 'x', 3.14); // Note: nothing unusual in the call }
当知道一个C++函数会被C编译器调用时,可以使用extern “C” 结构来声明函数。
//c++ code //声明 extern "C" f(int, char, float); //.... //在某个C++模块中定义此函数 void f(int i, char c, float x) { //..... }
extern “C” 这行告诉C++编译器,当有external信息在链接时,使用C调用规范和函数命名方式。
另:因为C并不支持重载,所以不能够实现用C调用重载函数。
如果,函数没有正确的设置了extern “C”,那么有可能出现链接错误而不是编译错误。这是因为C++编译器经常会”mangle”函数名(做特殊处理)– 一种于C编译器不同的方式。
例子: Fred.h:
//此头文件C/C++编译器都访问 /* This header can be read by both C and C++ compilers */ #ifndef FRED_H #define FRED_H #ifdef __cplusplus class Fred { public: Fred(); void wilma(int); private: int a_; }; #else typedef struct Fred Fred; #endif #ifdef __cplusplus extern "C" { #endif #if defined(__STDC__) || defined(__cplusplus) extern void c_function(Fred*); /* ANSI C prototypes */ extern Fred* cplusplus_callback_function(Fred*); #else extern void c_function(); /* K&R style */ extern Fred* cplusplus_callback_function(); #endif #ifdef __cplusplus } #endif #endif /*FRED_H*/
Fred.cpp:
// This is C++ code #include "Fred.h" int main() { Fred fred; c_function(&fred); ... }
c-function.c
/* This is C code */ #include "Fred.h" void c_function(Fred* fred) { cplusplus_callback_function(fred); }
c代码不能像c++代码一样,能够分辨出两个指针指向的是同一个对象,除非两个指针是严格的同一个类型。
举个例子,假设有两个对象,Derived* dp,和Base* bp,下面语句if(dp == bp)在c++下正常,c++编译器会自动将两个指针转为同一个类型,在这个例子中,两个指针都给转换成Base*,然后比较。这个过程,C做不到。
显而易见:C编译器不知道如何做种指针转换,所以如果非要对比这两个指针的话,Derived*到Base*的转换应该在C++编译器的地方完成,而不要C编译器做做这个工作。
看情况。
在以下情况下,C函数能够直接访问C++对象的数据成员:
展示本文用法的例子及使用linux时的链接方法,本例子有Makefile和VC6.0工程两种组织方式:下载地址。
Time-stamp:
1 http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html, http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html
2 http://book.douban.com/subject/1091086/