搜索
查看: 8352|回复: 29
打印 上一主题 下一主题

谣言终结(伪):使用Sleep就一定要添加windows.h吗?

[复制链接]
跳转到指定楼层
楼主
发表于 2013-6-21 00:48:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 rosynirvana 于 2013-6-21 00:53 编辑

这是从啊哈C十万个为什么中挑选出来的问题,实用性比前面一篇好多了。

和上一篇一样,刚入门的站友,看第一部分就好了。

下面的代码在ahaC 2.0中明显是无法编译的
  1. #include <stdio.h>

  2. int main()
  3. {
  4.         puts("Hello World!");
  5.         sleep(1);
  6.         return 0;
  7. }
复制代码
论坛上对于这个问题最常见的回答是
sleep首字母大写,并且加一句#include <windows.h>

这当然是可行的,但是有没有考虑过,为什么会导致无法编译呢?

下面先说一种最为直接(也是最正确的)的解决方法
  1. #include <stdio.h>
  2. void __stdcall Sleep(unsigned long);

  3. int main()
  4. {
  5.         puts("Hello World!");
  6.         Sleep(1);
  7.         return 0;
  8. }
复制代码
Sleep首字母大写是当然的,C语言对于大小写敏感——sleep和Sleep是两个不同的函数。但是只做这个改动还是无法编译——所以另外加了一句
  1. void __stdcall Sleep(unsigned long);
复制代码
这个看上去非常奇怪的东西。

其实,这只是一个函数原型。但不是标准C的函数原型,在ISO C中,没有__stdcall这种东西。
__stdcall事实上是一种calling convention,决定生成函数调用的汇编代码的一些细节问题。

为了让这段代码运行起来,最重要的就是这个__stdcall了,你可以试试把这句替换成下面几句中的一句:
  1. __stdcall Sleep(int);
  2. void __stdcall Sleep(int);
  3. __stdcall Sleep(float);
复制代码
都是可以运行的(但是你可能发现第3句暂停时间非常长)

为什么要加了这个calling convention才能正常编译呢?因为它会影响汇编代码的生成。
如果没有这句声明函数原型,汇编代码是
  1. call        _Sleep
复制代码
如果加上这句声明,汇编代码是
  1. call        _Sleep@4
复制代码
你可能已经明白了,函数在运行库内部的名字是不同的,所以不声明函数原型的情况下,会造成一个链接期的错误。

_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);
也就是本文一开始添加的那句声明。

评分

参与人数 3啊哈币 +11 收起 理由
code004 + 3 很给力!
创世菌 + 3 很给力!
啊哈磊 + 5 赞一个!

查看全部评分

推荐
发表于 2013-10-14 11:01:49 | 只看该作者
霁涟熙 发表于 2013-6-21 09:12
膜拜。另外小白的问一下,是不是一定要大写S能,有没有办法用sleep也可以呢?

_sleep.......
推荐
 楼主| 发表于 2013-10-5 20:22:19 | 只看该作者
wkl 发表于 2013-10-5 16:59
mingw中应该没有windows.h这个库吧,为什么加上include 这个库后,就可以
你说    “因为windows.h会将Sle ...

windows.h不是一个库啊,只是一个头文件而已
通常的mingw安装包包含了一个叫win32api的部分,这个部分里面包含了windows.h这个头文件
所以说windows.h可以说是mingw的一个可选组件
啊哈C只是mingw的一个图形前端而已

我想应该解释清除了吧
推荐
发表于 2013-10-5 16:59:49 | 只看该作者
mingw中应该没有windows.h这个库吧,为什么加上include <windows.h>这个库后,就可以
你说    “因为windows.h会将Sleep的原型声明拷贝到你的源码文件中去”

这个windows.h是啊哈C自带的(而不是mingw自带)的吗?
谢谢!
推荐
 楼主| 发表于 2013-8-28 13:23:24 | 只看该作者
Cousin 发表于 2013-8-27 21:35
你让unistd.h情何以堪

啊哈C又不能运行在unix环境上
请先搞清楚哪些是标准库
沙发
发表于 2013-6-21 09:11:27 | 只看该作者
楼主神牛啊。
板凳
发表于 2013-6-21 09:12:11 | 只看该作者
膜拜。另外小白的问一下,是不是一定要大写S能,有没有办法用sleep也可以呢?
地板
发表于 2013-6-21 09:20:42 | 只看该作者
哇塞 楼主研究的好深入啊
5#
 楼主| 发表于 2013-6-21 11:43:14 | 只看该作者
霁涟熙 发表于 2013-6-21 09:12
膜拜。另外小白的问一下,是不是一定要大写S能,有没有办法用sleep也可以呢?

可以自己利用Sleep写一个函数或者宏

void sleep(unsigned long x)
{
  Sleep(x);
}

就可以了

点评

#define sleep Sleep//宏定义  发表于 2018-1-30 18:16
6#
发表于 2013-6-21 12:41:11 | 只看该作者
楼主好厉害,膜拜一下
7#
发表于 2013-6-26 20:29:00 | 只看该作者
不错啊!!!!
8#
发表于 2013-6-26 20:29:20 | 只看该作者
好厉害!!!
9#
发表于 2013-7-12 16:25:40 | 只看该作者
LZ的确很强大!
10#
发表于 2013-7-18 16:02:56 | 只看该作者
很好很强大
11#
发表于 2013-8-27 21:35:44 | 只看该作者
你让unistd.h情何以堪
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

广播台
特别关注
快速回复 返回顶部 返回列表