DLL模块:extern "C"的简单解析

1.揭密extern "C"

extern "C"包含双重含义,从字面上即可得到:首先,被它修饰的目标是 "extern”的;其次,被它修饰的目标是 "C”的。
首先来看一下 "extern”的含义
a.在一个文件内,如果外部变量不在文件的开头定义,其有效范围只限定在定义到文件的结束处。如果在定义前需要引用该变量,则要在引用之前用关键字 "extern” 对该变量做“外部变量声明”,表示该变量是一个已经定义的外部变量。有个这个声明,就可以从声明处起合理地使用该变量了。"extern”起到了扩展作用域的作用。

 

"C”的含义:(extern “C”)
C++通过函数参数的不同类型支持重载机制,编译器根据参数为每个重载函数产生不同的内部标识符。例如编译器为void Eat(Beef …);void Eat(Fish …);void Eat(Chicken …);三个Eat 函数产生象_eat_beef、_eat_fish、_eat_chicken 之类的内部标识符(不同的编译器可能产生不同风格的内部标识符)。
如果 C++程序要调用已经被编译后的C 函数,该怎么办?
假设某个 C 函数的声明如下:

1 void foo(int x, int y);

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

 1 extern “C”
 2 {
 3 void foo(int x, int y);
 4// 其它函数
 5 }
 6 或者写成
 7 extern “C”
 8 {
 9 #include “myheader.h”
10// 其它C 头文件
11 }

这就告诉C++编译译器,函数foo 是个C 连接,应该到库中找名字_foo 而不是找_foo_int_int。C++编译器开发商已经对C 标准库的头文件作了extern“C”处理,所以我们可以用#include 直接引用这些头文件。

2.extern "C"程序实例

假设有C文件:

1 //c.h
2 #ifndef _C_H_
3 #define _C_H_
4 
5 extern int add(int x, int y);
6 
7 #endif
1 //c.c
2 int add(int x, int y)
3 {
4     return x+y;
5 }
1 //cplusplus.cpp
2 #include <iostream>
3 #include "c.h"
4 using namespace std;
5 void main()
6 {
7     add(1, 0);
8     system("Pause");
9 }

产生错误:无法解析的外部符号 "int __cdecl add(int,int)" (?add@@YAHHH@Z),该符号在函数 _main 中被引用
为了解决这个问题,我们需要使用extern "C"。改写C文件

 1 //c.h
 2 #ifndef _C_H_
 3 #define _C_H_
 4 
 5 #ifdef __cplusplus
 6 extern "C" {
 7 #endif
 8 
 9 extern int add(int x, int y);
10 
11 #ifdef __cplusplus
12 }
13 #endif
14 
15 #endif

文件为*.c,__cplusplus没有被定义,extern "C" {}这时没有生效,对于C语言只是extern int add(int, int);而编译c++源文件,__cplusplus被定义,对于C++他看到的是extern "C" {extern int add(int, int);},编译器就会知道add(1, 0)调用的是C连接。
最后:很多DLL的生成文件(XXX.c)中常出现extern "C" ,windows采用C语言编译创建dll,C程序可以正确调用DLL,而当用户使用C++调用DLL时,extern "C" {}就起作用了。

posted @ 2013-06-25 19:23  瓶哥  Views(1614)  Comments(0Edit  收藏  举报