2005年十一月月 发布的文章

童心未泯

在sina“北京奥运吉祥物”发布专题中,发现吉祥物评审委员会有位评委是个童话故事家,叫“郑渊洁”,这个名字听起来很熟悉,在网上搜索后才知道,原来小时侯最喜欢看的《舒克和贝塔》就出自这位作家之手,心中顿时涌动着对其之敬仰之情,遂下载了一本“待补充”的《郑渊洁童话全集》开始拜读。

虽已工作一载有余,但仍自认为“童心未泯”,暂且忘记工作的压力、生活上的不如意,进入郑叔叔的童话世界中,这简直就是一种享受。由于郑叔叔的童话作品数量庞大,遂只挑选了郑渊洁风靡童话世界的几部脍炙人口之作如《舒克和贝塔》、《大灰狼罗克》以及《皮皮鲁和鲁西西》系列。

童话,原本就是为孩子们准备的丰盛大餐,其特点是故事情节简单,文字通俗易懂并与蕴含着无穷的快乐元素和教育意义。对于我这样的成年选手,读起童话来自然效率很高。

《舒克与贝塔》故事情节较连续,篇幅也不小,而且童话中的主角是两只拥有活泼可爱的形象和正直无畏的品格的小老鼠,在我看来,舒克和贝塔在中国小朋友(和我同龄的80年代的那几代)的心目中的地位丝毫不亚于美国的那个米奇老鼠。看了《郑渊洁童话全集》中的“舒克与贝塔”后感觉自己小时侯好像并没有看全整部童话,这次正好弥补一下这个缺憾。

对《大灰狼罗克》这部作品,我并不是非常的熟悉,隐隐约约好像记得这部作品是在前几年被搬上了荧屏的。罗克是一名“热心肠”的好大灰狼,在第一集中就看得出来。《大灰狼罗克》这部作品章节相对独立,而且其照比《舒克与贝塔》显得渺小的多。读完罗克的故事感觉有些情节是影射某些社会现实的,这样自然少了些童话故事的色彩,总体感觉不如《舒克与贝塔》。

至于《皮皮鲁和鲁西西》系列,情节趋于科幻,也许是作者在创作的那个年代受到了当时“科幻”风的影响。记得小时候有一段时间荧幕上连续的上演《恐龙特急克塞号》、《变形金刚》、《霹雳贝贝》、《小龙人》等经典科幻作品。皮皮鲁身上有着现在孩子身上叛逆的性格,而且陶气、聪明和果敢,我想也正是这些才可以吸引儿童们的眼球,皮皮鲁和鲁西西系列是相对较晚的作品,所以自己关注的比较少。

一口气说了这么一丁儿点自己内心的童话心声:),感觉孩提时代的我们比现在的孩子们要幸福,有那么多经典的影视作品供我们选择去看,现在的儿童只能饱受那些垃圾作品的摧残,可怜!呵呵一家之言而已!^_^

汇编之路-栈操作与栈帧

结构化程序的一个最基本的单元就是“函数”或者叫“过程”。在汇编这一层自然也相应的有支持这些概念的指令操作,如栈操作和栈帧的概念。

首先这里要为“打开汇编之门”那篇blog补充一点的是:汇编语言是与机器相关,这里的一切都是基于IA-32机器平台的。

1、寻址方式
我们已经知道在操作数表示中有一种是用来指示内存地址的内容的,在GNU Assembly中指示内存地址有多种方式,这些方式被统称“寻址方式”。通用的寻址格式为:“Imm(Eb, Ei, s)”[1]。解释一下:该表达式的计算方式为Imm + R[Eb] + R[Ei] * s,这一串的结果是什么呢?是一个存储器的地址,操作指令通过该操作数表达式计算出来的内存地址来访问内存。

由通用形式演化几种常见特殊形式如下:
1) Imm – 注意与$Imm区别,后者为立即数,而前者是以立即数形式承载的一个内存地址,这种方式叫绝对寻址;
2) (Ex) – 注意与Ex区别,后者为寄存器内容,而前者是以寄存器内容形式承载的一个内存地址,这种方式叫间接寻址;
3) Imm(Eb) – 其表示结果是内存地址为Imm + R[Eb];
4) (Eb, Ei) – 其表示结果是内存地址为R[Eb] + R[Ei];
5) Imm((Eb, Ei) – 其表示结果是内存地址为Imm + R[Eb] + R[Ei]。

2、寄存器使用
在“打开汇编之门”中曾经提过虽然寄存器的专用性已经降低,但是某些寄存器还是有其专用场合的。GNU为我们制定了一个寄存器使用规则,规则规定:“%eax、%ecx和%edx是由调用者负责存储的,而%ebx、%ebi和%esi则由被调用者保护,而%esp和%ebp都是栈操作专用的”。

3、栈操作
栈,实际上是一块儿专用的内存区域,每个进程地址空间都有其专有的栈区。地球人都知道关于栈有两种操作:Push和Pop。相应的GNU Assembly分别定义了“pushl S”和“popl D”分别来完成压栈和出栈操作。每个操作都包含两个步骤:移动栈顶指针和数据传送。
pushl S R[%esp] <– R[%esp] – 4 ;M[R[%esp]]<– S
popl D   D <– M[R[%esp]];R[%esp] <– R[%esp] + 4

4、栈帧的形成
提到函数或者过程调用就不能离开栈操作。而每个函数或者过程调用也都离不开一个叫“栈帧”的概念。栈是用来传递参数、保存返回结果等作用的,而栈帧则是1对1映射到某个过程调用的。栈帧由%ebp来标识。我们来看看一个例子,通过该例子看看栈帧里到底有些什么东西?
void callee(int x, int y) {
 x = 1;
 y = 2;
}

void caller(int m, int n) {
 callee(m, n);
}

翻译为汇编代码为:
_callee:
 pushl %ebp   //保存调用者的栈帧地址
 movl %esp, %ebp  //初始化callee栈帧地址
 movl $1, 8(%ebp)  //获取参数x信息
 movl $2, 12(%ebp)  //获取参数y信息
 popl %ebp
 ret
… …
… …
_caller:
 pushl %ebp   //保存调用者的栈帧地址
 movl %esp, %ebp  //初始化caller栈帧地址
 subl $8, %esp  
 movl 12(%ebp), %eax  
 movl %eax, 4(%esp)
 movl 8(%ebp), %eax
 movl %eax, (%esp)
 call _callee
 leave
 ret
看看callee的汇编码:进入callee后首先保存其调用者caller的栈帧地址,然后读取其调用者caller栈帧中的参数信息进行计算。可以看出一个过程的栈帧中起码包括其上一个栈帧的起始地址,然后是一些参数信息,按照CS.APP说法,栈帧在存储参数信息之前还有可能保存一些本地变量或临时变量等。在每个过程的栈帧的结尾处都记录着过程返回地址,这个返回地址是由call执行时自动加入的。callee都是通过%ebp +/- 偏移量来获取参数信息的。用下面的图可以小结一下栈帧的模样(起始:%ebp所指的字节–> 终止:返回地址所在字节):

+              +
 |               |
+———-+
| old %ebp | <— %ebp
+———-+
| 本地变量 |
+———-+
|   参数n  |
+———-+
|   参数…|
+———-+
|   参数1  |
+———-+
| 返回地址 |
+———-+
|     …        |
|                |<– %esp

[注1]
这里采用了CSAPP中的表示方法,Eb表示基址寄存器,Ei表示变址寄存器,s为伸缩因子。我们使用R来表示引用某个寄存器的值,使用M来表示引用某内存地址。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats