编译器的函数名修饰
java里面main函数为什么要用static修饰

java⾥⾯main函数为什么要⽤static修饰
这学期刚开java,因为之前只写过C++和Python没接触过java,有些写法挺不习惯的,今天写完⼀个程序,run的时候发现提⽰the selection can't be launched.......查了⽹上⼀些资料,虽然不知道具体是不是我以为的这个原因,我main函数没有写static(C++和Python都没这玩意⼉),我想我这⾥应该是这个原因。
如果不是这个原因,欢迎指正。
那么我那时候就在想,为什么我不加static就这样了,于是去查了查static的作⽤:
static 是静态的意思,这个我们在学C和C++⾥⾯就学过。
0.static 修饰的域,我们叫静态域,它是归类所有的,被所有对象所共享,只有⼀个
1.static修饰的区块,域只会初始化⼀次
2.static修饰的域或⽅法,可以直接通过类的名字加上.进⾏调⽤
3.static修饰的⽅法内不能直接使⽤⾮静态成员
static这个字告诉编译器,这个main函数是静态的,储存在静态存储区,在定义以后它就存在了,缺少它的话,编译并不会出错,⽽是在运⾏的时候报错,因为这个时候main这个类并没有实例化,⾃然main⽅法就不能⽤,加上static之后,这个⽅法(main)就变成了静态的,不需要实例化就能⽤,我记得刚开始学C语⾔的时候,我们⽼师说main是⼀个程序的⼊⼝,当时怎么想都没想明⽩,现在看这个例⼦,就可以想得通为什么要加static,main作为⼊⼝,不可能先去实例化⼀个对象去调⽤它,所以他必须可以直接调⽤。
volatile函数

volatile函数Volatile函数是一种在C和C++中使用的修饰符,它告诉编译器不应优化变量的访问。
Volatile函数可以用于保护共享内存,以确保来自多个线程的并发访问不会引起意外的,未预料的结果。
由于编译器优化,它会假设变量仅在被声明时和程序结束时通过程序来访问。
但是在这两个时间点之间,它不去考虑变量是否会被更改,这将导致程序失灵。
要解决此问题,应使用volatile函数将变量标记为“volatile”。
这会告诉编译器不要优化或缓存变量,而是必须在内存中查找变量的正确值。
使用volatile函数时需要格外小心,因为这会严重影响性能。
由于编译器无法缓存变量,因此必须每次读取变量时都要从内存中检索其值。
因此,使用volatile函数时,必须仔细考虑是否真的需要使用它来保护变量,以节省CPU时间。
通常,只有在变量存储在多个线程之间共享的内存中时才需要使用volatile函数。
类似地,如果多个线程都可以访问该变量,则必须将其标记为volatile。
例如,某些多任务系统需要实现全局变量,以跨多个线程共享信息。
这种情况下,必须将全局变量标记为volatile。
另一个常见情况是,多个线程都可以访问指向外部设备的指针,这时必须将该指针标记为volatile,以确保多个线程之间不会发生冲突。
此外,一些单片机系统也使用volatile函数,以确保变量在多个线程之间不会发生冲突。
总的来说,Volatile函数是一种修饰符,它可以用于保护共享内存,以便在多个线程之间不会发生意外的结果。
尽管它可以保证变量的一致性,但它也会增加程序处理时间,因此应该根据具体情况仔细考虑是否真的需要使用它。
extern修饰变量或函数时的作用。

extern修饰变量或函数时的作用。
在编程中,extern是一个关键字,用于修饰变量或函数,其作用是告诉编译器该变量或函数是在其他地方定义的,需要在当前文件中引用。
通过使用extern关键字,可以实现多个文件之间的变量或函数共享,提高代码的可维护性和可扩展性。
一、extern修饰变量的作用在C语言中,extern可以用于修饰全局变量,以便在不同的文件中共享该变量。
当某个文件需要使用其他文件中定义的全局变量时,可以使用extern关键字来声明该变量,从而在当前文件中引用其他文件中定义的全局变量。
例如,假设有两个文件a.c和b.c,其中a.c定义了一个全局变量num:```c// a.c文件int num = 10;```如果在b.c文件中需要使用a.c中定义的全局变量num,可以在b.c 中使用extern关键字声明该变量:```c// b.c文件extern int num;```通过使用extern关键字声明num变量,b.c文件就可以使用a.c中定义的全局变量num了。
这样,不同的文件就可以共享同一个全局变量,实现了变量的跨文件共享。
二、extern修饰函数的作用在C语言中,extern还可以用于修饰函数,以便在不同的文件中共享该函数。
当某个文件需要调用其他文件中定义的函数时,可以使用extern关键字来声明该函数,从而在当前文件中引用其他文件中定义的函数。
例如,假设有两个文件a.c和b.c,其中a.c定义了一个函数add:```c// a.c文件int add(int a, int b) {return a + b;}```如果在b.c文件中需要调用a.c中定义的函数add,可以在b.c中使用extern关键字声明该函数:```c// b.c文件extern int add(int a, int b);```通过使用extern关键字声明add函数,b.c文件就可以调用a.c中定义的函数add了。
iar ramfunc原理 -回复

iar ramfunc原理-回复iar ramfunc原理指的是指令编译器iar systems提供的一种特殊函数修饰符,它可以将函数放置在RAM中运行,从而提高程序的执行效率和响应速度。
本文将一步一步解释iar ramfunc原理。
第一步,了解函数修饰符的作用和原理。
函数修饰符是编程语言中的一种语法标记,用于改变函数的行为或特性。
iar systems开发的编译器中,引入了ramfunc这个特殊的函数修饰符。
一般情况下,程序的函数会被编译器放置在程序的ROM(Read-Only Memory,只读存储器)中运行。
但有些函数的执行时间较长,会占用较多的ROM空间,导致程序的执行效率降低。
ramfunc函数修饰符的作用就是将这些函数放置在RAM (Random Access Memory,随机存储器)中运行,从而加快函数的执行速度。
第二步,理解RAM和ROM的区别。
RAM和ROM是计算机存储器中的两种不同类型,具有不同的特点和用途。
RAM是一种易失性存储器,它可以读写数据,并且在电源断开时数据会丢失。
而ROM是一种只读存储器,只能读取数据,不能写入或修改数据,并且在电源断开时数据不会丢失。
由于RAM的读写速度较快,因此将函数放置在RAM中运行可以提高程序的执行效率和响应速度。
第三步,使用ramfunc函数修饰符。
在编译器iar systems的开发环境中,开发人员可以使用ramfunc修饰符来标记需要放置在RAM中运行的函数。
修饰符的使用方法很简单,只需要在函数的声明或定义前加上__ramfunc 关键字即可。
例如:__ramfunc void myFunction(){函数实现}通过这样的方式,编译器会将这个函数放置在RAM中运行,从而提高函数的执行速度。
需要注意的是,由于RAM的空间有限,因此只适合放置一些执行时间较长的函数。
第四步,注意ramfunc函数修饰符的限制。
虽然ramfunc函数修饰符能够提高函数的执行速度,但它也有一些限制。
Wine学习

一、Wine1、wine 实现了大多数的windows API,集成了winedbg2、windows API1)kernel32.dll允许一个W-process作为debugger 去执行另一个W-process,作为debuggee,包括设置breakpoint,单步执行等等2)DBGHELP.DLL让一个debbuger从任意模块查找符号和类型3、异常解决怎么根据下面信息查找crash原因Unhandled exception: page fault on write access to 0x00000000 in 32-bit code (0x0043369e). Register dump:CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006bEIP:0043369e ESP:0b3ee90c EBP:0b3ee938 EFLAGS:00010246( R- -- I Z- -P- )EAX:00000072 EBX:7b8acff4 ECX:00000000 EDX:6f727265ESI:7ba3b37c EDI:7ffa0000Stack dump:0x0b3ee90c: 7b82ced8 00000000 7ba3b348 7b8844010x0b3ee91c: 7b883cdc 00000008 00000000 7bc36e7b0x0b3ee92c: 7b8acff4 7b82ceb9 7b8acff4 0b3eea180x0b3ee93c: 7b82ce82 00000000 00000000 000000000x0b3ee94c: 00000000 0b3ee968 70d7ed7b 70c500000x0b3ee95c: 00000000 0b3eea40 7b87fd40 7b82d0d0Backtrace:=>0 0x0043369e in elementclient (+0x3369e) (0x0b3ee938)1 0x7b82ce82 CONSOLE_SendEventThread+0xe1(pmt=0x0(nil)) [/usr/src/debug/wine-1.5.14/dlls/kernel32/co nsole.c:1989] in kernel32 (0x0b3eea18)2 0x7bc76320 call_thread_func_wrapper+0xb() in ntdll (0x0b3eea28)3 0x7bc7916e call_thread_func+0x7d(entry=0x7b82cda0, arg=0x0(nil), frame=0xb3eeb18) [/usr/src/debug/wi ne-1.5.14/dlls/ntdll/signal_i386.c:2522] in ntdll (0x0b3eeaf8)4 0x7bc762fe RtlRaiseException+0x21() in ntdll (0x0b3eeb18)5 0x7bc7f3da start_thread+0xe9(info=0x7ffa0fb8) [/usr/src/debug/wine-1.5.14/dlls/ntdll/thread.c:408] in ntdl l (0x0b3ef368)6 0xf7597adf start_thread+0xce() in libpthread.so.0 (0x0b3ef468)0x0043369e: movl %edx,0x0(%ecx)Modules:Module Address Debug info Name (143 modules)PE 340000- 3af000 Deferred speedtreertPE 71930000-719b8000 Deferred shdoclcPE 78130000-781cb000 Deferred msvcr80ELF 79afb000-7b800000 Deferred libnvidia-glcore.so.304.51ELF 7b800000-7ba3d000 Dwarf kernel32<elf>\-PE 7b810000-7ba3d000 \ kernel32ELF 7bc00000-7bcd5000 Dwarf ntdll<elf>\-PE 7bc10000-7bcd5000 \ ntdllELF 7bf00000-7bf04000 Deferred <wine-loader>ELF 7c288000-7c400000 Deferred libvorbisenc.so.2PE 7c420000-7c4a7000 Deferred msvcp80ELF 7c56d000-7c5b6000 Deferred dinput<elf>Threads:process tid prio (all id:s are in hex)00000008 (D) C:\Perfect World Entertainment\Perfect World International\element\elementclient.exe 00000031 0 <==00000035 1500000012 000000021 000000045 000000044 000000043 000000038 1500000037 000000036 1500000034 000000033 000000032 000000027 000000009 00000000e services.exe0000000b 000000020 000000017 000000010 00000000f 0下面信息的含义:000d:Call advapi32.RegOpenKeyExW(00000090,7eb94da0 L"Patterns",00000000,00020019,0033f968) ret=7eb 39af8000d:线程idadvapi32: 被调用模块RegOpenKeyExW:被调用函数后面几个都是参数ret :返回地址4、Useful memory address1)32位linux:0x08000000 0x00400000 0x400000002)16位(增强模式):segment:offsetsegment 如果最低三比特位都是1,就是一个selector,如果最低三笔特除了最低位外都是1,可能是全局内存0x1f7 (0x40320000, 0x0000ffff, r-x) :分别是基地址,最大偏移,访问权限r-x 表示可读可执行实际地址:selector基地址+offset3)16位(标准模式):segment:offsetsegment和offset可以是0~0xffff实际地址:segment×16+offset5、配置1)配置debuger[MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug] 957636538 "Auto"=dword:00000001"Debugger"="winedbg %ld %ld"2)配置winedbg[HKCU\\Software\\Wine\\WineDbg]BreakAllThreadsStartup TRUE:所有线程停止FALSE:第一个线程停止BreakOnCritSectTimeOut TRUE:在临界区超时5分钟停止FALSE:不停止BreakOnAttachBreakOnFirstChance一个异常产生两个debug 事件,或者说两次chancefirst chance:发生异常之后传递给debugger,debugger 要么继续执行(cont),要么交给exception handler chain(pass)last chance:如果没有exception handler 处理异常,会再次传递给debugger,这一次不能passTRUE:两次机会都会处理FALSE :仅仅进入last chance AlwaysShowThunk TRUE:根据名称显示所有thunks FALSE:3)+relay 行为配置可能输出会很多,但可以进行设置[HKCU\\Software\\Wine\\Debug]RelayExclude 列出不需要的输出RelayInclude 仅输出列出的输出怎么知道哪些输出是不需要的?WINEDEBUG=+relay wine appname.exe &>relay.logawk -F'(' '{print $1}' < relay.log | awk '{print $2}' | sort | uniq -c | sort二、WineDbg1、WineDbg表达式同C 格式总体相同,有少量差异。
gcc 函数名修饰

gcc 函数名修饰gcc(GNU Compiler Collection)是一个广泛使用的开源编译器套件,在C/C++编程领域有着非常高的知名度和应用率。
在使用gcc编译C/C++代码时,我们经常会遇到一个诸多困扰开发者的问题——函数名修饰。
对于初学者来说,理解函数名修饰的作用和原理是很有必要的。
本文将就此问题进行讲解。
一、什么是函数名修饰函数名修饰,也称为函数名重载,是指在编译阶段,编译器将函数名与参数列表一起编码成一个新的函数名,这种方法称为函数名修饰。
在C++中,函数多态性和函数重载(overloading)特性的实现就与函数名修饰密不可分。
而在C语言中,虽然不支持函数重载,但是编译器仍会对函数名进行修饰。
例如,在C++中,有如下几个函数:```cppvoid func(int a, int b) {}void func(double a, double b) {}void func(char a, char b) {}```这几个函数的函数名都是“func”,但是参数列表却是不同的。
为了能够区分不同的函数,编译器就采用了函数名修饰的方法将这几个函数名进行区分,生成了不同的函数名。
二、为什么要进行函数名修饰在很多编程语言中,函数的名称是可以相同的,但是函数的参数列表必须是不同的,通过函数名修饰可以实现不同名但参数列表相同的函数。
这种技术也是实现C++重载和多态这两个强大特性的核心机制。
从另一个角度来说,由于函数名中包含函数参数信息,所以也会在一定程度上增加代码安全性。
例如,在C++中,我们可以使用重载的方法实现不同类型的加法运算:```cppint add(int a, int b) { return a + b; }double add(double a, double b) { return a + b; }```这就是通过函数名修饰实现重载的一个例子。
三、如何禁用函数名修饰在某些情况下,我们需要禁用函数名修饰,以方便直接使用函数的原名称,如C语言中的一些库函数。
extern“C”有什么作用?

extern“C”有什么作用?Extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。
这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数,加上extren “c”后,C++就能直接调用C函数了。
Extern “C”主要使用正规DLL函数的引用和导出和在C++包含C函数或C头文件时使用。
使用时在前面加上extern “c”关键字即可重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?常考的题目。
从定义上来说:重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
重写:是指子类重新定义复类虚函数的方法。
从实现原理上来说:重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。
如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。
那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。
对于这两个函数的调用,在编译器间就已经确定了,是静态的。
也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!重写:和多态真正相关。
当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。
因此,这样的函数地址是在运行期绑定的(晚绑定)。
编写字符串拷备函数har *strcpy(char *strDest, const char *strSrc){if ( strDest == NULL || strSrc == NULL)return NULL ;if ( strDest == strSrc)return strDest ;char *tempptr = strDest ;while( (*st rDest++ = *strSrc++) != ‘\0’);return tempptr。
constexpr修饰函数

constexpr修饰函数
constexpr是C++11中引入的关键字,用于指示编译器在编译时进行常量表达式求值,以便提高程序的执行效率。
constexpr可以修饰变量、函数以及类成员函数。
在函数上下文中,constexpr可以用于指示函数可以在编译时进行常量表达式求值。
这意味着函数的返回值可以被视为常量,并且可以在编译时进行优化。
要使函数成为constexpr函数,必须满足以下条件:
1. 函数的参数和返回值类型必须是字面类型,即内置类型或者常量表达式构造的数组和结构体类型。
2. 函数体中只能包含一些基本的计算操作或者其他constexpr函数调用,不能有控制流语句(比如if、switch等)和循环语句(比如for、while等)。
3. 函数体中不能使用动态内存分配操作(比如new、delete等)。
constexpr函数用法示例:
c++
constexpr int factorial(int n) {
return (n == 0) ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5); 编译时求解
在上面的示例中,factorial函数被声明为constexpr函数,可以在编译时求解,将结果赋值给result变量。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
函数名字修饰(Decorated Name)方式函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。
LINK程序或其他工具有时需要指定函数的名字修饰来定位函数的正确位置。
多数情况下程序员并不需要知道函数的名字修饰,LINK程序或其他工具会自动区分他们。
当然,在某些情况下需要指定函数的名字修饰,例如在C++程序中,为了让LINK 程序或其他工具能够匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。
另一种需要指定函数的名字修饰的情况是在汇编程序中调用C或C++的函数。
如果函数名字,调用约定,返回值类型或函数参数有任何改变,原来的名字修饰就不再有效,必须指定新的名字修饰。
C和C++程序的函数在内部使用不同的名字修饰方式,下面将分别介绍这两种方式。
1. C编译器的函数名修饰规则对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,例如_functionname@number。
__cdecl调用约定仅在输出函数名前加上一个下划线前缀,例如_functionname。
__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,例如@functionname@number2. C++编译器的函数名修饰规则C++的函数名修饰规则有些复杂,但是信息更充分,通过分析修饰名不仅能够知道函数的调用方式,返回值类型,参数个数甚至参数类型。
不管__cdecl,__fastcall还是__stdcall 调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字,再后面是参数表的开始标识和按照参数类型代号拼出的参数表。
对于__stdcall方式,参数表的开始标识是“@@YG”,对于__cdecl方式则是“@@Y A”,对于__fastcall方式则是“@@YI”。
参数表的拼写代号如下所示:X--voidD--charE--unsigned charF--shortH--intI--unsigned intJ--longK--unsigned long(DWORD)M--floatN--double_N--boolU--struct....指针的方式有些特别,用PA表示指针,用PB表示const类型的指针。
后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复。
U表示结构类型,通常后跟结构体的类型名,用“@@”表示结构类型名的结束。
函数的返回值不作特殊处理,它的描述方式和函数参数一样,紧跟着参数表的开始标志,也就是说,函数参数表的第一项实际上是表示函数的返回值类型。
参数表后以“@Z”标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。
下面举两个例子,假如有以下函数声明:int Function1 (char *var1,unsigned long);其函数修饰名为“?Function1@@YG H PA D K@Z”,而对于函数声明:void Function2();其函数修饰名则为“?Function2@@YGXXZ”。
对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字符引导的类名;其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是“@@IAE”,私有(private)成员函数的标识是“@@AAE”,如果函数声明使用了const关键字,则相应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。
如果参数类型是类实例的引用,则使用“AA V1”,对于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”。
无论是C函数名修饰方式还是C++函数名修饰方式均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。
3.查看函数的名字修饰有两种方式可以检查你的程序中的函数的名字修饰:使用编译输出列表或使用Dumpbin 工具。
使用/FAc,/FAs或/FAcs命令行参数可以让编译器输出函数或变量名字列表。
使用dumpbin.exe /SYMBOLS命令也可以获得obj文件或lib文件中的函数或变量名字列表。
此外,还可以使用undname.exe 将修饰名转换为未修饰形式。
函数调用约定和名字修饰规则不匹配引起的常见问题函数调用时如果出现堆栈异常,十有八九是由于函数调用约定不匹配引起的。
比如动态链接库a有以下导出函数:long MakeFun(long lFun);动态库生成的时候采用的函数调用约定是__stdcall,所以编译生成的a.dll中函数MakeFun 的调用约定是_stdcall,也就是函数调用时参数从右向左入栈,函数返回时自己还原堆栈。
现在某个程序模块b要引用a中的MakeFun,b和a一样使用C++方式编译,只是b模块的函数调用方式是__cdecl,由于b包含了a提供的头文件中MakeFun函数声明,所以MakeFun 在b模块中被其它调用MakeFun的函数认为是__cdecl调用方式,b模块中的这些函数在调用完MakeFun当然要帮着恢复堆栈啦,可是MakeFun已经在结束时自己恢复了堆栈,b模块中的函数这样多此一举就引起了栈指针错误,从而引发堆栈异常。
宏观上的现象就是函数调用没有问题(因为参数传递顺序是一样的),MakeFun也完成了自己的功能,只是函数返回后引发错误。
解决的方法也很简单,只要保证两个模块的在编译时设置相同的函数调用约定就行了。
在了解了函数调用约定和函数的名修饰规则之后,再来看在C++程序中使用C语言编译的库时经常出现的LNK 2001错误就很简单了。
还以上面例子的两个模块为例,这一次两个模块在编译的时候都采用__stdcall调用约定,但是a.dll使用C语言的语法编译的(C语言方式),所以a.dll的载入库a.lib中MakeFun函数的名字修饰就是“_MakeFun@4”。
b包含了a提供的头文件中MakeFun函数声明,但是由于b采用的是C++语言编译,所以MakeFun 在b模块中被按照C++的名字修饰规则命名为“?MakeFun@@YGJJ@Z”,编译过程相安无事,链接程序时c++的链接器就到a.lib中去找“?MakeFun@@YGJJ@Z”,但是a.lib中只有“_MakeFun@4”,没有“?MakeFun@@YGJJ@Z”,于是链接器就报告:error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z解决的方法和简单,就是要让b模块知道这个函数是C语言编译的,extern "C"可以做到这一点。
一个采用C语言编译的库应该考虑到使用这个库的程序可能是C++程序(使用C++编译器),所以在设计头文件时应该注意这一点。
通常应该这样声明头文件:#ifdef _cplusplusextern "C" {#endiflong MakeFun(long lFun);#ifdef _cplusplus}#endif这样C++的编译器就知道MakeFun的修饰名是“_MakeFun@4”,就不会有链接错误了。
许多人不明白,为什么我使用的编译器都是VC的编译器还会产生“error LNK2001”错误?其实,VC的编译器会根据源文件的扩展名选择编译方式,如果文件的扩展名是“.C”,编译器会采用C的语法编译,如果扩展名是“.cpp”,编译器会使用C++的语法编译程序,所以,最好的方法就是使用extern "C"。
1.__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”2 __cdecl调用约定:规则同上面的_stdcall 调用约定,只是参数表的开始标识由上面的“@@YG”变为“@@Y A”。