静态库中的extern “C”难道说不是必须的吗
静态库中的extern “C”难道不是必须的吗?
在学习静态库的时候,我明白了一点,声明为extern "C" 的意思就是兼容C,这样对函数名就只会简单的处理,而不会加上参数类型等信息了。但是,我有次忘了添加extern "C"的时候,生产的静态库还是可以使用的。例如下面这两个函数:
生成静态库只会,还是可以正常使用。所以我迷糊了,难道extern "C"在生成静态库的时候还是可有可无的吗?
------解决方案--------------------
------解决方案--------------------
建议你导出符号表,比对下有无extern "C"的情况。然后能看到符号的区别
然后导出不同平台下编译的程序的符号表,查看不同平台的区别。就知道原因了
------解决方案--------------------
"C" 或者 "C++" 函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数,又如在汇编代码里调用"C""或"C++"函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。函数名修饰约定随编译种类(C或C++)和调用约定的不同而不同,下面分别说明。
C编译时函数名修饰约定规则:
__stdcall调用约定:
在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为 _functionname@number。
__cdecl调用约定:
仅在输出函数名前加上一个下划线前缀,格式为 _functionname。
__fastcall调用约定:
在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number。
它们均不改变输出函数名中的字符大小写。
C++编译时函数名修饰约定规则:
__stdcall调用约定:
以"?"标识函数名的开始,后跟函数名;函数名后面以"@@YG"标识参数表的开始,后跟参数表;
参数表以代号表示:
X——void,
D——char,
E——unsigned char,
F——short,
H——int,
I——unsigned int,
J——long,
K——unsigned long,
M——float,
N——double,
_N——bool,
....
PA——表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。
参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。其格式为
"?functionname@@YG*****@Z" 或 "?functionname@@YG*XZ",
例如
int Test1(char *var1,unsigned long) -----“?Test1@@YGHPADK@Z”
void Test2() -----“?Test2@@YGXXZ”(第一个X表示返回类型,第二个X表示参数类型)
__cdecl调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。
__fastcall调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。
对于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”表示这是一个私有函数。“X”表示返回类型为void,“H”表示参数类型为int类型。
成员函数CopyInfo只有一个参数,是对类CTest的const引用参数,其函数修饰名为“?CopyInfo@CTest@@IAEXABV1@@Z”。
在学习静态库的时候,我明白了一点,声明为extern "C" 的意思就是兼容C,这样对函数名就只会简单的处理,而不会加上参数类型等信息了。但是,我有次忘了添加extern "C"的时候,生产的静态库还是可以使用的。例如下面这两个函数:
- C/C++ code
/* 文件名:Math.h */ /*extern "C"*/ int add(int x,int y);//求和 /*extern "C"*/ int sub(int x,int y);//求差 /* 文件名:Math.cpp */ #include "Math.h" int add(int x,int y) { return x+y; } int sub(int x,int y) { return x-y; }
生成静态库只会,还是可以正常使用。所以我迷糊了,难道extern "C"在生成静态库的时候还是可有可无的吗?
------解决方案--------------------
------解决方案--------------------
建议你导出符号表,比对下有无extern "C"的情况。然后能看到符号的区别
然后导出不同平台下编译的程序的符号表,查看不同平台的区别。就知道原因了
------解决方案--------------------
"C" 或者 "C++" 函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如在模块定义文件里头指定输出"C++"重载函数、构造函数、析构函数,又如在汇编代码里调用"C""或"C++"函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。函数名修饰约定随编译种类(C或C++)和调用约定的不同而不同,下面分别说明。
C编译时函数名修饰约定规则:
__stdcall调用约定:
在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为 _functionname@number。
__cdecl调用约定:
仅在输出函数名前加上一个下划线前缀,格式为 _functionname。
__fastcall调用约定:
在输出函数名前加上一个"@"符号,后面也是一个"@"符号和其参数的字节数,格式为@functionname@number。
它们均不改变输出函数名中的字符大小写。
C++编译时函数名修饰约定规则:
__stdcall调用约定:
以"?"标识函数名的开始,后跟函数名;函数名后面以"@@YG"标识参数表的开始,后跟参数表;
参数表以代号表示:
X——void,
D——char,
E——unsigned char,
F——short,
H——int,
I——unsigned int,
J——long,
K——unsigned long,
M——float,
N——double,
_N——bool,
....
PA——表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前。
参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。其格式为
"?functionname@@YG*****@Z" 或 "?functionname@@YG*XZ",
例如
int Test1(char *var1,unsigned long) -----“?Test1@@YGHPADK@Z”
void Test2() -----“?Test2@@YGXXZ”(第一个X表示返回类型,第二个X表示参数类型)
__cdecl调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。
__fastcall调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。
对于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”表示这是一个私有函数。“X”表示返回类型为void,“H”表示参数类型为int类型。
成员函数CopyInfo只有一个参数,是对类CTest的const引用参数,其函数修饰名为“?CopyInfo@CTest@@IAEXABV1@@Z”。