转载自:http://blog.csdn.net/weiwangchao_/article/details/7165467
先来总结一下Sam看这篇文章的要点:
1. 函数名称 不能完全标识 一个函数;因此我们用“函数签名(function signature)”来唯一标识一个函数
2. “函数签名”经过不同“编译器/链接器”的“名称修饰(name decoration)”得到不同的“修饰后名称(decorated name)”。由于不同编译器采用不同的名字修饰方法,必然会导致由不同编译器产生的目标文件无法正常链接,这是导致不同编译器之间不能互操作的主要原因之一。
3. 解析GCC的C++名称修饰——binutils里面的一个小工具c++filt可以解析被修饰过的名称:
$ c++filt _ZN1N1C4funcEi N::C::func(int)
4. 解析Visual C++的名称修饰——Microsoft提供一个API:UnDecorateSymbolName()
-----------------------------------------------------------------------------------------------------------------------------------
约在20世纪70年代以前,编译器编译源代码产生目标文件时,符号名与相应的变量和函数的名字是一样的。比如一个汇编源代码里面包含了一个函数 foo,那么汇编器将它编译成目标文件以后,foo在目标文件中的相对应的符号名也是foo。当后来UNIX平台和C语言发明时,已经存在了相当多的使用 汇编编写的库和目标文件。这样就产生了一个问题,那就是如果一个C程序要使用这些库的话,C语言中不可以使用这些库中定义的函数和变量的名字作为符号名, 否则将会跟现有的目标文件冲突。比如有个用汇编编写的库中定义了一个函数叫做main,那么我们在C语言里面就不可以再定义一个main函数或变量了。同 样的道理,如果一个C语言的目标文件要用到一个使用Fortran语言编写的目标文件,我们也必须防止它们的名称冲突。
为了防止类似的符号名冲突,UNIX下的C语言就规定,C语言源代码文件中的所有全局的变量和函数经过编译以后,相对应的符号名前加上下划 线"_"。而Fortran语言的源代码经过编译以后,所有的符号名前加上"_",后面也加上"_"。比如一个C语言函数"foo",那么它编译后的符号 名就是"_foo";如果是Fortran语言,就是"_foo_"。
这种简单而原始的方法的确能够暂时减少多种语言目标文件之间的符号冲突的概率,但还是没有从根本上解决符号冲突的问题。比如同一种语言编写的目标文 件还有可能会产生符号冲突,当程序很大时,不同的模块由多个部门(个人)开发,它们之间的命名规范如果不严格,则有可能导致冲突。于是像C++这样的后来 设计的语言开始考虑到了这个问题,增加了名称空间(Namespace)的方法来解决多模块的符号冲突问题。
但是随着时间的推移,很多操作系统和编译器被完全重写了好几遍,比如UNIX也分化成了很多种,整个环境发生了很大的变化,上面所提到的跟 Fortran和古老的汇编库的符号冲突问题已经不是那么明显了。在现在的Linux下的GCC编译器中,默认情况下已经去掉了在C语言符号前加"_"的 这种方式;但是Windows平台下的编译器还保持的这样的传统,比如Visual C++编译器就会在C语言符号前加"_",GCC在Windows平台下的版本(cygwin、mingw)也会加"_"。GCC编译器也可以通过参数选 项"-fleading-underscore"或"-fno-leading-underscore"来打开和关闭是否在C语言符号前加上下划线。
C++符号修饰
众所周知,强大而又复杂的C++拥有类、继承、虚机制、重载、名称空间等这些特性,它们使得符号管理更为复杂。最简单的例子,两个相同名字的函数 func(int)和func(double),尽管函数名相同,但是参数列表不同,这是C++里面函数重载的最简单的一种情况,那么编译器和链接器在链 接过程中如何区分这两个函数呢?为了支持C++这些复杂的特性,人们发明了符号修饰(Name Decoration)或符号改编(Name Mangling)的机制,下面我们来看看C++的符号修饰机制。
首先出现的一个问题是C++允许多个不同参数类型的函数拥有一样的名字,就是所谓的函数重载;另外C++还在语言级别支持名称空间,即允许在不同的名称空间有多个同样名字的符号。比如清单3-4这段代码:
清单3-4 C++ 函数的名称修饰
部结构我们在这里先不展开了,在下一章分析静态链接过程的时候,我们还会详细地分析重定位表的结构。
- int func(int);
- float func(float);
- class C {
- int func(int);
- class C2 {
- int func(int);
- };
- };
- namespace N {
- int func(int);
- class C {
- int func(int);
- };
- }
这段代码中有6个同名函数叫func,只不过它们的返回类型和参数及所在的名称空间不同。我们引入一个术语叫做函数签名(Function Signature),函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息。函数签名用于识别不同的函数,就像签 名用于识别不同的人一样,函数的名字只是函数签名的一部分。由于上面6个同名函数的参数类型及所处的类和名称空间不同,我们可以认为它们的函数签名不同。 在编译器及链接器处理符号时,它们使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称(Decorated Name)。编译器在将C++源代码编译成目标文件时,会将函数和变量的名字进行修饰,形成符号名,也就是说,C++的源代码编译后的目标文件中所使用的 符号名是相应的函数和变量的修饰后名称。C++编译器和链接器都使用符号来识别和处理函数和变量,所以对于不同函数签名的函数,即使函数名相同,编译器和 链接器都认为它们是不同的函数。上面的6个函数签名在GCC编译器下,相对应的修饰后名称如表3-18所示。
表3-18
函数签名 |
修饰后名称(符号名) |
int func(int) |
_Z4funci |
float func(float) |
_Z4funcf |
int C::func(int) |
_ZN1C4funcEi |
int C::C2::func(int) |
_ZN1C2C24funcEi |
int N::func(int) |
_ZN1N4funcEi |
int N::C::func(int) |
_ZN1N1C4funcEi |
GCC的基本C++名称修饰方法如下:所有的符号都以"_Z"开头,对于嵌套的名字(在名称空间或在类里面的),后面紧跟"N",然后是各个名称空 间和类的名字,每个名字前是名字字符串长度,再以"E"结尾。比如N::C::func经过名称修饰以后就是_ZN1N1C4funcE。对于一个函数来 说,它的参数列表紧跟在"E"后面,对于int类型来说,就是字母"i"。所以整个N::C::func(int)函数签名经过修饰为 _ZN1N1C4funcEi。更为具体的修饰方法我们在这里不详细介绍,有兴趣的读者可以参考GCC的名称修饰标准。幸好这种名称修饰方法我们平时程序 开发中也很少手工分析名称修饰问题,所以无须很详细地了解这个过程。binutils里面提供了一个叫"c++filt"的工具可以用来解析被修饰过的名 称,比如:
- $ c++filt _ZN1N1C4funcEi
- N::C::func(int)
签名和名称修饰机制不光被使用到函数上,C++中的全局变量和静态变量也有同样的机制。对于全局变量来说,它跟函数一样都是一个全局可见的名称,它也遵循 上面的名称修饰机制,比如一个名称空间foo中的全局变量bar,它修饰后的名字为:_ZN3foo3barE。值得注意的是,变量的类型并没有被加入到 修饰后名称中,所以不论这个变量是整形还是浮点型甚至是一个全局对象,它的名称都是一样的。
名称修饰机制也被用来防止静态变量的名字冲突。比如main()函数里面有一个静态变量叫foo,而func()函数里面也有一个静态变量叫 foo。为了区分这两个变量,GCC会将它们的符号名分别修饰成两个不同的名字_ZZ4mainE3foo和_ZZ4funcvE3foo,这样就区分了 这两个变量。
不同的编译器厂商的名称修饰方法可能不同,所以不同的编译器对于同一个函数签名可能对应不同的修饰后名称。比如上面的函数签名中在Visual C++编译器下,它们的修饰后名称如表3-19所示。
表3-19
函数签名 |
修饰后名称 |
int func(int) |
?func@@YAHH@Z |
float func(float) |
?func@@YAMM@Z |
int C::func(int) |
?func@C@@AAEHH@Z |
int C::C2::func(int) |
?func@C2@C@@AAEHH@Z |
int N::func(int) |
?func@N@@YAHH@Z |
int N::C::func(int) |
?func@C@N@@AAEHH@Z |
我们以int N::C::func(int)这个函数签名来猜测Visual C++的名称修饰规则(当然,你只须大概了解这个修饰规则就可以了)。修饰后名字由"?"开头,接着是函数名由"@"符号结尾的函数名;后面跟着由"@" 结尾的类名"C"和名称空间"N",再一个"@"表示函数的名称空间结束;第一个"A"表示函数调用类型为"__cdecl"(函数调用类型我们将在第4 章详细介绍),接着是函数的参数类型及返回值,由"@"结束,最后由"Z"结尾。可以看到函数名、参数的类型和名称空间都被加入了修饰后名称,这样编译器 和链接器就可以区别同名但不同参数类型或名字空间的函数,而不会导致link的时候函数多重定义。
Visual C++的名称修饰规则并没有对外公开,当然,一般情况下我们也无须了解这套规则,但是有时候可能须要将一个修饰后名字转换成函数签名,比如在链接、调试程 序的时候可能会用到。Microsoft提供了一个UnDecorateSymbolName()的API,可以将修饰后名称转换成函数签名。下面这段代 码使用UnDecorateSymbolName()将修饰后名称转换成函数签名:
- /* 2-4.c
- * Compile: cl 2-4.c /link Dbghelp.lib
- * Usage: 2-4.exe DecroatedName
- */
- #include <Windows.h>
- #include <Dbghelp.h>
- int main( int argc, char* argv[] )
- {
- char buffer[256];
- if(argc == 2)
- {
- UnDecorateSymbolName( argv[1], buffer, 256, 0 );
- printf( buffer );
- }
- else
- {
- printf( "Usage: 2-4.exe DecroatedName\n" );
- }
- return 0;
- }
由于不同的编译器采用不同的名字修饰方法,必然会导致由不同编译器编译产生的目标文件无法正常相互链接,这是导致不同编译器之间不能互操作的主要原因之一。我们后面的关于C++ ABI和COM的这一节将会详细讨论这个问题。
相关推荐
C_C++函数符号生成规则(函数名的修饰);C++ 函数重载.pdf 本文主要介绍了C和C++函数符号生成规则,以及C++函数重载机制。 一、C函数符号生成规则 在C语言中,函数符号生成规则主要有三种:cdecl调用约定、std ...
`java.security.KeyPairGenerator` 用于生成密钥对,`java.security.MessageDigest` 用于生成摘要,`java.security.Signature` 类则提供了数字签名的加密和解密功能。 此外,这个项目可能还涉及到证书和证书颁发...
在TensorFlow 2.0中,函数签名(function signature)和图结构是两个重要的概念,它们对于理解和操作TensorFlow计算图至关重要。函数签名定义了函数的输入和输出类型的规范,而图结构则描述了计算图的拓扑,即计算...
- `$function_name`:这是一个必需的字符串参数,表示要检查的函数名称。 - **返回值**: - 如果指定的函数已经被定义,则返回 `true`。 - 如果指定的函数没有被定义,则返回 `false`。 #### 重要特性与应用...
其中,`function_name` 是函数的名称,`argu1` 是函数的参数,`datatype1` 是参数的数据类型,`RETURN datatype` 是函数的返回值数据类型。 函数示例 1. 不带参数的函数 ```sql CREATE OR REPLACE FUNCTION get_...
例如,通过`@tf.function(input_signature=[tf.TensorSpec([None], tf.int32, name='x')])`定义的`cube`函数,只接受形状为`(None,)`且数据类型为`tf.int32`的张量。当尝试传递不匹配的数据类型或形状时,如浮点数...
C++调用C函数实例详解 前言:以前见到extern “C”这样的语句,只是简单地知道跟外部链接有关,但是没有深刻理解它的意思。 首先,为什么要使用extern “C”修饰符? C++调用其它语言的函数,由于编译器生成函数的...
本主题将详细介绍如何为无LIB的DLL制作LIB函数符号输入库,以便于程序的编译和链接。 首先,我们需要理解LIB文件的作用。LIB文件是静态库,它是编译器在链接阶段使用的,其中包含了对DLL中导出函数的引用信息。当...
`function-class`是包含函数的类全名,`function-signature`是函数的方法签名。 为了让EL引擎能够识别这个自定义函数,我们需要在web.xml中注册我们的TLD文件: ```xml ... <taglib-uri>...
标题中的"signature"一词可能指的是程序或软件的签名,这在IT领域中是一个重要的概念。在编程和软件开发中,签名通常用于标识代码的作者、验证代码的完整性和安全性,或者是在函数或方法中定义输入参数和返回类型。...
`call_function_by_name`函数接受一个字符串参数,查找映射表,如果找到则调用对应的函数,否则输出错误信息。 然而,这种方法有一些限制,比如无法调用成员函数或重载函数,也不能处理不同参数列表的函数。为了...
MySQL 存储过程与函数 MySQL 存储过程和函数是数据库中定义的一组用户定义的 SQL 语句集合。它们之间的区别在于: 1. 存储过程实现的功能要复杂一点,而函数实现的功能针对性比较强。 2. 存储过程可以返回参数,而...
<function-signature>java.lang.Integer addNumbers(java.lang.Integer, java.lang.Integer)</function-signature> </function> ``` 在对应的`MathUtils`类中,你需要实现`FunctionMapper`接口: ```java public ...
在MATLAB中,S函数(System Function)是一种自定义模型,可以用于构建复杂的Simulink模块,包括实现PID控制器。 **S函数简介** S函数是MATLAB Simulink环境中的一种高级工具,它允许用户用C、C++或MATLAB语言编写...
该项目旨在作为资源,用于将EVM使用的4字节函数选择器映射回将生成这些4字节选择器的已知规范函数签名列表。 该项目当前包含两个主要组件: 当前网站位于 。 存放签名数据的以太坊智能合约。 (此部分正在进行中...
CREATE FUNCTION author_book(b_name CHAR(20)) RETURNS CHAR(8) BEGIN RETURN (SELECT 作者 FROM Book WHERE 书名= b_name); END$$ DELIMITER ; 调用此存储函数: SELECT author_book(‘计算机应用基础’); 存储...
修饰符是 printf 函数中的一种特殊格式符,它可以对转换说明符进行修饰,改变其输出结果。常见的修饰符包括: * digit(s):字段宽度的最小值 * .digit(s):精度 * -:左对齐 * +:带符号的值 * (空格):有符号的值 ...
### 知识点:匿名函数在JavaScript中的应用与特性 #### 标题解析:“匿名函数的一点知识” 标题“匿名函数的一点知识”简洁明了地指出本文将围绕JavaScript中的匿名函数进行讲解,旨在帮助读者深入理解匿名函数的...
MATLAB符号运算引擎MuPAD函数-MuPAD Functions.rar 本帖最后由 zdl0320 于 2012-11-7 19:48 编辑 刚找到的MuPAD Function,自己简单处理了一下,点击可以找到具体应用实例。
* function_name:要创建的自定义函数名称 * @parameter_name:参数名称 * scalar_data_type:返回值类型 * WITH:附加选项,如果需要对函数体进行加密,可以使用 WITH ENCRYPTION;如果需要将创建的函数与引用的...