http://hi.baidu.com

在面向对象编程语言出现之前,如果你想要打印不同类型的数据,需要写多个方法

,象是PrintInteger(int i),PrintString(string s) 和 PrintFloat(float f)

。也就是说, 你必须通过命名来区别行为和数据类型,因为 OOP语言出现前任一语言象是C,不允许你用相同的名字写方法,

即使他们的参数类型不同。

C++的来到实现了方法重载。因此,你可以写多个方法 , 象是

PrintInteger(int i)、PrintString(string s) 和 PrintFloat(float

f),编译器自会准确调用特定的Print方法。方法重载被一种称为名称重整(name

mangling)的技术所支持,在这种技术中,编译器通过把原方法名称与其参数相结合产生一个独特的内部名字来取代原方法名称。

如此,当你调用Print(1)的时候, 编译器可能在内部用源于参数类型的前缀重命名Print方法,这样一来Print(1)可能就变成

i_Print (1) 。

下面是更详细的例子:

C++编译器实际上将下面这些重载函数:

1

voidprint(inti);

2

voidprint(charc);

3

voidprint(floatf);

4

voidprint(char* s);

编译为:

1

_print_int

2

_print_char

3

_print_float

4

_pirnt_string

这样的函数名,来唯一标识每个函数。注:不同的编译器实现可能不一样,但是都是利用这种机制。所以当连接是调用print(3)时,它会去查找_print_int(3)这样的函数。下面说个题外话,正是因为这点,重载被认为不是多态,多态是运行时动态绑定(“一种接口多种实现”),如果硬要认为重载是多态,它顶多是编译时“多态”。

C++中的变量,编译也类似,如全局变量可能编译g_xx,类变量编译为c_xx等。连接是也是按照这种机制去查找相应的变量

方法重载仅是多态性的一种情形。

名称重整是一种支持方法重载的机制。更普遍的情况下,多态性是与继承相联系。 什么是继承呢?继承就是一个新类 (称为子类)

从被继承类(称为父类或超类)取得自身的部分定义同时增加一些自己的新的信息。 如果你在相同的类中重载方法,

数据类型必须是不同的。如果你在继承关系下重载方法,

子类与父类的方法可能完全相同,而且名称重整器生成同样的重整名称。

举例来说, 假设一个超类定义一个Print(int

i)方法而一个从它继承的子类也定义了一个Print(int

i)方法。当你有一个子类的实例时,运用多态性调用Child.Print(int);而当你产生一个父类的实例时运用多态性调用Parent.Print(int)。这就是继承多态性:相同的名字和签字但是类却不同。

继承多态性是通过使用一种与名称重整相关的另外一种机制实现的。编译器把方法放置在一个被称为虚拟方法表(其实是一个方法数组)的地方。每一个方法在VMT中都有一个索引,

如此当Print(int)被调用的时候,

编译器将被路由到VMT处找寻Print方法和类的内在索引。这样一来,编译器就可以调用正确的方法实现。由编译器负责管理所有的VMT索引和类偏移量。

简言之,多态性使你能够用非常相似的名字定义许多方法,这里的名字往往都是直观易记的。

OOP编译器自会根据调用者类理解到底该调用哪个方法。

http://it.china-b.com/cxsj/cc/20090612/91952_1.html

Name-mangling是指为了在目标文件符号表中和连接过程中使用的名字通常和编译目标文件的源程序中的名字不一样,编译器将目标源文件中的名字进行调整。Name-mangling不是一个非常新的技术,例如在C语言中也有,我们在汇编C语言时经常看到的以下划线“_”开头的函数名,其实就是C编译器将函数名进行了Name-mangling。

但是在C++中Name-mangling要复杂的多。因为C++中支持overload和override,这就导致了C++编译器必须要有完成的Name-mangling把函数名或者变量名进行调整。

一种Name-mangling的方法(选自Linkerandloader):在C++类外的数据变量的名字完全不进行调整。一个叫做foo的数组的名字调整后还是foo。没有与类相关的函数名字的调整是根据参数类型用__F后缀和一串代表参数类型的字母进行的。

参数类型一般会被进行简写(如void→v,int→i,float→f,char→c,double→d,varages→e,long→l,unsigned→U,const→C,volatile→V,pointer→P,reference→R,function→f,pointertonthmembers→MnS等等)。举例,一个函数是func(float,int,unsignedchar)可能变成func__FfiUc。

类的名字被认为是有类型的,被编码成类的名字的长度后面跟着名字,如4Pair。类还可以含有多个层次的内部类的名字;这些”合法的”名字用Q来编码,后面是一个数字标明层次的数目,然后是类的名字,这样First::Second::Third变成了Q35First6Second5Third。也就是说一个有两个类参数的函数f(Pair,First::Second::Third)变成了f__F4PairQ35First6Second5Third。

类成员函数编码成函数名字,然后是两个下划线,然后是编码后的类的名字,然后是F和参数,这样,cl::fn(void)变成fn__2clFv。所有的操作符也都编码成4或5个字符的名字,如__ml代表*,而__aor代表

http://blog.sina.com.cn/s/blog_4bb59dc40100ea1d.html

以vc为例,

1、c和c++之间:

void foo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int

之类的名字用来支持函数重载和类型安全连接.由于编译后的名字不同,C++程序不能

直接调用C函数.C++提供了一个C连接交换指定符号extern"C"来解决这个问题.

2、不同编译器之间:

即使是按照c链接,但是不同的调用约定,比如__stdcall 和 __cdecl调用也会产生不同的名字改编。

---------------------------------------------------------

关于调用约定

---------------------------------------------------------

调用约定 堆栈清除 参数传递

__cdecl 调用者 从右到左,通过堆栈传递

__stdcall 函数体 从右到左,通过堆栈传递

__fastcall 函数体 从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈

thiscall 函数体 this指针默认通过ECX传递,其它参数从右到左入栈

note:

(1)__cdecl是C\C++的默认调用约定;

VC的调用约定中并没有thiscall这个关键字,它是类成员函数默认调用约定;

C\C++中的main(或wmain)函数的调用约定必须是__cdecl,不允许更改;

默认调用约定一般能够通过编译器设置进行更改,如果你的代码依赖于调用约定,请明确指出需要使用的调用约定;

(2)常见的函数调用约定中,只有cdecl约定需要调用者来清除堆栈;C\C++中的函数支持参数数目不定的参数列表,比如printf函数;由于函数体不知道调用者在堆栈中压入了多少参数,所以函数体不能方便的知道应该怎样清除堆栈,那么最好的办法就是把清除堆栈的责任交给调用者;这应该就是cdecl调用约定存在的原因吧;

---------------------------------------------------------

C编译在进行编译的时候也会进行名字的改编,当函数使用_stdcall(WINAPI)调用规则时,MS编译器就会改编函数的名称。

比如:__declspec(DLLexport) LONG __stdcall Proc(int a ,int b);

编译器会改编为__Proc@8

因此 当非C++或非C编译器调用该DLL中的函数Proc时,就会出现找不到函数的情况。

这样我们就可以定义DEF文件来解决,并且在DEF文件加上下面的EXPORTS:

EXPORTS

Proc

Def模块执行原理:当连接程序分析这个DEF文件时,它发现Proc和__Proc@8都被输出,由于这两个函数是相互匹配的,因此连接程序使用Proc来输出该函数,根本不使用__Proc@8来输出函数名

(3)下面是

调用习惯 VC++命名 C++Builder命名

---------------------------------------

__stdcall _MyFunction@4 MyFunction

__cdecl MyFunction _MyFunction

可以从网上搜索“在C++Builder里创建可以被Visual C++使用的DLL”以及“Using Visual C++

DLLs in a C++Builder Project”这两篇文章,看看不同编译器生成的dll之间是如何互相调用的。

第二部分 C/C++混合编程:

http://bbs.ednchina.com/BLOG_ARTICLE_479334.HTM

由于C++包含了C语言的特征,因此一个代码是C语言的还是C++的取决的是你要使用哪一个编译器去编译它,

是C编译器(gcc)还是C++编译器(g++).

而不取决于使用的语法和文件后缀。

由于上面的name

mangling机制,在C编译器和C++编译器中的不同,因此使用C的规则去找C++的函数是找不到的,反之亦然;因此需要明确的在代码中告诉编译器,下面的代码需要使用哪一种名字粉碎规则去匹配(在object文件中找函数名).

1. 在C++中调用C语言的函数

extern

"C"表示编译生成的内部符号名使用C约定。C++支持函数重载,而C不支持,两者的编译规则也不一样。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:void foo( int x, int y ); 该函数被C编译器编译后在符号库中的名字可能为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。下面以例子说明,如何在C++中使用C的函数,或者在C中使用C++的函数。

C++引用C函数的例子(C++调用C,extern "C" 的作用是:让C++连接器找调用函数的符号时采用C的方式

如)

//test.c

#include

void mytest()

{

printf("mytest in .c

file ok\n");

}

//main.cpp

extern "C"

{

void mytest(); //表示按照C语言的名字粉碎规则去调用这个函数

_mytest()

}

int main()

{

mytest();

return 0;

}

上述也可以加个头文件

//test.h

void mytest()

在后在main.cpp中extern

"C"

{

#include “test.h”

}

2. C

程序中调用C++函数和变量  在C中引用C++函数(C调用C++,使用extern "C"则是告诉编译器把cpp文件中extern "C"定义的函数依照C的方式来编译封装接口,当然接口函数里面的C++语法还是按C++方式编译)

在C中引用C++语言中的函数和变量时,C++的函数或变量要声明在extern "C"{}里,但是在C语言中不能使用extern "C",否则编译出错。(出现错误: error C2059: syntax error :

'string',这个错误在网上找了很久,国内网站没有搜到直接说明原因的,原因是extern "C"是C++中的关键词,不是C的,所有会出错。那怎么用?看本文,或者搜extern "C"!)

//test.cpp

#include

extern "C"

{

void mytest() //这个函数是在C++中定义的,因为要给C调用,因此需要告诉C++编译器使用C的名字粉碎规

则来生成这个函数名。可见,extern "C"的含义在两个地方是不同的。

{

printf("mytest in .cpp

file ok\n");

}

}

//main.c

void mytest();

int main()

{

mytest();

return 0;

}

3.综合调用(事先不知道谁调用谁)

一般我们都将函数声明放在头文件,当我们的函数有可能被C或C++使用时,我们无法确定被谁调用,使得不能确定是否要将函数声明在extern "C"里,所以,我们可以添加

#ifdef __cplusplus //用预编译选项来确定使用哪种编译器

extern "C"

{

#endif

//函数声明

#ifdef __cplusplus

}

#endif

这样的话这个文件无论是被C或C++调用都可以,不会出现上面的那个错误:error C2059: syntax error :

'string'。

如果我们注意到,很多头文件都有这样的用法,比如string.h,等等。

//test.h

#ifdef __cplusplus

#include

using namespace std;

extern "C"

{

#endif

void mytest();

#ifdef __cplusplus

}

#endif

这样,可以将mytest()的实现放在.c或者.cpp文件中,可以在.c或者.cpp文件中include "test.h"后使用头文件里面的函数,而不会出现编译错误。

//test.c

#include "test.h"

void mytest()

{

#ifdef __cplusplus

cout

<< "cout mytest extern ok "

<< endl;

#else

printf("printf mytest

extern ok n");

#endif

}

//main.cpp

#include "test.h"

int main()

{

mytest();

return 0;

}

综合使用的另外一种解决方案(使用extern关键字):

关于C++引用C函数和变量的例子还有一个

两个文件:

c文件:C.c

***********************************************

int external="5"; //全局变量,缺省为extern。int func() //全局函数,缺省为extern。{

return external;

}

***********************************************

cpp文件:CPP.cpp

***********************************************

#include "iostream"

using namespace std;

#ifdef __cplusplus

extern "C"

{

#endif

extern int external; //告诉编译器extern是在别的文件中定义的int,这里并不会为其分配存储空间。

extern int func(); //虽然这两个都是在extern

"C"的{}里,但是仍然要显式指定extern,否则报错。#ifdef __cplusplus //不仅仅是函数,变量也要放在extern

"C"中。}

#endif

void main(void)

{

cout<

file is:

"<

external=10;

cout<

"<

}

***********************************************

extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字.,它告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

1.对于extern变量来说,仅仅是一个变量的声明,其并不是在定义分配内存空间。如果该变量定义多次,会有连接错误

2.通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。也就是说c文件里面定义,如果该函数或者变量与开放给外面,则在h文件中用extern加以声明。所以外部文件只用include该h文件就可以了。而且编译阶段,外面是找不到该函数的,但是不报错。link阶段会从定义模块生成的目标代码中找到此函数。

3.与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。

总结:由于C++编译器和C编译器在name

mangling命名粉碎规则上的不同,因此C和C++互相调用其的函数和变量时,需要进行处理,否则会找不到。一种方法就是在C++文件中使用extern

”C“,这个做法对C不生效,只能在C++文件中使用。

电脑粉碎文件 c语言,C/C++ 编译器的命名粉碎规则(name mangling)和C/C++混合(ZZ)相关推荐

  1. 电脑粉碎文件 c语言,文件操作(二):文件粉碎机

    文件操作(2):文件粉碎机 文件粉碎机原理:文件被删除之前,用随机字符对其进行写操作 (注:参考的教材使用的是BCB编译器,BCB不会,所以这里改成使用MFC) 一. 首先建立一个基于对话框的MFC工 ...

  2. 粉碎机无法粉碎文件_宣布《粉碎杂志》

    粉碎机无法粉碎文件 Life at Smashing Magazine never stops throwing new surprises and challenges my way. When I ...

  3. 如何恢复错删了本机计算机器,如何彻底删除粉碎电脑文件?如何恢复电脑丢失文件?推荐这几款数据管理工具!...

    原标题:如何彻底删除粉碎电脑文件?如何恢复电脑丢失文件?推荐这几款数据管理工具! 日常生活中,我们常常会使用各种不同类型的文件粉碎工具来消除某些隐私数据,以期保护自身信息安全.由于粉碎机可以某些选定的 ...

  4. vs编译器 printf 控制台输出_【语言教程】通过语言了解GCC编译器工作过程

    通过c语言了解GCC编译器如何工作的 比特 下面是c语言初学必看的打印helloworld #include int main(){ printf("hello, world\n" ...

  5. 电脑服务器传文件夹吗,服务器和电脑传文件夹吗

    服务器和电脑传文件夹吗 内容精选 换一换 Model File:模型文件.单击右侧的文件夹图标,在后台服务器sample所在路径(工程目录/run/out/test_data/resnet-18/mo ...

  6. c语言写os 编译器,你真的懂''Hello world''吗?从编译器到OS内核系列:编译器基本概念...

    本文是<你真的理解'Hello world'吗?从编译链接到OS内核系列专题>的第一章的第一小节,主要介绍编译器的基本概念以及C语言程序的构建过程.全系列大纲如下: 前言 第1章 编译器的 ...

  7. c语言getchar在哪个头文件_c语言入门(一)

    知识点1[写代码的过程] 编辑器:程序员写代码的过程(记事本.vc6.0.vim)(让程序员看懂) 编译器:查看代码的语法错误,生成汇编语言. 汇编器:将生成好汇编语言 生成 二进制语言(目标文件) ...

  8. c语言的所有头文件,C语言所有头文件.doc

    C语言所有头文件 C语言所有头文件 stdlib.h包含了很多标准函数和标准宏定义,比如NULL, EXIT_SUCCESS等. ctype.h包含了很多类型定义 string.h包含了字符串操作相关 ...

  9. 电脑删除文件显示计算机管理员,为什么电脑文件夹删不掉?需要管理员权限删除如何解决...

    电脑文件夹无法删除怎么办?最近有用户发现电脑中有些文件夹出现无法删除的问题,该如何解决?下面就给大家介绍文件夹不能删除的几种常见解决方法. 操作步骤: 1,一般文件删除不掉,是因为注入了进程或者中了病 ...

最新文章

  1. 真全!GitHub上出现了一个353种语言资源的汇总
  2. oracle函数查询数据字典
  3. 部署LAMP-WordPress站点上线
  4. delphi Hi 和 High
  5. 清华姚班、斯坦福博士、普林斯顿NLP组创始人 陈丹琦 获小诺奖之称的斯隆奖!...
  6. [Windows]msvcr110.dll运行库缺失问题解决方案
  7. 用yolov5训练kitti数据集
  8. 云服务器维护安全管理制度,云服务器安全管理制度
  9. 从零起步做到Linux运维经理, 你必须管好的23个细节
  10. 遥感科普|中国高分系列卫星综述(2020版)
  11. 一文描绘未来DeFi资管平台:应具备平台透明化、资产Token化等6大特点
  12. 阿里为何不允许用Executors创建线程池?
  13. Visual Paradigm使用技能
  14. 【Linux】Linux进程的创建与管理
  15. 联想y7000-2019黑苹果安装笔记(自用)
  16. 3-7 pandas数据的读取与存储
  17. unity3d培训_003
  18. AutodeskInstallOnlineCheck_2.0.0.24.sfx检查提示
  19. 机理模型和非机理模型
  20. 计算机考研面试自我介绍范文英语,研究生复试英文自我介绍范文

热门文章

  1. 贝叶斯优化(Bayesian Optimization)只需要看这一篇就够了,算法到python实现
  2. Charles抓包APP
  3. 数据挖掘mooc国防科技大学笔记(自留)
  4. MOOC战德臣数据库课程自用笔记_9_范式
  5. dbNSFP:非同义突变功能注释数据库
  6. 卡尔曼滤波原理公式详细推导过程[包括引出]
  7. windows10关闭安全中心服务器,微软:Windows 安全中心问题阻止 Win10 版本 2004 更新...
  8. 微信小程序中的本地存储
  9. 【数据库 关系代数和SQL】经典查询总结
  10. 详解朴素贝叶斯分类算法