`
lovnet
  • 浏览: 6693449 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

C++ 的 C 方式编译和 C 链接约定

 
阅读更多

原文:C++ 的 C 编译和链接方式 (VC)
作者:Breaker <breaker.zy_AT_gmail>


C++ 与 C 的编译方式

所有的 C 程序都是 C++ 程序,而所有的 C++ 编译器都是 C 编译器(几乎所有),兼容 C99 标准 wiki: C99。一些编译器以 C/C++ Compiler 命名,如 VC: cl (C/C++ Optimizing Compiler)、ICC (Intel C/C++ Compilers)

开发环境:VC 2005

以 VC 为例,它根据源文件的扩展名来选择 C 或 C++ 的编译方式,并且有一组编译选项显式指定以 C 还是 C++ 的方式编译,规则如下:

1. .c 源文件默认以 C 方式编译,这时拒绝 C++ 的语法,如 class

2. .cpp/.cxx 源文件默认以 C++ 方式编译

3. 使用 /Tc, /Tp, /TC, /TP 编译选项显式指定源文件的 C 或 C++ 编译方式:

  • /Tc file.ext: 指定 file.ext 以 C 方式编译
  • /Tp file.ext: 指定 file.ext 以 C++ 方式编译
  • /TC: 指定传给 cl 的所有源文件都以 C 方式编译,是全局选项
  • /TP: 指定传给 cl 的所有源文件都以 C++ 方式编译

规则的覆盖顺序为:Tc/Tp => TC/TP => 根据扩展名 .c/cpp/.cxx 默认判断

参考 MSDN:

编译示例说明

工程 TestProj 含以下源文件:

testproj.c: 程序入口和主要逻辑
tool.h: 工具例程 void tool() 的声明
tool.c: 工具例程 void tool() 的实现
ctool.h: 工具例程 void ctool() 的声明
ctool.c: 工具例程 void ctool() 的实现

include 预处理、编译单元和对象文件对应如下:

编译单元:translation unit,上面用 [] 表示,一个 .c/.cpp 源文件预处理之后的结果。编译器针对编译单元进行编译,一个编译单元生成一个 obj 对象文件

分别编译:separate compilation,类似 TestProj 的这种源文件组织方式,是一种实际中常用的减小编译单元依赖、加快重编译过程的源文件组织方式

编译示例 1

编译命令如下:

解释:
  • /TP: 覆盖默认的扩展名自动判断方式,将 testproj.c、tool.sub 作为 C++ 源文件编译
  • /Tc ctool.c: 覆盖 /TP 的作用,将 ctool.c 作为 C 源文件编译

意义:

Q1: 明显是 C 语言程序 testproj.c,为何要以 C++ 方式编译?
A1: 利用 C++ 的严格类型规则检查。C++ 比 C 拥有更严格的类型规则,以 C++ 的方式编译,如果程序中有类型安全 (type safety) 的 BUG,编译会报错或告警。案例:用 DDK 开发 Windows 驱动时,均是 C 程序,但通常以 .cpp 命名源文件,或者使用 /TP 编译选项

Q2: 在全部用 C++ 方式编译的情况下,为何 ctool.c 又用 /Tc 指明以 C 方式编译?
A2: 这种情况应用在:需要用 C 的约定进行链接 (C Linkage),例如 TestProj 生成的可执行文件是需要导出 C 链接约定的 ctool() 函数的动态链接库 (DLL)

Q3: 为何一定非要以 C 链接约定,而不以 C++ 链接约定导出函数呢?
A3: 这很大程度上属于设计、人为规定和版本兼容问题,两种情况:(1). 以 C 链接约定导出函数是在很早的时候由 DLL 用户和 DLL 提供者共同商榷而定的(或是 DLL 提供者单方面规定的),由于长期的使用和影响,二进制接口不能随便更改 (2). DLL 用户程序用 C 语言开发,如果 DLL 导出 C++ 链接约定的函数,不便使用

由于 Q2、Q3 原因,假定 TestProj 是 DLL 工程,编译命令修改如下:

/LD: 传递给链接器 /DLL 链接选项,以生成 DLL,并以 .dll 为扩展名,即具有 /link /DLL /OUT:testproj.dll 的作用

而 ctool.h 大致如下:

问题:如果 testproj.c 中调用了 ctool(),上面的编译命令无法成功,在链接阶段报错

原因:Q2 中说明在动态链接中需要保持一致的链接约定,调用者和被调者(实现方)需要用一样的 C Linkage 或 C++ Linkage 才行。其实静态链接也同样需要遵守这个规则,main.obj 可以调用 tool.obj 中的 tool() 函数,是因为它们都使用 C++ Linkage,链接时 main.obj 和 tool.obj 对 tool() 的修饰名都是 ?tool@@YAXXZ。而 ctool.obj 是以 C 方式编译的,对于 ctool() 函数,main.obj 寻找的是 C++ 修饰名的 ?ctool@@YAXXZ,但 ctool.obj 中只有 C 修饰名的 _tool,两者自然无法链接

调用和链接约定

三个概念:

  • 调用约定 (Calling Convention):用来规定函数调用时,参数入栈顺序、谁负责退栈、修饰名等规则的约定,如 __cdecl、__stdcall、__thiscall,详细说明见 MSDN:Calling Conventions

  • 修饰名 (Decorated Name):源程序中函数的原始名经过编译后,在二进制模块内部转换为另一个名字,称为修饰名,二进制模块间的此函数的引用、查找等,都以修饰名进行

    为什么二进制模块内部需要修饰名?(修饰名的作用)

    1. 不同调用约定的函数的修饰名是不一样的,修饰名的作用之一就是标识不同的调用约定
    2. 同一调用约定情况下,C 与 C++ 方式编译所得修饰名不一样。C++ 为了支持函数名重载,将函数的参数类型、返回类型等信息编码成字符,和原函数名拼成修饰名。修饰名的另一作用是支持函数重载

    参考 MSDN:

  • 链接约定 (Linkage):链接约定是调用约定与修饰名在链接时的具体表现,如 C linkage 和 C++ linkage

    参考 MSDN:

操作修饰名的一个主要时机是链接阶段,链接器在各二进制模块间查找引用(被调用)的函数修饰名,组装起它们间的函数调用,无论是 (1). 静态链接,二进制模块 obj、lib 等,还是 (2). 动态链接,二进制模块 exe、dll 等

多数情况下程序员不需要直接操作修饰名,只要注意:修饰名和链接约定的不一致导致链接失败,务必保持一致就行了,具体的修饰名转换、查找操作交给链接器,不用操心

但如果在汇编源程序中引用 C/C++ 源程序中的函数,就需要显式指定修饰名,而非原始名,因为修饰名才是存在于二进制模块中的“函数真名”,而汇编不是编译,不吃调用约定那套东西(换句话说,你得手工实现调用约定)

编译示例 2

在示例 1 的基础上修改 ctool.h:

仍采用示例 1 的编译命令:

解释:

  • extern "C": 虽然以 C++ 方式编译 [testproj.c include=> ctool.h],但对 ctool() 函数使用 C 链接约定,链接时 main.obj 和 ctool.obj 就会有相同的 C 修饰名 _ctool
  • __cplusplus 的条件编译:因为以 C 方式编译 [ctool.c include=> ctool.h],而 C 语法中没有 extern "C",只有 extern,所以用 C++ 语言标准预定义宏 __cplusplus 和条件编译跳过 extern "C"

编译示例 3

在示例 2 编译命令的基础上去掉 /Tc ctool.c:

等价于:

虽然成功,但这与示例 2 是有区别的:

(1). 示例 2 中以 C 方式编译 [ctool.c include=> ctool.h],而这里以 C++ 方式编译它,所以执行了 C++ 的严格类型检查,只是将 ctool() 变为 C 的链接约定

(2). 如果 ctool.c 中有其它函数,又没用 extern "C",则它们会用 C++ 的修饰名

这种情况下,ctool.h 中的 __cplusplus 条件编译可以去掉,但为了 (ctool.h, testproj.dll) 发布后,也能被 C 程序使用,通常保留 __cplusplus 条件编译

分享到:
评论

相关推荐

    函数调用约定与函数名称修饰规则

    函数调用约定与函数名称修饰规则-很实用 使用C/C++语言开发软件的程序员...本文分别对C和C++这两种编程语言的函数调用约定和函数名修饰规则进行详细的解释,比较了它们的异同之处,并举例说明了以上问题出现的原因。

    新手学习C++入门资料

    上面两个都是C风格的强制类型转换,C++还增加了一种转换方式,比较一下上面和下面这个书写方式的不同: long int el=123; short i=int (el); float m=34.56; int i=int (m); 使用强制类型转换的最大好处...

    C语言入门经典(第4版)--源代码及课后练习答案

    IvorHorton还著有关于C、C++和Java的多部入门级好书,如《C语言入门经典(第4版)》和《C++入门经典(第3版)》。 译者  杨浩,知名译者,大学讲师,从事机械和计算机方面的教学和研究多年,发表论文数篇,参编和翻译的...

    μC_OS-Ⅱ中文资料大全

    第一章:范例 在这一章里将提供三个范例来说明如何...用户只需要简单地编译、链接和执行。其次,使用Borland C/C++产 生的80186的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix 公司的80x86 CPU 兼容。

    嵌入式实时操作系统UCOS-II(邵贝贝译)

    笔者之所以在本书一开始就写这一章是为了让读者尽快开始...用户只需要简单地编译、链接和执行。其次,使用Borland C/C++产生的80186的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix公司的80x86 CPU兼容。

    NiceBASIC测试版3

    和图像库(用于编写游戏),还可以使用标准C语言函数库里的函数(调用静态库形式链接),也就是说NB可以在编译时链接所有用标准C语言编写的静态库(LIB)做为函数功能扩展,并且还可以调用WIN32API的大部分函数,...

    liblcl:通用的跨平台GUI库,核心使用Lazarus LCL

    中文 liblcl 一个通用的跨平台GUI库,核心使用Lazarus LCL。 已支持语言: go: c/c++: 完成度较高的语言: ...注: 如果在liblcl源代码ExtDecl.inc文件中启用了UsehandleException编译指令,则不再需要MySyscall

    Ucos中文书

    在这一章里将提供三个范例来说明如何使用 µC/OS-II。...用户只需要简单地编译、链接和执行。其次,使用Borland C/C++产生的80186的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix公司的80x86 CPU兼容。

    ucos 中文电子书

    第一章:范例 在这一章里将提供三个范例来说明如何使用 ...用户只需要简单地编译、链接和执行。其次,使用Borland C/C++产生的80186的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix公司的80x86 CPU兼容。

    uCOS-II中文版电子书 PDF

    第一章 范例 在这一章里将提供三个范例来说明如何...用户只需要简单地编译、链接和执行。其次,使用Borland C/C++产 生的80186 的目标代码(实模式,在大模式下编译)与所有Intel、AMD、Cyrix 公司的80x86 CPU 兼容。

    uc-os2 中文资料大全

    第一章:范例 在这一章里将提供三个范例来说明如何...用户只需要简单地编译、链接和执行。其次,使用 Borland C/C++产 生的 80186 的目标代码(实模式,在大模式下编译)与所有 Intel、AMD、Cyrix 公司的 80x86 CPU 兼容。

    易语言5.1测试版补丁破解+升级包+调用LIB的例子

     支持静态链接其它编程语言(如C/C++、汇编等)编译生成的静态库(.LIB或.OBJ),但仅限于COFF格式,支持cdecl和stdcall两种函数调用约定。  使用说明如下:函数声明和调用方法与DLL命令一致;“库文件名”以....

    自己写的dll的简介

    1. 如果你只用C语言,那么必然以C文件创建DLL(自动编出C符号名),考虑到潜在的C++用户(此类用户多以静态调用方式使用DLL,因而需要看到其函数声明),我们还需要使用EXTERN_C关键字(详见上面的讨论)。...

    易语言5.11

    支持静态链接其它编程语言(如C C++ 汇编等)编译生成的静态库( LIB或 OBJ) 但仅限于COFF格式 支持cdecl和stdcall两种函数调用约定 使用说明如下:函数声明和调用方法与DLL命令一致;“库文件名”以 lib或 obj为...

    NiceBASICV 1.0中文编程正式版【MSI安装版】

    导出函数可以为多种调用约定,比如:Stdcall(标准WINAPI)、 Cdecl(兼容C语言)、Pascal 。可供给其它语言调用。  四、静态链接库。供给标准的C语言调用链接。就是说NB的静态库是兼容C语言的LIB,互相通用。 为了...

    NiceBASIC V1.0中文编程正式版(绿色版).rar

    导出函数可以为多种调用约定,比如:Stdcall(标准WINAPI)、 Cdecl(兼容C语言)、Pascal 。可供给其它语言调用。  四、静态链接库。供给标准的C语言调用链接。就是说NB的静态库是兼容C语言的LIB,互相通用。 为了...

    windows驱动开发技术详解-part2

     3.1 用C语言还是用C++语言  3.1.1 调用约定  3.1.2 函数的导出名  3.1.3 运行时函数的调用  3.2 用DDK编译环境编译驱动程序  3.2.1 编译版本  3.2.2 nmake工具  3.2.3 build工具  3.2.4 makefile...

    Windows驱动开发技术详解的光盘-part1

     3.1 用C语言还是用C++语言  3.1.1 调用约定  3.1.2 函数的导出名  3.1.3 运行时函数的调用  3.2 用DDK编译环境编译驱动程序  3.2.1 编译版本  3.2.2 nmake工具  3.2.3 build工具  3.2.4 makefile...

    精通Windows.API-函数、接口、编程实例.pdf

    3.1 使用Visual C/C++编译链接工具 26 3.1.1 编译器cl.exe 27 3.1.2 资源编译器rc.exe 31 3.1.3 链接器link.exe 32 3.1.4 其他工具 38 3.1.5 编译链接工具依赖的环境变量 39 3.1.6 示例:使用/D选项...

Global site tag (gtag.js) - Google Analytics