C++函数重载的编译后的一个奇怪的问题

在看一本C++教程,看到下面一段之后晕了:
在下面的代码中:
void f();
class x {void f();};
类x内的函数f()不会与全局的f()发生冲突,编译器用不同的内部名f()(全局)和x : : f ( )
(成员函数)来区分两个函数。在第2章中,我们建议在函数名前加类名的方法来命名函数,所
以编译器使用的内部名字可能就是_ f和_ x_f。函数名不仅与类名关系密切,而且还跟其他因素
有关。
以上是书上的内容,看到这里有个疑问,是不是在C++中定义的函数如:add(),math::add()这样的函数并不是真正的函数名,被编译之后会被变成别的名呀?而且这种变化是随机的?比如add()可能变为_add,math::add()变为_math_add?是不是呀?
另外,上面说的内部名字是什么意思呀?是指编译器编过的定义的函数吗?比如add()被编译器编译后变成_add,这个_add就是内部名字吗?
还有那些通过参数重载的函数,比如void print(char);void print(float);这两个重载函数,是不是被编译之后,会根据参数的不同形成两个有些相同又有些不同的名字呀,比如_ p r i n t c h a r,_ p r i n t f l o a t,相同的是函数名和不同的是参数的组合,换句话说,在程序中,实际上像void print(char);void print(float);这样的函数名并不存在,而是被改成了这种_ p r i n t c h a r,_ p r i n t f l o a t,并且这种被称为内部名字

  是的,对于编译器来说,名字是不一样而且唯一的,编译器会对函数名“修饰”。


    C++ 编译器的函数名修饰规则 

    函数名字修饰(Decorated Name)方式 

        函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。LINK程序或其他工具有时需要指定函数的名字修饰来定位函数的正确位置。多数情况下程序员并不需要知道函数的名字修饰,LINK程序或其他工具会自动区分他们。当然,在某些情况下需要指定函数的名字修饰,例如在C++程序中,为了让LINK程序或其他工具能够匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。另一种需要指定函数的名字修饰的情况是在汇编程序中调用C或C++的函数。如果函数名字,调用约定,返回值类型或函数参数有任何改变,原来的名字修饰就不再有效,必须指定新的名字修饰。C和C++程序的函数在内部使用不同的名字修饰方式,下面将分别介绍这两种方式。 


    1. C编译器的函数名修饰规则 

        对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,例如_functionname@number。__cdecl调用约定仅在输出函数名前加上一个下划线前缀,例如_functionname。__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,例如@functionname@number 

    2. C++编译器的函数名修饰规则 

       C++的函数名修饰规则有些复杂,但是信息更充分,通过分析修饰名不仅能够知道函数的调用方式,返回值类型,参数个数甚至参数类型。不管__cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字,再后面是参数表的开始标识和按照参数类型代号拼出的参数表。对于__stdcall方式,参数表的开始标识是“@@YG”,对于__cdecl方式则是“@@YA”,对于__fastcall方式则是“@@YI”。参数表的拼写代号如下所示: 
    X--void    
    D--char    
    E--unsigned char    
    F--short    
    H--int    
    I--unsigned int    
    J--long    
    K--unsigned long(DWORD) 
    M--float    
    N--double    
    _N--bool 
    U--struct 
    .... 
    指针的方式有些特别,用PA表示指针,用PB表示const类型的指针。后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复。U表示结构类型,通常后跟结构体的类型名,用“@@”表示结构类型名的结束。函数的返回值不作特殊处理,它的描述方式和函数参数一样,紧跟着参数表的开始标志,也就是说,函数参数表的第一项实际上是表示函数的返回值类型。参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。下面举两个例子,假如有以下函数声明: 

    int Function1 (char *var1,unsigned long); 
    其函数修饰名为“?Function1@@YGHPADK@Z”,而对于函数声明: 
    void Function2(); 
    其函数修饰名则为“?Function2@@YGXXZ” 。 

        对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字符引导的类名;其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是“@@IAE”,私有(private)成员函数的标识是“@@AAE”,如果函数声明使用了const关键字,则相应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。如果参数类型是类实例的引用,则使用“AAV1”,对于const类型的引用,则使用“ABV1”。下面就以类CTest为例说明C++成员函数的名字修饰规则: 

class CTest 

...... 
private: 
    void Function(int); 
protected: 
    void CopyInfo(const CTest &src); 
public: 
    long DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE bUnder, bool bSet); 
    long InsightClass(DWORD dwClass) const; 
...... 
};

  对于成员函数Function,其函数修饰名为“?Function@CTest@@AAEXH@Z”,字符串“@@AAE”表示这是一个私有函数。成员函数CopyInfo只有一个参数,是对类CTest的const引用参数,其函数修饰名为“?CopyInfo@CTest@@IAEXABV1@@Z”。DrawText是一个比较复杂的函数声明,不仅有字符串参数,还有结构体参数和HDC句柄参数,需要指出的是HDC实际上是一个HDC__结构类型的指针,这个参数的表示就是“PAUHDC__@@”,其完整的函数修饰名为“?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z”。InsightClass是一个共有的const函数,它的成员函数标识是“@@QBE”,完整的修饰名就是“?InsightClass@CTest@@QBEJK@Z”。

追问

成员函数CopyInfo只有一个参数,是对类CTest的const引用参数,其函数修饰名为“?CopyInfo@CTest@@IAEXABV1@@Z”中的“@@Z”怎么两个@,前面的那个@是什么情况?

追答

?CopyInfo (函数名) @CTest (类名) @@IAE (访问级别) X (void返回值) ABV1@ (ABV1是常量引用@表示自己的类名,相当于ABV1 ‘CTest’) @Z (结束符)

追问

?CopyInfo@CTest@@IAEXABV1@@Z怎么没有@@YG这样的调用方式
?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@Z中的UtagRGB,U表示结构类型,通常后跟结构体的类型名,这里不是RGBQUAD?怎么成了tagRGBQUAD?

温馨提示:答案为网友推荐,仅供参考
相似回答