编程中已经确认大小的js 对象数组组未进行实例化占用的内存情况

关于Java反射机制的浅析
Person p = new Person();
这是实例化一个对象,可是这种实例化对象的方法存在一个问题,那就是必须要知道类名才可以实例化它的对象,这样我们在应用方面就会受到限制。
就提供了这样的机制
1.java程序在运行时可以获得任何一个类的字节码信息,包括类的修饰符(public、static等)、基类、实现的接口、字段和方法等信息。
2.java程序在运行时可以根据字节码信息来创建该类的实例对象,改变对象的字段内容和调用对象方法。
这样的机制就叫反射机制。可以想象光学中的反射。反射技术提供了一种通用的动态链接程序的方法,不必要把程序所需要的目标类硬编码到源程序中,从而使得我们可以创建灵活的程序。
Java的反射机制是通过反射API来实现的,它允许程序在运行过程中取得任何一个已知名称的类的内部信息。反射API位于java.lang.reflect包中。主要包括以下几类:
1.Constructor类:用来描述一个类的构造方法
2.Field类:用来描述一个类的成员变量。
3.Method类:用来描述一个类的方法。
4.Modifer类:用来描述类内各元素的修饰符
5.Array: 用来对数组进行操作。
Constructor,Field,Method这三个类都是JVM在程序运行时创建的,用来表示加载类中相应的成员。这三个类都实现了java.lang.reflect.Member接口,Member接口定义了获取类成员或构造方法等信息的方法。要使用这些反射API,必须先得到要操作的对象或类的Class类的实例。通过调用Class类的newInstance方法(只能调用类的默认构造方法)可以创建类的实例。这样有局限性,我们可以先从类的Class实例获取类需要的构造方法,然后再利用反射来创建类的一个实例。
一、获取类的构造方法的Constructor对象(数组)
Constructor[] getDeclaredConstructors():返回已加载类声明的所有的构造方法的Constructor对象数组。 Constructor getDeclaredConstructor(Class[] param Types):返回已加载类声明的指定构造方法的Constructor对象,paramTypes指定了参数类型。 Constructor[] getConstructors():返回已加载类声明的所有public类型的构造方法的Constructor对象数组。 Constructor getConstructor(Class[] paramTypes):返回已加载类声明的指定的public类型的构造方法的Constructor对象,paramTypes指定了参数类型。
如果某个类中没有定义构造方法,第一个和第三个方法返回的数组中只有一个元素,就是缺省的构造方法;如果某个类中只定义了有参数的构造函数,而没有定义缺省构造函数,第一个和第三个方法返回的数组中不包含缺省的构造方法。
import java.lang.reflect*;
public class DumpMethods{
public static void main(String[] args){
if(args.length&1){
System.out.println(&请输入完整的类名:&);
Class strClass = Class.forName(args[0]);
//检索带有指定参数的构造方法
Class[] strArgsClass = new Class[]{byte[].class, String.class};
Constructor
constructor = strClass.getConstructor(strArgsClass);
System.out.println(&Constructor:&+constructor.toString());
//调用带有参数的构造方法创建实例对象object
byte[] bytes = &java就业&.getBytes();
Object[] strArgs = new Object[]{bytes,&gb2312&};
Object object = constructor.newInstance(strArgs);
System.out.println(&Object&+object.toString());
}catch(Exception e){
e.printStackTrace();
二、获取类成员变量的Field对象(数组)
Field[] getDeclaredFields():返回已加载类声明的所有成员变量的Field对象数组,不包括从父类继承的成员变量。 Field getDeclaredField(String name):返回已加载类声明的所有成员变量的Field对象,不包括从父类继承的成员变量,参数name指定成员变量的名称。 Field[] getFields():返回已加载类声明的所有public型的成员变量的Field对象数组,包括从父类继承的成员变量。
Field getField(String name):返回已加载类声明的所有成员变量的Field对象,包括从父类继承的成员变量,参数name指定成员变量的名称。
import java.lang.reflect.*
public class ReflectTest{
public Reflect Test(String name, String age){
this.name =
this.age =
public static void main(String[] args){
//TODO自动生成方法存根
ReflectTest rt = new ReflectTest(&zhanghandong&,&shiba&);
}catch(Exception e){
e.printStackTrace();
public static void fun(Object obj) throws Exception{
Field[] fields = obj.getClass().getDeclaredFields();
System.out.println(&替换之前的:&);
for(Field field:fields){
System.out.println(field.getName()+&=&+field.get(obj));
if(field.getType().equals(java.lang.String.class)){
field.setAccessible(true);//必须设置为true才可以修改成员变量
String org=(String)field.get(obj);
field.set(obj, org.replaceAll(&a&,&b&));
System.out.println(&替换之后的:&);
for(Field field:fields)
System.out.println(field.getName()+&=&+field.get(obj));
三、获取类的方法的Method对象(数组)
Method[] getDeclaredMethods():返回已加载类声明的所有方法的Method对象数组,不包括从父类继承的方法。 Method getDeclaredMethod(String name, Class[] paramTypes):返回已加载类声明的所有方法的Method对象,不包括从父类继承的方法,参数name指定方法的名称,参数paramTypes指定方法的参数类型。 Method[] getMethods():返回已加载类声明的所有方法的Method对象数组,包括从父类继承的方法。 Method getMethod(String name, Class[] paramTypes):返回已加载类声明的所有方法的Method对象,包括从父类继承的方法,参数name指定方法的名称,参数paramTypes指定方法的参数类型。
四、检索类的其他信息
- int getModifiers():返回已加载类的修饰符的整形标识值。
- Package getPackage():返回已加载类的包名。
- Class getSuperclass():返回已加载类的父类的Class实例。
- Class[] getInterfaces():返回已加载类实现的接口的Class对象数组。
- boolean isInterface():返回已加载类是否是接口。
反射的功能很强大,但是使用不当可能会缺点大于优点,反射使代码逻辑混乱,会带来维护的问题。
Java反射机制原理
运行时类型识别(Run-time Type Identification, RTTI)主要有两种方式,一种是我们在编译时和运行时已经知道了所有的类型,另外一种是功能强大的&反射&机制。
要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表达的,这项工作是由&Class对象&完成的,它包含了与类有关的信息。类是程序的重要组成部分,每个类都有一个Class对象,每当编写并编译了一个新类就会产生一个Class对象,它被保存在一个同名的.class文件中。在运行时,当我们想生成这个类的对象时,运行这个程序时,Java会确认这个类的Class对象是否已经加载,如果尚未加载,JVM就会根据类名查找.class文件,并将其载入,一旦这个类的Class对象被载入内存,它就被用来创建这个类的所有对象。一般的RTTI形式包括三种:
传统的类型转换。如&(Apple)Fruit&,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常。 通过Class对象来获取对象的类型。如:Class c = Class.forName(&Apple&); Object o = c.newInstance(); 通过关键字instanceof或Class.isInstance()方法来确定对象是否属于某个特定类型的实例,准确的说,应该是instanceof/Class.isInstance()可以用来确定对象是否属于某个类及其所有基类的实例,这和equals()/==不一样,它们用来比较两个对象是否属于同一个类的实例,没有考虑继承关系。
如果不知道某个对象的类型,可以通过RTTI来获取,但前提是这个类型在编译时必须已知,这样才能使用RTTI来识别。即在编译时,编译器必须知道所有通过RTTI来处理的类。
使用反射机制可以不受这个限制,它主要应用于两种情况,第一个是&基于构件的&,在这种编程方式中,将使用某种基于快速应用开发(RAD)的应用构建工具来构建项目。这是现在最常见的可视化编程方法,通过代表不同组件的图标拖动到图板上来创建程序,然后设置构件的属性值来配置它们。这种配置要求构件都是可实例化的,并且要暴露其部分信息,使得程序员可以读取和设置构件的值。当处理GUI时间的构件时还必须暴露相关方法的细节,以便RAD环境帮助程序员覆盖这些处理事件的方法。在这里,就要用到反射的机制来检查可用的方法并返回方法名。Java通过JavaBeans提供了基于构件的编程架构。
第二种情况,在运行时获取类的信息的另外一个动机,就是希望能够提供在跨网络的远程平台上创建和运行对象的能力。这被称为远程调用(RMI),它允许一个Java程序将对象分布在多台机器上,这种分布能力将帮助开发人员执行一些需要进行大量计算的任务,充分利用计算机资源,提高允许速度。
Class支持反射,java.lang.reflect中包含了Field/Method/Constructor类,每个类都实现了Member接口。这些类型的对象都是由JVM在运行时创建的,用来表示未知类里对应的成员。如:可以用Constructor类创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。同时,还可以调用getFields()、getMethods()、getConstructors()等方法来返回表示字段、方法以及构造器的对象数组。这样,未知的对象的类信息在运行时就能被完全确定下来,而在编译时不需要知道任何信息。
另外,RTTI有时能解决效率问题。当程序中使用多态给程序的运行带来负担的时候,可以使用RTTI编写一段代码来提高效率。C#里面将对象实例赋值为null会否加速GC释放资源? - 知乎有问题,上知乎。知乎作为中文互联网最大的知识分享平台,以「知识连接一切」为愿景,致力于构建一个人人都可以便捷接入的知识分享网络,让人们便捷地与世界分享知识、经验和见解,发现更大的世界。82被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答2125 条评论分享收藏感谢收起5添加评论分享收藏感谢收起12:33 提问
抽象类不可以实例化,为什么可以创建数组对象啊
抽象类不可以实例化,为什么可以创建数组对象,标注长度,然后向上造型呢
按赞数排序
这里的创建数组对象只是申明吧,并没有实例化。
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!
其他相关推荐解析VC++ Name Mangling 机制
在C++中,Name Mangling 是为了支持重载而加入的一项技术,目前C++ Name Mangling 并没有统一的标准,也没有较完整的中文文档化资料,所以本篇文章在VS2005环境中,解析C++ Name Mangling 的技术细节,以及怎样将VC Name Mangling后的名称还原为可读的形式。
Name Mangling 简介
VC环境中Name Mangling
VC环境中C 语言的 Name Mangling
VC环境中C++ 语言中的 Name Mangling
将Name Mangling 后的名称还原为可读的形式
Name Mangling 简介
Name Mangling 是一种在编译过程中,将函数、变量的名称重新改编的机制。在 C++重载、namespace等操作符下,函数可以有同样的名字,编译器为了区分各个不同地方的函数,将各个函数通过编译器内定的算法,将函数改成唯一的名称。
Name Mangling翻译成中文意思为:名字修饰、名字改编等,由于对这个翻译没有统一的约定,所以本文中采用英文表示。
在写VC++程序时,我们有时会遇到类似于“error LNK2019: unresolved external symbol "void __cdecl MyFun (void)" (?MyFun@@YAXXZ) referenced in function _wmain”的连接错误,此语句中“?MyFun@@YAXXZ”是VC Name Mangling后的结果。本文主要讨论Name Mangling 后名称还原为可读的方式。
本文首先讨论 VC 环境中,C/C++ 语言的 Name Mangling 算法机制。然后讨论手动将 C++ 语言 Name Mangling 后的字符串转换为函数的定义式,最后编码实现还原。
VC环境中Name Mangling
在VC中,微软采用了自己独特的Name Mangling技术,当然微软也为此Name Mangling技术申请了专利。
想要查看 VC 将函数名称 Name Mangling 的结果,只需将函数声明,不实现,然后调用之即可。
下面先讨论VC环境中C语言的Name Mangling技术,然后再讨论C++。
VC环境中C 语言的 Name Mangling
在VC中,也可以采用C语言编译器,只需要如下设置:Project à Property…à Configuration Properties à C/C++ à Advanced à Compile As,将其设为“(/TC)”即可。至于文件后缀是否为cpp,VC并不关心。
在 C 语言中,函数可以有如下声明方式(其中 __CALLTYPE 可以为 __cdecl、__stdcall、__fastcall等)[1]:
1、void __CALLTYPE fun();
2、int __CALLTYPE fun();
3、int __CALLTYPE fun(int);
4、double __CALLTYPE fun(int, double);
5、int* __CALLTYPE fun(int*, char*);
我们先假设此处 CALLTYPE 为 __cdecl(即:#define __CALLTYPE __cdecl),在 VC 中,Name Mangling 结果如下:
由此可见,在 VC 编译 C 语言时(__cdecl),仅仅在函数名前加“_”。
我们再看看其他调用方式,如:__stdcall(#define __CALLTYPE __stdcall),结果如下:
4、_fun@12
最后,我们看看 __fastcall 的结果(#define __CALLTYPE __ fastcall):
4、@fun@12
由此,我们可以得出结论,从被 Name Mangling 后的字符串推断出原来的函数名。
1、__cdecl
在此声明方式下,仅仅在函数名前加一个下划线,至于函数返回值、参数,完全没有处理。
2、__stdcall
在此声明方式下,在函数名前加一个下划线,然后紧跟“@”符号,最后是函数参数大小总和(注意:此总和包含了字节填充)。
3、__fastcall
在此声明方式下,跟 __stdcall 唯一不同的是,函数前面的下划线变为了“@”符号。
由上面 5 个实例函数,我们大概可以看到 VC 环境中,C 语言 Name Mangling 技术了,但也可以发现,从 Name Mangling 后的字符串,并不能得出函数原来的定义式。不同的定义式,Name Mangling 后的名称可以相同,由此也可以知道,C 语言不支持函数重载。
Name mangling或者Decorated Name是指程序设计语言中具有存储性质的对象的名字被编译器改写,以适合编译器、链接器(linker)、汇编器(assembler)使用。
所谓的具有存储性质的对象,即lvalue对象,是指要实际占用内存空间、有内存地址的那些实体对象,例如:变量(variables)、函数、函数指针 等。C++中的纯虚函数作为特例也属于这一范畴。而数据类型(data type)就不属于具有存储性质的对象。Name mangling如何翻译成中文,尚无广泛接受的译法。可翻译作改名或名字修饰(name decorating)。
对于支持重载(overload)的程序设计语言,name mangling是必需的因而具有特别重要意义。C++允许函数重载
int foo(int i);
void foo(char c);
C++也允许变量名称在不同限定(qualifier)下同名:
std::cout;
因此C++的编译器必须给这些函数及变量不同的名字以供程序编译链接、载入时的内部辨别使用。
C++的编译器可大略分为Windows平台上的Microsoft Visual C++与类Linux平台上的GNU GCC/g++两大类,分别成了各自操作系统环境下的业界工业标准。例如,Windows平台上的Intel C++ Compiler(ICC)与Digital Mars C++,都与Visual C++保持了二进制兼容(Application Binary Interface, ABI)。而Linux平台上的Intel C++ Compiler(ICC)与HP aC++,都与GCC 3.x/4.x做到二进制兼容。GCC是开源产品,它的内部实现机制是公开的;而Visual
C++不公开它内部实现细节,因此在name mangling上并无详尽的正式文档,Visual C++ name mangling的细节属于hacker行为。
一般情况下,编程者不需要知道C/C++函数的修饰名字。但是,如果在或者中引用了C/C++函数,就必须使用其正确的修饰名字。
并 不支持重载,因此C程序中禁止函数与函数同名,也就没有必要做name mangling。但C语言的函数调用协议(calling conventions)五花八门,用某一种调用协议编译的静态库或动态库的函数,如果用另外的调用协议去调用将会导致错误甚至系统崩溃。因此C语言编译 器对函数的名字做少量的修饰,用于区别该函数支持哪种调用协议,这可以给编译、链接、特别是库函数的载入提供额外的检查信息。Microsoft
C编译器在八十年代最先引入这种mangling模式,随后各家编译器如Digital Mars, Borland, GNU gcc等纷纷效仿。
目前,C语言常用的调用协议有三种:cdecl, stdcall与fastcall,其它众多调用协议如__pascal, __fortran, __syscall, __far等基本上算是过时了(obsolote),因此无需考虑。cdecl的函数被改名为_name;stdcall的函数被改名为_name@X;fastcall的函数被改名为@name@X。其中X是函数形参所占用的字节长度(包括那些用寄存器传递的参数,
如fastcall协议). 例如:
int __cdecl foo(int i); // mangled name is _
int __stdcall bar(int j); // mangled name is _bar@4
int __fastcall qux(int i) ; // mangled name is @qux@4
注意在64位Windows平台上,由于ABI有正式标准可依,只存在一种子程序调用协议,所以该平台上的C语言的函数不在名字前加上下划线 (leading underscore). 这会导致一些老的程序(legacy program), 例如使用'alias'去链接C语言的函数的Fortran程序不能正常工作。
C++语言由于含有大量复杂的语言特性,如classes, templates, namespaces, operator overloading等,这使得对象的名字在不同使用上下文中具有不同的意义。所以C++的name mangling非常复杂。
这些名字被mangled的对象,实际上都是具有全局属性的存储对象(storage object),其名字将绑定到所占用内存空间的地址,都有lvalue。存储对象具体可分为数据变量(data variable)与函数(function)。为什么不考虑函数的非静态局部变量、类的非静态数据成员呢?因为编译器把函数的非静态局部变量翻译为[sp]+固定的偏移量;把类的非静态数据成员翻译为this+固定的偏移量。
下文使用了来表述一些name mangling的语法定义。方括号[]表示该项出现0次或1次,除非在方括号后用上下角标给出该项出现的上下限。下文使用类,一般包含了class、struct、union等复合数据类型。
&C++的name mangling& ::= ?&Qualified Name& &Type Information&
&Qualified Name& ::= &name&@ [&namespace&@]∞0 @
C++中被mangled的名字都使用问号(?)开始,因为这与用字母数字(alphanumeric)、下划线(_)或@开头的C语言程序中的被mangled的名字能完全区分开。
C++中的变量与函数,可定义于名字空间或类中。所以变量与函数受到名字空间或类的限定(qualification)。而名字空间、类又可以嵌套(nest)。
&Qualified Name&表示变量与函数的名字及所定义的名字空间(或类)的嵌套情况。并采用与C++程序中作用域嵌套相反的顺序编码。例如,namespace1::nestedClass::something编码为something@nestedClass@namespace1@@。
Name mangling时,名字的字符串用@符号作为结束标志。例如&name&@,表示&name&这个字符串以@符号作为结束标志。因为名字的长度不是事先确定的。如果一个词法单元的长度是确定的,这些词法单元就不用@作为结尾标志,例如下文中&CV Modifier&只需用单个字母表示,则无需额外的结束标志。
&Type Information&是变量与函数的类型信息的编码表示。对于数据对象,就是它的数据类型,见;对于函数,类型信息就是它的返回值类型、参数类型列表、调用协议等情况,见。
这里所说的数据对象,包括全局变量(global variables)、类的静态数据成员变量(static member variables of classes)。
&数据对象的name mangling& ::= ?&name&@[&namespace&@]∞0@&data access modifier&&data type&&CV Modifier&
&data access modifier&用于表示数据对象的类别,其编码为:
Private static member
Protected static member
Public static member
global variable
static local variable
&CV Modifier&是数据对象的访问属性的编码表示,一般常用的值有:A表示default对象、B表示const对象、C表示volatile对象、D表示const volatile对象。详见。需要注意的是对于指针、数组、引用类型的对象,&CV
Modifier&是对所指向的基类型的内存空间的访问属性。
int alpha;
// mangled as ?alpha@@3HA 其中3表示全局变量,H表示整形,A表示非const非volatile
char beta[6] = "Hello"; // mangled as ?beta@@3PADA
//其中P表示为指针(或一维数组)且指针自身为非const非volatile,
//D表示char类型,两次出现的A都表示基类型为非const非volatile
class myC{
static int s_v;
// mangled as ?s_v@myC@@0HA 其中0表示私有静态数据成员
函数需要分配内存空间以容纳函数的代码,函数的名字实际上都是lvalue,即指向一块可执行内存空间的起始地址。而函数模板的实例化 (function template instantiation),也是lvalue,需要分配内存空间存储实例化后的代码,其name mangling在中详述。
&全局函数的name mangling& ::= ?&function name&@ [&namespace&@]∞0 @ &func modifier&
&calling conv& [&storage ret&] &return type& &parameter type&∞1 &throw type&
&成员函数的name mangling& ::= ?&function name&@ [&namespace&@]∞0 @ &func modifier& [&const modifier&]
&calling conv& [&storage ret&] &return type& &parameter type&∞1 &throw type&
其中,&func modifier&给出了函数是near或far、是否为静态函数、是类成员函数还是全局函数、是否为虚函数、类成员函数的访问级别等基本信息。需要注意,far属性仅适用于Windows 16位环境,32位或64位环境下使用扁平(flat)内存地址模型,函数只能具有near属性。
类成员函数的&const modifier&是指是否为只读成员函数(constant member function). 如果不是const,则编码为A;如果是const,则编码为B;如果是类的静态成员函数,则省略该项,因为静态成员函数没有this指针,无法修改类对象的数据。
&calling conv&是指函数的调用协议。详见。常见的调用协议的编码为:__cdecl是A, __pascal是C, __thiscall是E,
__stdcall是G, __fastcall是I。在Windows 64位环境中唯一允许的调用协议的编码是A。
[&storage ret&]是指函数的返回值的是否有const或volatile属性:
&storage ret& ::= ?&CV Modifier&
如果函数的返回值不具有const或volatile性质,那么该项在编码中被省略;但是如果函数的返回类型是class、struct或union等复合数据类型,此项在编码中是必需的,不能省略。
&return type&是函数的返回值的数据类型,详见。
&parameter type&是函数的形参列表(parameter list)的数据类型的编码。按照形参从左到右顺序给每个参数的数据类型编码,详见。参数类型列表的编码为:
X (即函数没有参数,或者说参数为void,该编码也是列表的结束标志) type1 type2 ... typeN @ (正常N个形参. 以@作为列表的结束标志) type1 type2 ... Z (形参表最后一项为...,即ellipsis,其编码Z也标志着列表的结束)&throw type&是函数抛出异常的说明,即异常规范(exception specification)。截至Visual C++ 2010,仍是接受但没有实现异常规范。因此这一项编码仍然保持为字符Z。
void Function1 (int a, int * b); /*mangled as ?Function1@@YAXHPAH@Z
其中 Y: 全局函数
A: cdecl调用协议
X: 返回类型为void
H: 第一个形参类型为int
PAH:第二个形参类型为整形指针
@: 形参表结束标志
Z: 缺省的异常规范 */
int Class1::MemberFunction(int a, int * b); /* mangled in 32-bit mode as
?MemberFunction@Class1@@QAEHHPAH@Z
其中 Q: 类的public function
A: 成员函数不是const member function
E: thiscall调用协议
H: 返回值为整形
H: 第一个形参类型为整形
PAH: 第二个形参为整形指针 */
C++程序中,需要考虑的具有全局存储属性的变量名字及函数的名字。这些名字受namespace、class等作用域(scope)的限定。因此,带完整限定信息的名字定义为:
&Qualified Name& ::= &Basic Name& [&Qualifier&]∞0 @
&Basic Name& ::= &Name Fragment& | &Special Name&
&Qualifier& ::= &namespace& | &class name& | &Template Instantiation&
| &Numbered Namespace& | &Back Reference& | &Nested Name&
其中,&Name Fragment&是组成名字标识符的ASCII码串,规定必须以@作为结尾后缀。&Special Name&是指构造函数、析构函数、运算符函数(operator function)、虚表(vtable)等内部数据结构等,详见。
&namespace&与&class name&就是指C++程序中的名字空间与类。&Template Instantiation&是指实例化后的函数模板或类模板,详见。&Numbered
Namespace&是对一个函数内部用花括号{ ... }给出的不同的作用域(scope)的编号表示,详见。&Back Reference&是对一个mangled
name的ASCII码串中重复出现的类型或名字的简写表示方法,详见。&Nested
Name&是对静态局部变量所在的函数名的表示方法,详见。
Visual C++的name mangling,有时会用到数(number),例如多维数组的维数等。数的编码使用一套独特的方法:
1-10 编码为 0-9,这节省了最常用的数的编码长度; 大于10的数编码为十六进制,原来的十六进制数字0-F用A-P代替,不使用前缀0或0x,使用后缀@作为结束标志; 0编码为A@; 负数编码为前缀?后跟相应的(absolute value)的编码。例如,8编码为7。29110编码为BCD@。-1510编码为?P@。
特殊名字(special names)是指类的构造函数、析构函数、运算符函数、类的内部数据结构等的名字,表示为前缀?后跟编码。已知的编码:
Constructor
operator /=
Destructor
operator %=
operator new
operator &&=
operator delete
operator &&=
operator =
operator &=
operator &&
operator |=
operator &&
operator ^=
operator !
operator ==
operator !=
operator[]
'managed vector constructor iterator'
operator returntype
'local static guard'
'managed vector destructor iterator'
operator -&
'string'(Unknown)
'eh vector copy constructor iterator'
operator *
'vbase destructor'
'eh vector vbase copy constructor iterator'
operator ++
'vector deleting destructor'
operator --
'default constructor closure'
operator -
'scalar deleting destructor'
operator +
'vector constructor iterator'
operator &
'vector destructor iterator'
operator -&*
'vector vbase constructor iterator'
operator /
'virtual displacement map'
operator %
'eh vector constructor iterator'
operator &
'eh vector destructor iterator'
operator &=
'eh vector vbase constructor iterator'
operator &
'copy constructor closure'
operator &=
'udt returning' (prefix)
operator ,
operator ()
RTTI-related code (see below)
operator ~
'local vftable'
operator ^
'local vftable constructor closure'
operator |
operator new[]
operator &&
operator delete[]
operator ||
operator *=
'placement delete closure'
operator +=
'placement delete[] closure'
operator -=
虚表的mangled name是::= ??_7[ &class name& ]∞0 @6B@,
前缀_P用在?_PX之中. 其含义未知。
下表是相关的编码,都是在_R后跟一个数字. 有些编码还后跟参数.
type 'RTTI Type Descriptor'
Data type type.
'RTTI Base Class Descriptor at (a,b,c,d)'
Four encoded numbers: a, b, c and d.
'RTTI Base Class Array'
'RTTI Class Hierarchy Descriptor'
'RTTI Complete Object Locator'
函数模板实例化后,就是一个具体的函数。类模板实例化后,就是一个具体的类数据类型。
&类模板实例化的名字& ::= ?$ &类模板的名字& &模板实参的编码&
&函数模板实例化的名字& ::= ?$ &函数模板的名字& &模板实参的编码&
&函数模板实例化的名字manging& ::= ?$ &函数模板的名字& &模板实参的编码& &函数的类型信息&
模板的名字以前缀?$开始。?$ &函数模板的名字& &函数模板实参的编码&可以代替&function name&,?$ &类模板的名字& &类模板实参的编码&可以代替&class name&。
模板实参(template argument),可以分为类型名字(typename)与非类型(non-type)的常量两类。如果是类型名字或类作为模板实参,那么其编码格式详见。如果模板实参是常量(constant),则已知的编码格式列为下表:
anonymous type template parameter x ('template-parameter-x')
实数值a × 10b-k+1, where k是无符号整数a的十进制的位数
anonymous type template parameter a ('template-parametera')
2-tuple {a,b} (unknown)
3-tuple {a,b,c} (unknown)
anonymous non-type template parameter a ('non-type-template-parametera')
上表中,用a, b, c表示有符号整数,而x, y, z表示无符号整数. 这些有符号整数或无符号整数的编码格式,详见。上表中,实数值的编码表示$2ab,a、b都是有符号整数,但计算实数的值时,实际上规范到以10为基数的的表示形式。
template&class T& class one{
one&int& one1; // mangled as ?one1@@3V?$one@H@@A
//3V...@A表示这是一个全局的类对象,其中的@是类的编码的结束标志;
//类名为one&int&,编码是?$one@H@,其中one@是模板的名字,
//H是模板实参int,其后的@是模板实参表结束标志
class Ce{};
one&Ce& another; /* mangled as ?another@@3V?$one@VCe@@@@A */
//注意,倒数第1个@表示整个(模板实例)类的结束;
倒数第2个@表示模板实参表的结束;
倒数第3个@表示类Ce的限定情况的结束(此处限定为空,即Ce的作用域是全局);
倒数第4个@表示类名码串的结束
编号名字空间(numbered namespace)用于指出函数静态局部变量包含在函数的哪个内部作用域中。之所以需要引入编号名字空间,是为了区分函数内部的不同作用域,从而可以区 分包含在不同作用域中但同名的变量,详见下例。其编码格式为前缀?后跟一个无符号数,无符号数的编码参见小节。
特例情况是, 以?A开始的编号名字空间, 是('anonymous namespace').
int func()
static int i;
// mangled as ?i@?1??func@@YAHXZ@4HA
内部表示为`func'::`2'::i
// ?1表示第2号名字空间;?func@@YAHXZ@是函数的mangled名字;4表示静态局部变量
static int i; // mangled as ?i@?2??func@@YAHXZ@4HA
内部表示为`func'::`3'::i
// ?3表示第3号名字空间
在对一个名字做mangling时,用简写方法表示非首次出现的同一个名字或同一个类型。整个简写过程需要对ASCII码串做3次扫描处理:
对函数或函数指针的形参表中的重复出现的参数数据类型的编码简写; 把第一步获得结果中重复出现的名字的编码简写; 把第二步获得的结果中的模板实例化(模板名字+模板实参表)内部重复出现的名字的编码简写。
这适用于函数与函数指针的形参列表。只有编码超过一个字符的类型参与简写,包括指针、函数指针、引用、数组、bool、__int64、 class、实例化的模板类、union、struct、enum等数据类型。形参表中前10种多字符编码的类型按照出现次序依次编号为 0,1,...,9。用单个字符编码的类型不参加编号。对不是该数据类型首次出现的形参,用该类型的单个数字的编号代替该数据类型的多个字符的编码来简写 表示。排在前十名之后的多字符编码的数据类型,不再简写。函数的返回值类型不参与此编号及简写。
如果函数的返回类型或者形参是函数指针型,那么函数指针型的形参也参与类型排序编号与简写,但函数指针型的返回值类型不参与类型排序编号与简写。在对类型排序编号时,先编号函数指针型内部的形参的数据类型,再编号函数指针型本身。例如,假如函数的第一个形参是void (__cdecl*)(class alpha, class beta),那么class alpha编号为0,class beta编号为1, 最后整个函数指针编号为2.
bool ExampleFunction (int*a, int b, int c, int*d, bool e, bool f, bool*g);
// mangled as ?ExampleFunction@@YA_NPAHHH0_N1PA_N@Z
其中,_N为返回类型bool,不参与类型简写,不参与编号排序;
类型的排序编号:int*为0,bool为1,bool*为2。
int的编码为单字符H,因此不参与编号
第3个形参不简写,仍为H;第四个形参简写为0,第五个形参简写为1 */
typedef int* (*FP)(int*); /* 该函数指针类型编码为 P6APAHPAH@Z */
funcfp (int *, FP) /* mangled as ?funcfp@@YAP6APAHPAH@Z0P6APAH0@Z@Z */
其中P6A为函数指针类型的编码前缀
其中共出现了5次int* (编码为PAH)
第1次为funcfp的返回值类型FP的返回值类型,不参与排序编号与简写;
第2次为funcfp的返回值类型FP的形参,参与排序编号,编号为0,
因为是该类型int*的首次作为形参出现,不简写,仍编码为PAH
第3次为funcfp的第一个形参,简写为0;
第4次为funcfp的第二个形参的返回值类型,不参与排序编号与简写;
第5次为funcfp的第二个形参的参数,简写为0
{return 0;}
在对码串完成后,再对结果中所有不同的名字排序编号,从0编号到9。排在前10个之后的名字不再编号、简写。这里的名字是指函数、class、struct、union、enum、实例化的带实参的模板等等的以@作为结尾后缀的名字。例如,在alpha@?1beta@@(即beta::'2'::alpha)中,
0指代alpha@, 1指代beta@,?1是‘2’的编码.、都不参加此轮名字的排序编号与简写。
class C2{};
union C2{};
void func(C2,C1::C2) /* mangled as ?func@@YAXTC2@@V1C1@@@Z */
//其中,func的形参表是TC2@@V1C1@@@
//TC2@@是第一个形参union C2;
//V1C1@@是第二个形参C1::C2,其中V表示这是class,
//首个1表示是编号为1的名字(即C2@)重复出现,随后的C1@表示C2的限定域为C1
//随后的@表示限定域的嵌套结束。注意,该字串中编号为“0”的名字是func@
对于,模板名字后跟模板实参作为一个整体视作一个名字,参加此轮排序编号与简写。而模板实参表中的参数序列,单独处理它的编号及简写。例如:
template&class T& class tc{
public: void __stdcall func(tc&T&){};
int main(int argc, char *argv[])
tc&int& ins;
ins.func(ins); /* tc&int&::func(tc&int&) mangled as ?func@?$tc@H@@QAEXV1@@Z */
//其中,?$tc@H@表示实例化的类模板tc&int&,里面的H表示模板实例化的实参是整型
//Q表示public的成员函数;A表示非只读成员函数;E表示thiscall
//X表示函数返回类型void;
//V1@表示形参为一个class,class的名字为''1''号名字,本例中即tc&int&的编码?$tc@H@;
//注意,本例中''0''号名字是函数名,即func@
//最后一个@表示函数的形参表结束;Z表示缺省的exception specification
,基本上就是用模板的名字与模板实参作为一个整体,当作&func name&或&class name&使用。因此模板实例化的名字在参与完成与两步处理之后,再单独处理模板实例化的模板实参表,对其内部重复出现的名字的编号与简化。其方法与的处理相同。
template&class T1, class T2& class tc{
public: void __stdcall func(tc&T1,T2& p1,tc&T1,T2& p2){};
class Ce{};
int main(int argc, char *argv[])
tc&Ce,Ce& ins;
ins.func(ins,ins);
/* void tc&Ce,Ce&::func(tc&Ce,Ce&,tc&Ce,Ce&)
mangled as ?func@?$tc@VCe@@V1@@@QAGXV1@0@Z */
//?$tc@VCe@@V1@@表示实例化的类模板tc&Ce,Ce&,其中的V1@是类模板的实参的重复名字简写;
//函数func的作用域是tc&Ce,Ce&,即tc&Ce,Ce&::func编码为func@?$tc@VCe@@V1@@@
//V1@表示成员函数func的第一个形参为一个类,类名为''1''号名字,本例中即tc&Ce,Ce&,这是重复名字的简写;
//0表示该函数func的第二个形参与形参表中的''0''号形参相同,即tc&Ce,Ce&,这是重复类型的简写
class class1{
class class2{} ee2;
union class2{};
template &class T1, class T2, class T3& void func( T1,T2,T3 ){}
int main(int argc, char *argv[])
func&class2, class1::class2, class2&(b,a.ee2,b);
/* func&class2,class1::class2,class2&(class2,class1::class2,class2)
mangled as ??$func@Tclass2@@V1class1@@T1@@@YAXTclass2@@V0class1@@0@Z
//如果不简写: ??$func@Tclass2@@Vclass2@class1@@Tclass2@@@@YAXTclass2@@Vclass2@class1@@Tclass2@@@Z
首先对函数的形参表中重复的类型简写,第3个参数与第1个参数相同,以类型编号0简写;
然后对整个字串中重复的名字编号、简写,第1个形参中的名字‘class2@’编号为0,
所以函数第2个形参中的‘class2@’被简写为0;
这也说明“函数模板名+模板实参”,不作为名字参与编号及简写(但普通函数的名字却是参与名字编号!),
而类模板名+模板实参”却算作单独一个名字而参与编号及简写;
之后,对模板实例化名字,即“函数模板名+模板实参”,单独执行重复出现的名字编号及简写,
其中的‘class2@’出现3次,后2次被简写为1,这也说明在模板实例化名字内部,仅执行重复名字的简写,
不执行重复类型的简写
class class1{
public: class name9{} ee;
template &class T& void name9 ( T p1){}
int main(int argc, char *argv[])
class1 vv;
name9&class1::name9&(vv.ee); /* void name9&class1::name9&(class1::name9)
mangled as ??$name9@V0class1@@@@YAXVname9@class1@@@Z */
此例说明模板实例化在做重复名字简化时,
模板实参中的name9与模板名字name9相同,因而简化为编号0
这里所说的类型,包括数据类型、函数指针的类型、函数模板、类模板等不需要分配内存空间的一些概念属性。类型是数据对象与函数这两类实体的属性。
不带下划线(_)的含义
前置下划线(_)的含义
用于表示模板
用于表示模板
__w64 (prefix)
Back reference即用于重复出现的类型或名字的简写
Type modifier (reference)
Type modifier (volatile reference)
signed char
unsigned char
unsigned __int8
unsigned short
unsigned __int16
unsigned int
unsigned __int32
unsigned long
unsigned __int64
unsigned __int128
long double
Type modifier (pointer)
Type modifier (const pointer)
Type modifier (volatile pointer)
Type modifier (const volatile pointer)
void, Complex Type (coclass)
Complex Type (coclass)
Complex Type (cointerface)
Complex Type (cointerface)
... (ellipsis)
对于简单的数据类型,其编码往往就是一个字母。如int类型编码为X。对各种衍生的数据类型(如指针)、复合的数据类型(如类)、函数指针、模板等,在下文中分述。
X表示void 仅当用于表示函数的返回类型、形参表的终止或指针的基类型, 否则该编码表示cointerface. 代码Z (表示ellipsis)仅用于表示不定长度的形参列表(varargs).
&指针类型的编码& ::= &type modifier& &CV Modifier& &base type&
&引用类型的编码& ::= &type modifier& &CV Modifier& &base type&
&一维数组类型的编码& ::= &指针类型的编码&
&多维数组类型的编码& ::= &type modifier& &CV Modifier& &Array property&&base type&
其中&type modifier&作为前缀,用于区分各种情况的指针、引用、数组。指针自身是const还是volatile等访问属性,由&type modifier&确定。共有八种情况:
const volatile
&CV Modifier&表示所指向的基类型(Referred type)是否具有const或volatile等访问属性,详见。
&base type&表示指针或引用的基类型(Referred type),或数组的成员类型(element type)。其编码详见。
&Array property&表示多维数组的基础维度信息,其格式为:Y&数组总的维数-1&&第2维的长度&...&最后的第N维的长度&。注意,这里使用的数字,要用Visual C++ name mangling特有的数字编码方法,详见。
可见,C++语言的数组是对连续存储数据的内存的直接随机访问(random access)的手段;因此一维数组视作指针,数组访问是否越界,完全由编程者负责;而对多维数组,必须知道除了第一维之外其它各维的长度,才能做到直接 随机访问,所以多维数组作为函数形参时,必须已知其除了第一维之外其它各维的长度(以及总的维数),这些信息都被编入了数组的mangled name中。
对于函数指针类型的编码,其&base type&为函数调用接口信息,包括使用的调用协议、返回值类型、形参类型、允许抛出的异常等,详见。类成员指针的类型编码,详见。类成员函数指针的类型编码,详见。
2003年x86-64位处理器问世后,第一批64位Windows平台的C++编译器曾经使用_O作为数组类型的前缀修饰符(type modifier). 但不久就改回了32位Windows平台C++编译器使用的P前缀.
需注意的是,全局数组类型被编码为P(指针型),同时作为函数形参的数组类型被编码为Q(常量指针). 这与其本来含义恰恰相反——全局数组型的变量名字表示某块内存地址,该名字不能再改为指向其它内存地址;而作为函数形参的数组型变量的名字所表示的内存地 址是可以修改的。但数组类型这种编码方法已经被各种C++编译器广泛接受。显然,这是为了与老的代码保持向后兼容。例如:
int ia[10];
//ia是数组类型的非函数形参的变量
int main(int argc, char *argv[]) //argv是数组类型的形参, 其类型为 (char *)[]
int j=*(ia++);
//编译错误!ia是只读的lvalue,不能完成地址的++操作
char *c= *(argv++);
//编译正确!argv是可以修改的lvalue
typedef int * p1; // coded as PAH 其中P表示default访问属性的指针,
//A表示对基类型的default访问属性,H表示基类型为int
typedef const int * p2; //coded as PBH 其中B表示基类型的const访问属性
typedef volatile int *p3; //coded as PCH 其中C表示基类型的volatile访问属性
typedef volatile const int *p4; //coded as PDH 其中D表示基类型的const volatile访问属性
typedef int * const p5; //coded as QAH 其中Q表示是const pointer
typedef int * volatile p6; //coded as RAH 其中R表示是volatile pointer
typedef int * const volatile p7; //coded as SAH 其中S表示是const volatile pointer
typedef volatile int * const p6; //coded as QCH 例如这是一个外部IO设备输入数据的内存地址
typedef int &r1; // coded as AAH 其中第一个A表示引用类型(reference type)
typedef int[8] a1; // global array coded as PAH
typedef int[10][8] a2; //global array coded as PAY07H 其中Y标志多维数组,0是(维数-1)即1的编码
// 7是第二维长度8的编码
typedef int[4][16][5] a3; //global array coded as PAY1BA@4H 其中1为(维数-1)即2的编码
//BA@是第二维长度16的编码(16进制的10),4是第3维长度5的编码
int[7][6] // 作为函数形参时,该数据类型编码为 QAY05H
函数指针的类型信息,包括函数返回类型,函数形参类型,调用协议等。以前缀P6开始。各项具体定义可参见:
&global function pointer type info& ::= &type modifier& &CV Modifier& &calling conv&
[&storage ret&]&return type&[&parameter type&]∞1&throw type&
一般地,&type modifier&取值为P,意为指针;&CV Modifier&取值为6,意为指针的基类型为near属性的非成员函数。
typedef const
int (__stdcall *FP) (int i); /* coded as P6G?BHH@Z */
其中?B表示&storage ret&为const
指向类成员的指针,其编码为
&pointer-to-Member Type& = &type modifier& &CV Modifier&
&base-class Qualified Name& &base type&
其中各项的定义详见。注意,&CV Modifier&是指基类型的属性,常用的值为:Q
for default, R S T for const volatile。
typedef int C1::*p; // coded as PQC1@@H
类成员指针(pointer to member)的名字mangling的最末尾处对基类型访问属性的编码不同于普通的指针,要在最后加上所指向类的带完整限定信息的名字:
&pointer-to-member name mangling& ::= ?&Qualified Name& &data access modifier&
&pointer-to-Member Type& &CV Modifier& &base-class Qualified Name&
有的文献称,类成员指针、类成员函数指针的name mangling都必须以Q1@作为结尾,以替代&CV Modifier&。从下述几例可以看出,这种说法是错误的。Q是对所指向的成员类型使用default访问属性,这是最常见的情况。1是该指针所指向类的名字简写,因为在此位置之前该类的名字必然已经出现在该数据类型的编码中,所以此处名字的简写是必然的。但不一定总是简写作1
下例中,成员指针变量的mangled name以S12@结尾:
class outer{
class cde{
public: volatile int i;
volatile int outer::cde::* p; // mangled as ?p@@3PScde@outer@@HS12@
// 其中两个S都是表示基类型为volatile属性
// 1是cde@的简写
// 2是outer@的简写
public: int i;
C1 const *pi; // ?pi@@3PBVC1@@B 一个简单的指针变量。作为对比
TP; // coded as PQC1@@H
void func(TP){
static TP ppp=0; // `func'::`2'::ppp mangled as ?ppp@?1??func@@YAXPQC1@@H@Z@4PQ2@HQ2@
//其中,?ppp@?1??func@@YAXPQC1@@H@Z@表示带作用域限定信息的名字`func'::`2'::ppp
//4表示静态局部变量;PQ2@H表示成员指针类型,其中的“2”是C1@的简写。
//注意ppp@编号为0,func@编号为1
//最后三个字符Q2@表示对基类型“2”(C1@的简写)的访问属性为Q(缺省属性,即非const非volatile)
类成员函数的指针(pointer to member function),遵从的一般规则。但与相比,多了一项&const
Modifier&,表示所指的函数是否为只读成员函数(constant member function)。
&pointer-to-member-function type info& ::=
&type modifier& &CV Modifier& &base-class Qualified Name&
[&const Modifier&] &calling conv& [&storage ret&] &return type&
[&parameter type&]∞1
&throw type&
一般地,&type modifier&取值为P,意为指针;&CV Modifier&取值为8,意为指针的基类型为near属性的类成员函数。其它各项取值参见。
public: void foo(int) const
void (C1::*TP)(int) const; /* coded as P8C1@@BEXH@Z
其中B表示c E表示thiscall */
类成员函数指针(pointer to member function)的名字mangling,对基类型访问属性的编码&CV Modifier&不同于普通的指针,要在最后加上所指向类的带完整限定信息的名字。
&pointer-to-member name mangling& ::= ?&Qualified Name& &data access modifier&
&pointer-to-Member-Function Type& &CV Modifier& &base-class Qualified Name&
上述定义中,&CV Modifier&取值一般是Q
class xyz{
public: void foo(int) {};
void (xyz::*pfunc)(int) ; /* mangled as ?pfunc@@3P8xyz@@AEXH@ZQ1@ */
其中Q表示对基类型的访问属性为default;1表示被简写的编号为‘1’的名字,即‘xyz@’;
注意,编号为‘0’的名字是‘pfunc@’
&复合类型的编码& ::= &复合类型的种类&&复合类型的带限定的名字&
其中复合类型的种类作为前缀,union编码为T, struct编码为U, class编码为V, coclass编码为X, cointerface编码为Y。复合类型的带限定的名字&Qualified Name&,是指按照名字所在的名字空间、所属的类,逐级列出限定情况(qualifier),详见。
经常可以看到复合类型的编码以@@两个字符结尾,这是因为第一个@表示复合类型名字的字串结束,第二个@表示限定情况的结束(即作用域为全局,限定情况为空)。
编写代码时,经常要用到类的前向声明(forward declaration),即提前声明这个名字是个类,但类的成员尚未给出。例如:
class myClassName; //mangled type name is VmyClassName@@
class myClassName::embedClassName; //mangled type name is VembedClassName@VmyClassName@@
&枚举类型的编码& ::= W &枚举实际使用的数据类型& &enum-type Qualified Name&
&枚举成员的编码& ::= W &枚举实际使用的数据类型& &enumerator name&@ &enum-type Qualified Name&
其中,W为枚举类型前缀词。&enum-type Qualified Name&为枚举类型的带限定的名字,是指按照名字所在的名字空间、所属的类,逐级列出限定情况(qualifier),详见。枚举实际使用的数据类型,
编码如下:
对应的实际数据类型
unsigned char
unsigned short
int (generally normal "enum")
unsigned int
unsigned long
enum namex:unsigned char {Sunday, Monday}; // enum-type coded as W4namex@@
看起来Visual C++已经把所有枚举类型用int型实现,因此枚举的基类型(The underlying type of the enumeration identifiers)的编码总是为4
&CV Modifier&用于普通的数据对象,表示其是否具有const、volatile等访问属性;用于指针、数组、引用类型,则表明对基类型的访问属性,而指针自身是否为const、volatile等属性,则专由&type modifier&编码表示。
&CV Modifier&用于函数指针时,表示该指针所指向的基类型是函数。但与指向数据对象的普通指针不同——函数指针指向的基类型(即函数)也有自己的内存空间,只是这块内存空间必定是只读的、可执行的,因此函数指针所指向的基类型内存空间不存在const、volatile等访问属性。
&CV Modifier&的取值情况:
const volatile
__based() Member
&CV Modifier&可以有0个或多个前缀:
type __ptr64
__unaligned type
type __restrict
指针变量的__based()属性是Microsoft的C++语言扩展. 这一属性编码为:
0 (意味着__based(void)) 2&Qualified Name& (意味着__based(&Qualified Name&)) 5 (意味着没有__based()) 例如:
int *pBased;
// mangled name:
?pBased@@3PAHA
int __based(pBased) * pBasedPtr;
// 需要注意Visual C++编译器把这个指针变量的声明解释为:
(int __based(pBased) * __based(pBased) pBasedPtr)
因此其mangled name: ?pBasedPtr@@3PM2pBased@@HM21@
其中PM2pBased@@表示这是基于&::pBased&的指针;HM21表示是基于“1”的整型指针,
“1”是重复出现的名字的编号简写,这里就是指pBased@
int __based(void) *pbc; // mangled name: ?pbc@@3PM0HM0 其中的0表示这是__based(void).
编译器把该变量声明解释为(int __based(void) * __based(void) pbc)
函数的类型信息,是指调用函数时必须考虑的ABI(Application Binary Interface),包括调用协议、返回类型、函数形参表、函数抛出异常的说明(exception specification)等,参见。
&func modifier&给出了函数是near或far(但far属性仅适用于Windows 16位环境,32位或64位环境下只能函数具有near属性)、是否为静态函数、是否为虚函数、类成员函数的访问级别等信息:
static near
static far
virtual near
virtual far
thunk near
protected:
not member
上表中的,是指在时,由编译器生成的包装函数(warpper
function),用于多态调用实际已被子类对应函数覆盖(overrided)的父类虚函数,并把指向父类的this指针调整到指向子类的起始地址。
Calling Convention
__pascal __fortran
__thiscall
__thiscall
__fastcall
__fastcall
64位编程时,唯一可用的调用协议的编码是A
有多种方法,可以方便地查看一个函数在编译后的修饰名字:
直接用工具软件(如微软开发环境提供的dumpbin)查看obj、exe等二进制文件。使用dumplib查看.obj或.lib文件时,使用"/SYMBOLS"命令行选项。编译时使用"/FA[c|s|u]"编译选项,生成带有丰富注释信息的汇编源程序,其文件扩展名是.cod或者.asm,可以查看每个C/C++函数的修饰名字。在源程序中使用微软提供的预定义宏(Microsoft-Specific Predefined Macros)—— __FUNCDNAME__,例如:
void exampleFunction()
printf("Function name: %s\n", __FUNCTION__);
printf("Decorated function name: %s\n", __FUNCDNAME__);
printf("Function signature: %s\n", __FUNCSIG__);
// 输出为:
// -------------------------------------------------
// Function name: exampleFunction
// Decorated function name: ?exampleFunction@@YAXXZ
// Function signature: void __cdecl exampleFunction(void)
使用微软Visual C++中的解析修饰名字的工具软件undname.exe。例如:
C:\&undname.exe ??$name9@V0class1@@@@YAXVname9@class1@@@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.
Undecoration of :- "??$name9@V0class1@@@@YAXVname9@class1@@@Z"
is :- "void __cdecl name9&class class1::name9&(class class1::name9)"
使用Windows提供的系统调用UnDecorateSymbolName()把 修饰名字翻译为未修饰名字。UnDecorateSymbolName在DbgHelp.h或imagehlp.h中声明,在DbgHelp.dll中实
现,需要使用导入库DbgHelp.lib。Windows SDK中包含了DbgHelp.h与DbgHelp.lib。示例程序:
//UnDecorate.cpp
#include &windows.h& //如果不包含此头文件,编译DbgHelp.h时会产生大量语法错误
#include &DbgHelp.h&
#include &tchar.h&
#include &iostream&
#pragma comment(lib,"dbghelp.lib") //告诉链接器使用这个输入库
int _tmain(int argc, _TCHAR* argv[])
TCHAR szUndecorateName[256];
memset(szUndecorateName,0,256);
if (2==argc)
::UnDecorateSymbolName(argv[1],szUndecorateName,256,0);
std::cout&&szUndecorateName&&std::endl;
编译后,执行上述程序:
C:\&UnDecorate.exe
?apiname@@YA_NEEPAD@Z
bool __cdecl apiname(unsigned char,unsigned char,char *)
在Windows平台上,使用dllexport关键字直接输出C++函数时,DLL的用户看到的是修饰后的函数名字. 如果不希望使用复杂的C++修饰后的函数名,替代办法是在DLL的.def文件中定义输出函数的别名,或者把函数声明为extern
在汇编源程序或者内联汇编中引用了C/C++函数,就必须引用该函数的修饰名字。
VC环境中C++ 语言中的 Name Mangling
在 C++ 语言中,函数需要支持重载,新增命名空间函数调用、类函数调用、运算符重载、模板函数等等,所以情况也比 C 语言复杂很多。
下面我们列举一些函数例子进行分析,函数可以有如下声明方式(其中 __CALLTYPE 可以为 __cdecl、__stdcall、__fastcall等)[1]:
1、void __CALLTYPE fun();
2、int __CALLTYPE fun();
3、int __CALLTYPE fun(int);
4、double __CALLTYPE fun(int, double);
5、int* __CALLTYPE fun(int*, char*);
6、class ABCD
int __CALLTYPE fun();
7、template&typename T&
int fun(typename T);
我们仍先假设此处 CALLTYPE 为 __cdecl(即:#define __CALLTYPE __cdecl),在 VC 中,Name Mangling 结果如下:
1、?fun@@YAXXZ
2、?fun@@YAHXZ
3、?fun@@YAHH@Z
4、?fun@@YANHN@Z
5、?fun@@YAPAHPAHPAD@Z
6、?fun@ABCD@@QAAHXZ
7、??$fun@H@@YAHH@Z
由此可见,C++ 的 Name Mangling 技术比 C 语言的复杂很多。
我们挑选第一条分析一下,“?”表示一个函数的开始,用以区别于 C 语言的“_”,fun 为函数名称,“@@YA”表示函数调用约定为 __cdecl,“X”表示函数的参数为空,“XZ”为结束标识。
将上述名称还原为可读方式并不复杂,但要记住这些规则,考虑到所有组合方式却是一件比较复杂的事情,下面我们来看看一个比较复杂的函数调用,声明函数如下:
int fun(const CString&, const std::vector&);
Name Mangling 后的结果为:
?fun@@YAHABV?$CStringT@_WV?$StrTraitATL@_WV?$ChTraitsCRT@_W@ATL@@@ATL@@@ATL@@ABV?$vector@NV?$allocator@N@std@@@std@@@Z
如此长的一串,用人脑来直接分析显然不符合实际,好在 Windows 提供了 API 函数用于解析字符串,具体解析办法,下面一节将详细解释。
将Name Mangling 后的名称还原为可读的形式
在 Windows 的DbgHelp.dll 导出函数中,UnDecorateSymbolName 是用于解析 Name Mangling 字符串的,具体函数的细节可以查看 MSDN。如下为实例代码:
void UnDecorateName()
char szDecorateName[1024] = {0};
char szUnDecorateName[2048] = {0};
printf("Please Input Decorated Name: ");
scanf("%s", szDecorateName);
if (UnDecorateSymbolName(szDecorateName, szUnDecorateName, sizeof(szUnDecorateName), UNDNAME_COMPLETE) == 0)
printf("UnDecorateSymbolName Failed. GetLastError() = %d", GetLastError());
getchar();
printf("The UnDecorated Name Is: %s/r/n", szUnDecorateName);
getchar();
在 Xp 中当我们输入如上的:?fun@@YAPAHPAHPAD@Z
程序得出的结果为:int * __cdecl fun(int *,char *)
注意:在 Xp 中,带有模板的 Name Mangling 字符串无法直接还原,如需还原,可以在 Vista、Win7 中运行此程序。
***********************************************************
1. c++的一个重要的特性就是重载,一个类可以拥有多个同名函数。那么编译器是如何来区别重载函数的那?答案就是Name Mangling(或者叫做Name Decorate).
2. Name Mangling就一个对函数进行哈希的算法,所以有的中文翻译为:函数签名。一个VC++函数经过函数签名后可能是这个结果:?Test2@@YAHXZ 。
3. Name Mangling不是C++特有的,只要是支持重载的高级语言就会有。对于VC++来说,有一个WIN32 API可以解析这些经过编码后的函数签名。
4. 这篇文章是研究如何Mangling的:
#include &Windows.h&#include &Dbghelp.h&#pragma comment(lib, "dbghelp.lib")int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow){
wchar_t * lpdecorate_name
= L"?Test2@@YAHXZ";
wchar_t readable[MAX_PATH]=
if (::UnDecorateSymbolNameW(lpdecorate_name, readable,
MAX_PATH, UNDNAME_COMPLETE))
::MessageBox(NULL, readable, L"Readable Name", MB_OK);
return 0;}
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!

我要回帖

更多关于 js json转换对象数组 的文章

 

随机推荐