http://coltkwong.com/blogs/kenlin/posts/475.aspx(略有删节,请参看原文)
对李怡青先生的逝世表示哀悼,同时,提醒大家注意身体健康
Just received an email from Taiwan MVP Committee,
親愛的MVP們,
我們十分遺憾地通知您,第二屆第一回合台灣區微軟「最有價值專家」李怡青先生已
於10月26日因糖尿病病逝,享年31歲,台灣微軟技術支援處在此特別感謝李怡青先生
對台灣微軟技術社群所做的諸多貢獻並致上無限哀悼.!
李怡青當選微軟「最有價值專家」(MVP)以來,一直致力於服務廣大技術社群的使用者及協助解答微軟社群支援網站的許多技術的問題, 更十分
積極參與許多微軟『線上專家對談』,許多社群使用者皆對 李怡青先生的專業及熱忱
十分讚賞及敬佩!
天嫉英才!我們對李怡青先生留下無限的懷念及感謝!
李怡青先生 (Yi-Ching Lee):
佳和資訊工作室 CTO 興趣:任何與電腦相關, 電影,撞球
微軟技術專長:網站程式開發應用ASP、Windows、FrontPage
以下是李怡青先當選微軟「最有價值專家」(MVP)感言:
當選微軟「最有價值專家」(MVP)是肯定自己幫助他人對解決使用微軟相關產品
問題之能力,你是否也發現,世界的距離因電腦而縮短,時間的耗費也因電腦而減
少,我認為這一切幾乎都是因為微軟才漸漸開始的,我和多數人一樣,喜歡沉浸在
網路的世界裡,也選擇與網路有關的工作;之前接案設計網站,現在開發Office
FrontPage Add-ins 程式增益集,因著微軟相關產品軟體版本不斷更新,所以相關技術
也必須跟著提升,現正努力取得微軟認證MCP+SB,未來計劃再取得微軟認證MCSD!
默哀~
才31岁啊,唉。
老天有时真的是不公平唉.坏人老是长命百岁.贪官一个不死.
而忙碌的好人总是不长命……..
默哀~
提醒大家注意身体健康
其实就是"人有旦夕福祸"!
宋美玲活到106!好恐怖呀!
时时刻刻注意身体健康!
是電腦工作的都有危險哦,我想還是及早退出比較好
:(
我家里没有你的信箱,所以发在这里,希望不会引起别人注意。
你的病例我做好了,这个问题非常深,我没有信心让人懂。
因此,先那你做试验 🙂
你仔细地看2遍先,再想一想。如果不懂,就扔下,有空了再回来看看想想。如果两天之后仍然不懂,那么请告诉我具体哪里不懂,
我好像办法。不好意思阿,呵呵。
请用等宽字体,比如notepad缺胜字体,来看。
—————————————————————————
病例:不理解局部变量超出作用域之后的行为
病人:医生,局部变量超出作用域之后会发生什么事?我为此头疼了很久。
中医:哦,它们不能被访问了,消亡了。你的病不会这么简单吧,到底什么问题,详细描述一下。
病人:我想知道的是,指针所指的局部变量,超出作用域之后,那个指针的行为。比如这段程序
#include "stdio.h"
int main(int argc, char* argv[])
{
int i=10;
int *piToTest=&i;
printf("%dn",*piToTest);
{
int iGone=20;
piToTest=&iGone; // <—咔咔
}
printf("%dn",*piToTest); // <—啊啊
return 0;
}
////////////////////////////////////////////////////////////////
//// Figure 1.
////////////////////////////////////////////////////////////////
运行结果是打出“20”,正是*piToTest生前的值,难道iGone其实没有消亡,永远活在我们心中?
中医:嗯,你要这么说的话,iGone“生前”究竟活在什么地方呢?用术语来说,其storage在何处?
病人:哦哦,不知道。
中医:这就对了,你的问题的本质正是局部变量的storage问题,所以你会头疼。像往常一样,这个问题先搁置一下。
你也不知道函数传递参数的机制对么?
病人:对。
中医:我们得从这里着手。每次函数调用,都要有一块内存存放其参数(不妨假设所有函数都有一些参数),
而编译时刻无从知道某个函数究竟会被递归调用几次,要为他留多少份参数的空间,对不对?
病人:。。。 对,即使是有一个main函数,也不妨碍他调用自己n次m层。放参数的地方,必定是一个很“动态”的地方。
中医:这个很动态的地方,术语叫堆栈。其运作机制和数据结构中的堆栈一样,都是先进后出,不过这里的堆栈是由CPU和OS来实现的,
每一个线程,都有一个自己的堆栈。堆栈不单很动态,还很静态,任凭进程中其他线程怎么折腾,对于某个线程来说,他自己的
线程都是他神圣不可侵犯的私有财产,不受其他线程影响,否则就会有灾难性后果。这点和本篇关系不大,你了解一下先,对于
你理解线程的本质大有好处。
话转回来,函数调用的时候,①把其参数push进堆栈,②把返回地址push进堆栈,③跳转到函数入口地址。
void func1(int a,int b);
…
func1(10,20);
…
┌——————–┐
│返回地址 │
├——————–┤
│左面的参数 — 10 │
├——————–┤
│最右面的参数 — 20 │
├——————–┤
│之前的堆栈 │
////////////////////////////////////////////////////////////////
//// Figure 2. 进入func1时刻的堆栈情况(假定堆栈向上生长)
//// 参数自右向左入栈,最后是返回地址
////////////////////////////////////////////////////////////////
现在,你说说函数返回的时候,应该发生什么事。
病人:想来是调用的逆序列吧。(1)取得返回地址跳转回去,(2)堆栈恢复成“之前的堆栈”
中医:很好,请记住(1)是callee做的事,而(2)是caller的责任。看完病之后,想一下printf之类不定个数参数函数的机制,
来理解这样做的必要性。不过现在我们得整理一下函数调用的过程。
①caller 把其参数push进堆栈
②caller 把返回地址push进堆栈
③跳转到函数入口地址。
④callee的函数体被执行
⑤callee取得返回地址跳转回去
⑥caller把堆栈恢复成“之前的堆栈”
////////////////////////////////////////////////////////////////
//// Figure 3. 函数调用的过程
////////////////////////////////////////////////////////////////
病人:嗯,这些我理解了。这和我的病根“局部变量的storage问题”之间的关系是–?
中医:真的理解了么?这里面还隐含了一个推论,callee必定可以取得返回地址。
病人:这还用推,不就在栈顶么?
中医:嘿嘿,你这句话也隐含了一个推论,函数体内,除了调用函数这样的“堆栈平衡的操作”,堆栈不生长,
不然返回地址不会在栈顶让你唾手可得。
病人:难道不是? 。。。
啊!
莫非函数体内把局部变量给放进了堆栈?局部变量的作用域和函数的参数非常接近,他的storage也应该是堆栈吧!
中医:能悟出这点,强。我们来把图3中的函数体再细化一下。
④.1 把局部变量放进堆栈(函数内有多少局部变量编译器很清楚)
④.2 函数代码
④.3 把堆栈恢复成“进入函数时的堆栈”(姑且认为是.1的逆操作)
////////////////////////////////////////////////////////////////
//// Figure 4. 函数体细化
////////////////////////////////////////////////////////////////
┌——————–┐
│ │
│各局部变量 │
│ │
├——————–┤
│返回地址 │
├——————–┤
│左面的参数 — 10 │
├——————–┤
│最右面的参数 — 20 │
├——————–┤
│之前的堆栈 │
////////////////////////////////////////////////////////////////
//// Figure 5. 局部变量给放进了堆栈(函数内有多少局部变量编译器很清楚)
////////////////////////////////////////////////////////////////
这样,返回时的确可以在栈顶取到返回地址。顺便,你说说函数代码中如何定位某个具体参数?
病人:这下总可以根据栈顶了吧!编译器为每一个局部变量定下一个该函数内栈顶的偏移量,
函数代码中就根据当时的栈顶和那个偏移量来确定每个变量。
等等!
我的病,我想想。。。
┌——————–┐
│iGone=20 │
│piToTest │
│i=10 │
├——————–┤
│返回地址 │
├——————–┤
│左面的参数 — 10 │
├——————–┤
│最右面的参数 — 20 │
├——————–┤
│之前的堆栈 │
////////////////////////////////////////////////////////////////
//// Figure 6. 运行到“咔咔”时,堆栈的概貌。
////////////////////////////////////////////////////////////////
由于C++编译器,只对类和结构产生析构函数,
在简单类型变量消亡时不对它们做清理操作(如果一个int也要配上西沟函数,那C++还能有效率么),
所以iGone消亡后,他的空间还在
回信地址:[email protected]
真是感叹英年早逝!天妒英才啊!
每每看到programer的辛勤写作,甚至于腰脊疼痛,手指颤动,
都感慨颇多,联想到奋斗在windows的不同战线、不同论坛上
的mvp们,常常牺牲自己的休息时间,义务的为大家服务,
一个又一个的热情的面孔浮现眼帘;
每每仰望星空之时,mvp们的一言一语,一语争执、一言谅解,
都宛如这浩瀚天幕的烁烁繁星;
今息李怡青先生的逝世,不禁扼腕怅然,愿他的精神能够永远
激励我们,我们也会在这方祝福他,愿他安息!
test