您的位置:首页 > 编程语言 > C语言/C++

C或C++编译成lib或dll时修饰符extern C及相关的浅析

2017-11-11 11:35 316 查看
(注:以下只是我在使用时遇见的问题的一些理解,理解的不是很透彻,也没去看具体的一些原理。
         同时,下文有什么错的地方,请给我朋友私聊我指出,我好修改,以免误导大家。谢谢!
)

开发平台:vs2010下的VC++  WIN32项目

关键字:extern “c” extern “c++” __stdcall __cdecl  __fastcall 
_declspec(dllexport) _declspec(dllimport)

1:extern “c” 和 extern “c++” 的浅谈

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

且extern "C"只能
dd10
在C++代码中使用,其是告诉编译器被extern "C"修饰的这部分C++代码要以C的规则编译。因为在C代码中,程序本来就是以C的规则编译的,所以extern "C"就多余
注:如果在C代码中加了extern "C",编译器可能编译不过,示例如下:



但是,经但验证,在C代码中,无论添加extern “c” 还是extern “c++”,都不能,编译通过,而C++代码中,extern “c”或extern “c++”都可以使用。

总结:我认为。
对于C代码就extern “c”和extern “c++”都不要添加,编译器本来就是以C的规范编译的。
对于C++的代码,如果你希望让它以C++的规范编译,则不需要添加extern “c”;如果你希望它以C的规范编译则需要添加extern “c”。

OK,这部分就到这。

2:_declspec(dllexport)和_declspec(dllimport)的浅谈
_declspec(dllexport) 和_declspec(dllimport)的使用就先说说动态库和静态库(参考我另一篇文章)。

所以,一般而言:
_declspec(dllexport) 只在编译成动态库时使用;
_declspec(dllimport)则是在调用DLL库时使用。

_declspec(dllexport) 的使用:
程序编译成动态库,就必须添上_declspec(dllexport),这是让DLL中有函数。这样你在调用DLL时,连接的时候才不会出错。
同时,编译成动态库时没有要_declspec(dllexport),编译出来的输出文件没有lib文件,只有dll(动态库有dll和lib);如果添加上了_declspec(dllexport),则会编译出dll和lib。如下:







你看,就没有lib文件

添加上_declspec(dllexport)后:







这就有lib文件。

_declspec(dllimport)的使用:
_declspec(dllimport)没测试,不做看法详解。

总结:

在程序编译成动态库时,_declspec(dllexport)一定要加上,这样才会编译出dll和lib文件。如果不加上,则只有dll文件,没有lib文件。

在程序编译成静态库时,_declspec(dllexport)就不用加上。静态库只有一个lib文件。

OK,这部分就到这。

3: __stdcall 和 __cdecl  和 __fastcall 的浅谈

__stdcall和__cdecl都是函数调用约定关键字,先给出这两者的区别,然后举实例分析:
  __stdcall:参数由右向左压入堆栈;堆栈由函数本身清理。
  __cdecl:参数也是由右向左压入堆栈;但堆栈由调用者清理。
另外,这两者在同一名字修饰(如extern “C”)约定下,编译过后变量和函数的名字也不一样   

这三个可以通过添加在代码前来起作用,也可以通过VS的属性配置来。
(右键项目名===》属性,就会显出下面的界面)



下面我直接直接贴出不同修饰符的情况:

C代码 _cdecl



C代码 _fastcall



C代码 _stdcall



C++代码 _cdecl



C++代码 _fastcall



C++代码 _stdcall



不同的修饰,其编译后的dLL中的函数名都不一样(我这里修饰的一个函数)
这就是为什么你在调用生成的Dll后,使用里面的函数,会出现不能LINK的错误。
因为你添加的头文件里函数的声明是:”int add_c_cdecl(int a,int b);“(我这里截图中实例的函数名),而如果你用的是
C代码 _stdcall修饰的话,其生成的DLL里函数名确实 “_add_c_cdecl@8”(见上图)。这两个不一致,编译器肯定就找不到。所以会LINK出错。

总结:
你用哪个修饰符修饰,就造成了编译出来的函数名的格式不同。

C和C++程序的缺省调用方式则为__cdecl(就vs2010下的VC++而言,因其默认的配置是__cdecl),因此在不显式写明调用约定的情况下,一般都是采用__cdecl方式,而在与Windows
API打交道的场景下,通常都是显式的写明使用__stdcall,才能与Windows API保持一致(Windows API是标准的C库格式)。

4:.def文件

4.1 .def文件的格式

LIBRARY//必要

EXPORTS//必要

函数名 1  @1//你的函数名

函数名 2  @2//你的函数名

。。。。。。。

4.2 在VC++下可以利用添加文件,找到以 .def结尾的文件,命名创建就ok

4.3 作用

其作用就是告诉编译器,编译“函数名 1”时其在DLL里的名字就叫“函数名 1”,避免了 __stdcall 和 __cdecl  和 __fastcall 修饰时DLL里的函数名的不确定性

DEF文件手工定义导出哪些函数的一个方法。当然,如果你的DLL里全是C++的类的话,你无法在DEF里指定导出的函数,只能用__declspec(dllexport)导出类。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: