|
本帖最后由 rosynirvana 于 2013-6-21 00:53 编辑
这是从啊哈C十万个为什么中挑选出来的问题,实用性比前面一篇好多了。
和上一篇一样,刚入门的站友,看第一部分就好了。
下面的代码在ahaC 2.0中明显是无法编译的- #include <stdio.h>
- int main()
- {
- puts("Hello World!");
- sleep(1);
- return 0;
- }
复制代码 论坛上对于这个问题最常见的回答是
sleep首字母大写,并且加一句#include <windows.h>
这当然是可行的,但是有没有考虑过,为什么会导致无法编译呢?
下面先说一种最为直接(也是最正确的)的解决方法- #include <stdio.h>
- void __stdcall Sleep(unsigned long);
- int main()
- {
- puts("Hello World!");
- Sleep(1);
- return 0;
- }
复制代码 Sleep首字母大写是当然的,C语言对于大小写敏感——sleep和Sleep是两个不同的函数。但是只做这个改动还是无法编译——所以另外加了一句- void __stdcall Sleep(unsigned long);
复制代码 这个看上去非常奇怪的东西。
其实,这只是一个函数原型。但不是标准C的函数原型,在ISO C中,没有__stdcall这种东西。
__stdcall事实上是一种calling convention,决定生成函数调用的汇编代码的一些细节问题。
为了让这段代码运行起来,最重要的就是这个__stdcall了,你可以试试把这句替换成下面几句中的一句:- __stdcall Sleep(int);
- void __stdcall Sleep(int);
- __stdcall Sleep(float);
复制代码 都是可以运行的(但是你可能发现第3句暂停时间非常长)
为什么要加了这个calling convention才能正常编译呢?因为它会影响汇编代码的生成。
如果没有这句声明函数原型,汇编代码是如果加上这句声明,汇编代码是你可能已经明白了,函数在运行库内部的名字是不同的,所以不声明函数原型的情况下,会造成一个链接期的错误。
_Sleep@4这个符号,在链接期会被解析到libkernel32.a中,然后再进行符号重定位,运行时调用kernel32.dll中相应的二进制代码。
最后需要说明的是,为什么#include <windows.h>也能解决这个问题呢?
因为windows.h会将Sleep的原型声明拷贝到你的源码文件中去,Sleep这个函数最为正规的原型是
void WINAPI Sleep(DWORD);
其中WINAPI和DWORD是宏,在mingw预处理的时候分别被替换成 __stdcall 和unsigned long:
void __stdcall Sleep(unsigned long);
也就是本文一开始添加的那句声明。 |
评分
-
查看全部评分
|