C++程序有时需要调用其它语言编写的函数,最常见的是调用C语言编写的函数。像所有其它名字一样,其它语言中的函数名字也必须在C++中进行声明,并且该声明必须指定返回类型和形参列表。对于其它语言编写的函数来说,编译器检查其调用的方式与处理普通C++函数的方式相同,但是生成的代码有所区别。C++使用链接指示(linkage directive)指出任意非C++函数所用的语言。
链接指示(linkage directive):支持C++程序调用其它语言编写的函数的一种机制。所有编译器都应支持调用C++和C函数,至于是否支持其它语言则由编译器决定。
要想把C++代码和其它语言(包括C语言)编写的代码放在一起使用,要求我们必须有权访问该语言的编译器,并且这个编译器与当前的C++编译器是兼容的。
声明一个非C++的函数:链接指示可以有两种形式:单个的或复合的。链接指示不能出现在类定义或函数定义的内部。同样的链接指示必须在函数的每个声明中都出现。
链接指示的第一种形式包含一个关键字extern,后面是一个字符串字面值常量以及一个”普通的”函数声明。其中的字符串字面值常量指出了编写函数所用的语言。编译器应该支持对C语言的链接指示。此外,编译器也可能会支持其它语言的链接指示,如extern “Ada”、extern “FORTRAN”等。
链接指示与头文件:我们可以令链接指示后面跟上花括号括起来的若干函数的声明,从而一次性建立多个链接。花括号的作用是将适用于该链接指示的多个声明聚合在一起,否则花括号就会被忽略,花括号中声明的函数名字就是可见的,就好像在花括号之外声明的一样。多重声明的形式可以应用于整个头文件。
当一个#include指示被放置在复合链接指示的花括号中时,头文件中的所有普通函数声明都被认为是由链接指示的语言编写的。链接指示可以嵌套,因此如果头文件包含带自带链接指示的函数,则该函数的链接不受影响。
C++从C语言继承的标准库函数可以定义成C函数,但并非必须:决定使用C还是C++实现C标准库,是每个C++实现的事情。
指向extern “C”函数的指针:编写函数所用的语言是函数类型的一部分。因此,对于使用链接指示定义的函数来说,它的每个声明都必须使用相同的链接指示。而且,指向其它语言编写的函数的指针必须与函数本身使用相同的链接指示。指向C函数的指针与指向C++函数的指针是不一样的类型。一个指向C函数的指针不能用在指向初始化或赋值操作后指向C++函数,反之亦然。就像其它类型不匹配的问题一样,如果我们试图在两个链接指示不同的指针之间进行赋值操作,则程序将发生错误。
链接指示对整个声明都有效:当我们使用链接指示时,它不仅对函数有效,而且对作为返回类型或形参类型的函数指针也有效。因为链接指示同时作用于声明语句中的所有函数,所以如果我们希望给C++函数传入一个指向C函数的指针,则必须使用类型别名。
导出C++函数到其它语言:通过使用链接指示对函数进行定义,我们可以令一个C++函数在其它语言编写的程序中可用。值得注意的是,可被多种语言共享的函数的返回类型或形参类型受到很多限制。例如,我们不太可能把一个C++类的对象传给C程序,因为C程序根本无法理解构造函数、析构函数以及其它类特有的操作。
对链接到C的预处理器的支持:有时需要在C和C++中编译同一个源文件,为了实现这一目的,在编译C++版本的程序时预处理器定义__cplusplus(两个下划线)。利用这个变量,我们可以在编译C++程序的时候有条件地包含进来一些代码。
重载函数与链接指示:链接指示与重载函数的相互作用依赖于目标语言。如果目标语言支持重载函数,则为该语言实现链接指示的编译器很可能也支持重载这些C++的函数。C语言不支持函数重载,因此也就不难理解为什么一个C链接指示只能用于说明一组重载函数中的某一个了。如果在一组重载函数中有一个是C函数,则其余的必定都是C++函数。
C++虽然兼容C,但C++文件中函数编译后生成的符号与C语言生成的不同。相同的函数,在C和C++中,编译后生成的符号不同。
extern"C"的主要作用就是为了能够正确实现C++代码调用其它C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
extern "C" makes a function-name in C++ have 'C' linkage (compiler does not mangle the name) so that client C code can link to (i.e use) your function using a 'C' compatible header file that contains just the declaration of your function. Your function definition is contained in a binary format (that was compiled by your C++ compiler) that the client 'C' linker will then link to using the 'C' name.
Since C++ has overloading of function names and C does not, the C++ compiler cannot just use the function name as a unique id to link to, so it mangles the name by adding information about the arguments. A C compiler does not need to mangle the name since you can not overload function names in C. When you state that a function has extern "C" linkage in C++, the C++ compiler does not add argument/parameter type information to the name used for linkage.
Just so you know, you can specify "C" linkage to each individual declaration/definition explicitly or use a block to group a sequence of declarations/definitions to have a certain linkage. (from: https://stackoverflow.com/questions/1041866/in-c-source-what-is-the-effect-of-extern-c )
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
c_head.h:
#ifndef FBC_CPPBASE_C_HEAD_H_
#define FBC_CPPBASE_C_HEAD_H_
void tmp_f(void);
#endif // FBC_CPPBASE_C_HEAD_H_
c_head.c:
#include "c_head.h"
#include
void tmp_f(void)
{
fprintf(stdout, "\n This is a C code\n");
}
extern_c.hpp:
#ifndef FBC_CPPBASE_EXTERN_C_HPP_
#define FBC_CPPBASE_EXTERN_C_HPP_
namespace extern_c_ {
// the first method
#ifdef __cplusplus
extern "C" {
#include "c_head.h"
}
#endif
// the second method
//extern "C" void tmp_f(void);
int test_extern_c_1();
} // namespace extern_c_
#endif // FBC_CPPBASE_EXTERN_C_HPP_
extern_c.cpp:
#include "extern_c.hpp"
#include
namespace extern_c_ {
//
// reference: http://www.thegeekstuff.com/2013/01/mix-c-and-cpp/
int test_extern_c_1()
{
tmp_f();
return 0;
}
///
// reference: https://stackoverflow.com/questions/1292138/when-to-use-extern-c-in-c
/*
extern "C" makes names not mangled.
It used when:
1. We need to use some C library in C++
extern "C" int foo(int);
2. We need export some C++ code to C
extern "C" int foo(int) { something; }
3. We need an ability to resolve symbol in shared library -- so we need to get rid mangling
extern "C" int foo(int) { something; }
typedef int (*foo_type)(int);
foo_type f = (foo_type)dlsym(handle,"foo")
*/
} // namespace extern_c_