一、extern "C"修饰的变量和函数是按照C语言方式编译和连接的,c编译器不识别extern "C"。
二、C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
三、CPP支持多态性,也就是具有相同函数名的函数可以完成不同的功能,CPP通常是通过参数区分具体调用的是哪一个函数。在编译的时候,CPP编译器会将参数类型和函数名连接在一起,于是在程序编译成为目标文件以后,CPP编译器可以直接根据目标文件中的符号名将多个目标文件连接成一个目标文件或者可执行文件。但是在C语言中,由于完全没有多态性的概念,C编译器在编译时除了会在函数名前面添加一个下划线之外,什么也不会做(至少很多编译器都是这样干的)。由于这种的原因,当采用CPP与C混合编程的时候,就可能会出问题。
四、假设在某一个头文件中定义了这样一个函数:int foo(int a, int b); 而这个函数的实现位于一个.c文件中,同时,在.cpp文件中调用了这个函数。那么,当CPP编译器编译这个函数的时候,就有可能会把这个函数名改成_fooii,这里的ii表示函数的第一参数和第二参数都是整型。而C编译器却有可能将这个函数名编译成_foo。也就是说,在CPP编译器得到的目标文件中,foo()函数是由_fooii符号来引用的,而在C编译器生成的目标文件中,foo()函数是由_foo指代的。但连接器工作的时候,它可不管上层采用的是什么语言,它只认目标文件中的符号。于是,连接器将会发现在.cpp中调用了foo()函数,但是在其它的目标文件中却找不到_fooii这个符号,于是提示连接过程出错。extern "C" {}这种语法形式就是用来解决这个问题的。
五、首先假设有下面这样三个文件:(c++中使用c函数)
/* file: test_extern_c.h */
#ifndef __TEST_EXTERN_C_H__
#define __TEST_EXTERN_C_H__
#ifdef __cplusplus
extern "C" {
#endif
/*
* this is a test function, which calculate
* the multiply of a and b.
*/
extern int ThisIsTest(int a, int b);
#ifdef __cplusplus
}
#endif /* end of __cplusplus */
#endif
在这个头文件中只定义了一个函数,ThisIsTest()。这个函数被定义为一个外部函数,可以被包括到其它程序文件中。假设ThisIsTest()函数的实现位于test_extern_c.c文件中:
/* test_extern_c.c */
#include "test_extern_c.h"
int ThisIsTest(int a, int b)
{
return (a + b);
}
可以看到,ThisIsTest()函数的实现非常简单,就是将两个参数的相加结果返回而已。现在,假设要从CPP中调用ThisIsTest()函数:
/* main.cpp */
#include "test_extern_c.h"
#include <stdio.h>
#include <stdlib.h>
class FOO {
public:
int bar(int a, int b)
{
printf("result=%i\n", ThisIsTest(a, b));
}
};
int main(int argc, char **argv)
{
int a = atoi(argv[1]);
int b = atoi(argv[2]);
FOO *foo = new FOO();
foo->bar(a, b);
return(0);
}
在这个CPP源文件中,定义了一个简单的类FOO,在其成员函数bar()中调用了ThisIsTest()函数。下面看一下如果采用gcc编译test_extern_c.c,而采用g++编译main.cpp并与test_extern_c.o连接会发生什么情况:
[cyc@cyc src]$ gcc -c test_extern_c.c
[cyc@cyc src]$ g++ main.cpp test_extern_c.o
[cyc@cyc src]$ ./a.out 4 5
result=9
可以看到,程序没有任何异常,完全按照预期的方式工作。那么,如果将test_extern_c.h中的extern "C" {}所在的那几行注释掉会怎样呢?注释后的test_extern_c.h文件内容如下:
/* test_extern_c.h */
#ifndef __TEST_EXTERN_C_H__
#define __TEST_EXTERN_C_H__
//#ifdef __cplusplus
//extern "C" {
//#endif
/*
* this is a test function, which calculate
* the multiply of a and b.
*/
extern int ThisIsTest(int a, int b);
//#ifdef __cplusplus
// }
//#endif /* end of __cplusplus */
#endif
除此之外,其它文件不做任何的改变,仍然采用同样的方式编译test_extern_c.c和main.cpp文件:
[cyc@cyc src]$ gcc -c test_extern_c.c
[cyc@cyc src]$ g++ main.cpp test_extern_c.o
/tmp/cca4EtJJ.o(.gnu.linkonce.t._ZN3FOO3barEii+0x10): In function `FOO::bar(int, int)':
: undefined reference to `ThisIsTest(int, int)'
collect2: ld returned 1 exit status
在编译main.cpp的时候就会出错,连接器ld提示找不到对函数ThisIsTest()的引用。
六、c 文件中使用c++(c目标文件、和c++目标文件的连接需要使用g++)
//* file TestClass.h */
class HJH
{
public:
int add(int a, int b)
{
return (a + b);
}
};
/* file TestCpp.cpp */
#include "TestClass.h"
extern "C" int add_cpp(int a, int b);
int add_cpp(int a, int b)
{
HJH hjh;
return hjh.add(a, b);
}
/*file TestC.c */
#include "stdio.h"
extern int add_cpp(int a, int b);
int main()
{
printf("add_cpp = %d/n", add_cpp(2, 5));
return 0;
}
七、extern "C" 使用情况
extern "C"的使用要点
1. 可以是单一语句
extern "C" double sqrt(double);
2. 可以是复合语句, 相当于复合语句中的声明都加了extern "C"
extern "C"
{
double sqrt(double);
int min(int, int);
}
3.可以包含头文件,相当于头文件中的声明都加了extern "C"
extern "C"
{
#i nclude <cmath>
}
4. 不可以将extern "C" 添加在函数内部
5. 如果函数有多个声明,可以都加extern "C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则。
6. 除extern "C", 还有extern "FORTRAN" 等。