34 如何在同一个程序中结合C++和C

首先,你得确保你的C++和C编译器产生兼容的目标文件。

然后另外4件事情需要考虑:name mangling(名称重整)、statics(静态对象)初始化、动态内存分配、数据结构的兼容性。

  • Name Mangling(名字重整)

C++编译器为程序内的每一个函数编出独一无二的名字,在C语言中,却不会这样做,因为你无法将函数名重载。重载不兼容于大部分连接器,因为连接器往往将多个同名函数视为不正常。

为了解决这个问题,你需要某种方法告诉你的编译器,叫它不要重整某些函数名称。不要重整其他语言撰写的函数的名称——不论是以C语言,assembler,FORTRAN,Lisp,Forth,COBOL,还是其他语言。

要压制name mangling,必须使用C++的extern “C”指令:

extern "C"
void drawLine(int x1,int y1,int x2,int y2);

非C语言的其他语言写的函数如果要被C++调用的话都可以使用这个指令。

你甚至可以将C++函数声明为extern “C”。如果你正以C++语言开发的一个程序库,而你希望供应给其他语言的客户使用。

如果很多函数都不需要重整,extern “C”可以施行于一整组函数身上,只要以花括号封住头尾范围即可:

extern "C"
{
	void drawLine(int x1,int y1,int x2,int y2);
	void twiddleBits(unsigned char bits);
	void simulate(int iterations);
	...
}

如果用于C和C++同时使用的文件中,你可以加入__cplusplus(由于预处理器这个符号只针对C++才有定义):

#ifdef __cplusplus
extern "C"
{
#endif
	void drawLine(int x1,int y1,int x2,int y2);
	void twiddleBits(unsigned char bits);
	void simulate(int iterations);
	...
#ifdef __cplusplus
}
#endif	
  • Statics的初始化

许多代码会在main之前和之后执行代码。更明确说,static class对象、全局对象、namespace内对象以及文件范围(file scope)内的对象,其constructors总是在main之前执行,这个过程称为static initialization。通过static initialization产生出来的对象,其destructors必须在所谓的static destruction过程中被调用。那是发生在main结束之后。

经过编译的main,看起来像这样:

int main()
{
	performStaticInitialization();	//此行由编译器加入
	
	the statements you put in main go here;
	
	performStaticDestruction();		//此行由编译器加入
}

重点是:如果一个C++编译器采用这种方法来构造和析构对象,那么除非程序中有main,否则这种对象既不会被构造也不会被析构。

有时候,在C成分中撰写main似乎比较合理——如果程序主要以C完成而C++只是个支持库的话。尽管如此,C++程序库中内含static对象仍是极有可能的,所以如果能够,还是尽量在C++中撰写main的好。然而这并非意味你需要重写你的C代码。只要将你的C main重新命名的realMain,然后让C++ main调用realMain:

extern "C"
int realMain(int argc,char* argv[]); //以C语言完成此函数

int main(int argc,char* argv[])
{
	realMain(argc,argv);
}
  • 动态内存分配

动态分配规则很简单:程序的C++部分使用new和delete,程序的C部分则使用malloc和free。

有时候说比做容易很多,考虑粗糙(但好用)的strdup函数,它虽然并非C或C++标准的一份子,却被广泛使用:

char* strdup(const char* ps); //返回一个ps所指字符串的副本

strdup分配的内存必须由strdup的调用者负责释放。如果它自C函数库,使用free;如果它来自一个C++程序库,那么应该用delete。因此调用strdup后,你应该做的事情不只随系统的不同而不同,也随编译器的不用而不同。为了降低这种头痛的移植问题,请避免调用标准程序库以外的函数或是大部分计算平台上尚未稳定的函数。

  • 数据结构的兼容性

如果你的C++和C编译器有着兼容的输出,两个语言的函数便可以安全的交换对象指针、non-member函数指针或者static函数指针。很自然的,structs以及内建类型的变量也可以安全跨越C++/C边界。

对于struct来说没如果只是加上一些非虚函数,其内存布局应该不会改变,如果加上虚函数,或者继承也会改变struct的布局,所以一个struct如果带有base structs(或classes),无法和C函数交换。

如果你打算在同一程序中混合使用C++和C,请记住以下简单的守则:

  • 确定你的C++和C编译器产出兼容的目标文件(object files)。
  • 将双方都使用的函数声明为extern “C”。
  • 如果可能,尽量在C++中撰写main。
  • 总是以delete删除new返回的内存;总是以free释放malloc返回的内存。
  • 将两个语言间的“数据结构传递”限制于C所能了解的形式;C++ structs如果内含非虚函数,倒是不受此限制。

猜你喜欢

转载自blog.csdn.net/weixin_28712713/article/details/82619841
34