汇编揭秘C中的参数传递

文章作者:怕冷的北极熊

  很多学习汇编的朋友想必对C也比较了解,因为在当前大学的课程体系里,它很有可能就是你接触到的第一门编程语言。由于对计算机的理解不够,学习时必定会遇到各种问题。有些问题是你通过思考就可以解决的,而更多的问题则是你无从思考,就好像它天生就是这样,你只要记住就OK了。然而这样的学习方式是机械的,更是没有创造力的。只有真正理解了C语言,你才有能力去驾驭它,否则它和你之间永远会隔着一层窗户纸,虽然很薄,但是你永远也捅不透。这是为什么呢?其实道理很简单,就好比在一个公司有现成的代码库可以调用,有的程序员遇到问题时,他唯一可作的就是调用代码库中的功能模块,完事后就万事大吉。而有的程序员则是只要有时间宁可自己实现。即使没有时间,调用完代码库中的功能模块,他还会想,如果是自己,这个功能应该如何实现,代码库中的模块是否有不妥之处,进而对其功能不断进行改进和完善。这可能就是专业和非专业的重要区别。而那些不善于思考的程序员,将来很有可能就会成为我们眼中的“代码工人”。

  那么如何才能真正理解C语言呢?答案就是汇编。汇编指令是机器指令的助记符表示,任何高级语言要想被计算机执行,都必须转化为一条条的机器指令,而它又与汇编指令一一对应,通过分析汇编指令,就能真正理解C语言在计算机中的运行机理,只有这样才算真正掌握了C语言,然而如何通过汇编指令分析C语言,很多朋友还不是很熟悉,或者根本就不知道。在有些人的脑子里,C语言是直接被CPU执行的,根本就不会想到还有汇编这一层。而对汇编只是懂点皮毛的,此时也只能是心有余而力不足。在此,我就用汇编语言来揭开C语言参数传递的真正面纱。首先我们来写一个最简单的C语言源程序t.c如下:
main(){}
然后我们在Turboc集成开发环境下生成可执行文件t.exe,接着我们用debug命令加载此文件,查看里面的汇编代码后发现
C:\c>debug t.exe
-u
0C1C:0000 BA720C      MOV  DX,0C72
0C1C:0003 2E             CS:
0C1C:0004 8916F801   MOV  [01F8],DX
0C1C:0008 B430          MOV  AH,30
0C1C:000A CD21         INT  21
...
  看后一头雾水,虽然我们知道每条指令所代表的具体含义,然而却不明白把t.c编译成此汇编代码的真正缘由。其实这是Turboc集成开发环境在作怪。C源程序要想生成exe文件,同样也要经历编译和链接两个阶段。而在Turboc下对应的编译器和链接器分别为Turboc根目录下的Tcc.exe和Tlink.exe。如果像我们所学汇编那样经过 tcc -c t.c(这里加入-c参数是要求只编译,否则它会自动调用链接程序),然后tlink t.obj,同样会生成t.exe,只是运行时不能正确返回,而Turboc集成开发环境为我们解决了这个问题。既然问题解决了,必然要加入相应的功能代码,因此程序也就不容易读懂了。不过没关系,其实C语言中的函数调用就相当于汇编中call指令,而函数名则代表了功能函数在内存中的偏移地址。我们只要把函数名的值以十六进制形式打印一下就可以了。在C语言中,以十六进形式显示标记为%x。代码t.c如下:
main(){ printf("%x",main); }
执行后显示的结果就是main()函数在内存中的偏移地址。我的电脑打印的结果为1FA,因此我们用debug加载此程序后,通过u命令u 1fa得到的结果如下: 
-u 1fa
0C1C:01FA B8FA01     MOV     AX,01FA
0C1C:01FD 50            PUSH    AX
0C1C:01FE B89401     MOV     AX,0194
0C1C:0201 50            PUSH    AX
0C1C:0202 E8B708     CALL    0ABC
0C1C:0205 59            POP     CX
0C1C:0206 59            POP     CX
0C1C:0207 C3            RET
这里有人可能会问到两个问题:
1:第一次打印的是1fa,就能保证第二次加载也是在1fa位置吗?
2:以上汇编指令怎么还是很难看懂呢?
其实对于问题1,多试验几次后你会发现,每次打印的结果都会一样。这和操作系统的内存管理有关,我们只要记住具体方法就可以了,因为我们当前的问题是要通过汇编语言来分析C语言的参数传递,与此问题无关的问题不便过多讨论。
对于问题2,真正的原因在于我们调用了库函数printf()所致。由于我们不知道此函数的具体实现,因此也无法理解
0C1C:01FA B8FA01     MOV     AX,01FA
0C1C:01FD 50            PUSH    AX
0C1C:01FE B89401     MOV     AX,0194
0C1C:0201 50            PUSH    AX
四条指令的的具体原因。分析问题要从最简单的开始入手,因此我们需要写一个能够说明问题的最简函数,尽量不去调用库函数。我的t.c如下:
int add(int,int);
main()
{
      int a;
      int b;
      int c;
      a=4;
      b=5;
      c=add(a,b);
}
int add(int a,int b)
{
      return a+b; 
}
它包括两个函数,主函数和相加函数。我们在Turboc集成开发环境下通过熟悉的快捷键F9就会生成t.exe。我们上面提到,main函数的在内存中的偏移地址为1fa(我的机子上的结果是1fa,不同的机子上可能是其它值),然后我们通过debug t.exe把程序加载到内存,通过u 1fa直接跳转到main()函数的起始位置,查看其对应的汇编代码如下:
C:\c>debug t2.exe
-u 1fa ------------------------------------------>以下对应main()
0C1C:01FA 55            PUSH    BP                     
0C1C:01FB 8BEC        MOV     BP,SP
0C1C:01FD 83EC02    SUB     SP,+02
0C1C:0200 56            PUSH    SI
0C1C:0201 57            PUSH    DI
0C1C:0202 BE0400     MOV     SI,0004
0C1C:0205 BF0500     MOV     DI,0005
0C1C:0208 57            PUSH    DI
0C1C:0209 56            PUSH    SI
0C1C:020A E80B00    CALL    0218
0C1C:020D 59            POP     CX
0C1C:020E 59            POP     CX
0C1C:020F 8946FE     MOV     [BP-02],AX
0C1C:0212 5F            POP     DI
0C1C:0213 5E            POP     SI
0C1C:0214 8BE5        MOV     SP,BP
0C1C:0216 5D            POP     BP
0C1C:0217 C3            RET
-u --------------------------------------------->以下对应int add(int a,int b)
0C1C:0218 55            PUSH    BP
0C1C:0219 8BEC        MOV     BP,SP
0C1C:021B 8B4604     MOV     AX,[BP+04]
0C1C:021E 034606     ADD     AX,[BP+06]
0C1C:0221 EB00        JMP     0223
0C1C:0223 5D            POP     BP
0C1C:0224 C3            RET
查看代码后我们发现,MOV SI,0004 和MOV DI,0005中的4、5正好对应我们t.c中的4和5。我们从头开始一步步执行,并观察栈中元素的变化如下:
PUSH BP            栈中元素为:BP
MOV  BP,SP          BP中保存当前栈顶位置,即指向栈中元素BP
SUB SP,+02          sp减2,相当于入栈操作,只是入栈元素为当前栈空间中残留字型数据,相当于开辟了一个字空间,此时栈中元素为:BP-残留数据
PUSH SI             此时栈中元素为:BP-残留数据-SI 
PUSH DI             此时栈中元素为:BP-残留数据-SI-DI
MOV SI,0004      把4放入寄存器SI   
MOV DI,0005      把5放入寄存器DI
PUSH DI             此时栈中元素为:BP-残留数据-SI-DI-5
PUSH SI             此时栈中元素为:BP-残留数据-SI-DI-5-4
CALL 0218           调用子程序,对应c=add(a,b),段内近转移,CALL命令做的第一件事是把IP入栈,以便能正确返回。执行后栈中元素为:
BP-残留数据-SI-DI-5-4-020D
PUSH BP        函数add()的第一句汇编代码,为了还原现场,所以要重新使BP入栈。此时栈中元素为:BP-残留数据-SI-DI-5-4-020D-BP
MOV BP,SP           把当前栈顶位置赋给BP
MOV AX,[BP+04]   注意[BP+idata]默认的段寄存器应该是SS,因为SS:[SP]对应栈顶的BP,而BP==SP,所以 SS:[BP+4]应该对应栈中的4。
ADD AX,[BP+06]   由上部同样可推导出SS:[BP+6]应该对应栈中的5,对应return a+b;把相加后的结果放在AX中。
JMP 0223          跳转到偏移地址0223处,对应指令POP BP,为何跳转,不予考虑。
POP BP           还原BP,执行后栈中元素为:BP-残留数据-SI-DI-5-4-020D
RET                回到调用函数(对应main函数) 执行后栈中元素为:BP-残留数据-SI-DI-5-4,而把IP赋值为020D。
POP CX           执行后栈中元素为:BP-残留数据-SI-DI-5,  CX=4 
POP CX           执行后栈中元素为:BP-残留数据-SI-DI,    CX=5
MOV [BP-02],AX  由于main()对应的汇编指令初始时把栈顶偏移地址给了BP,所以此时的SS:[BP]应该对应栈中元素BP,而SS:[BP-2]则对应栈中的残留数据,由于add()对应的汇编代码ADD AX,[BP+06]把相加后的结果存放在了AX中,所以这里是用战中残留数据的空间存储了两个值相加后的结果。而此位置则对应了t.c中的变量c的存储位置。
POP  DI          还原DI,执行后栈中元素为:BP-残留数据-SI
POP  SI          还原SI,执行后栈中元素为:BP-残留数据
MOV  SP,BP      还原SP,执行后栈中元素为:BP,因为main()对应的汇编指令的前两句PUSH BP,MOV BP,SP,结果就是SS:[BP]执向栈中元素元素BP。所以还原后SS:[BP]仍然指向栈中元素元素BP。
POP  BP          还原BP,执行后栈中元素为:空
RET                函数调用完成,返回掉用main()的函数。

  经过以上分析我们会发现,C语言经编译链接后生成的汇编程序并不复杂,每一条指令都是我们学过的。从中我们知道了,在函数间的参数传递以及在函数内部局部变量的声明都是通过栈来完成的。明白了这些,你是不是会恍然大悟,原来C语言最终是要以这种方式来执行。我们可以用同样的方法,去分析全局变量、指针、结构体、数组等C语言的各个知识点,到时候C语言会赤裸裸的暴露在我们面前,在我们的眼里,它将不再神秘。而此时,我们也许就已经具备了驾驭它的能力。

 

网友评论(35)

wyi2668 发表于:24/03/29 01:17】

籟043861或TG搜索@av8526#高中學生妹 #貧乳學生妹 #大學生妹 #學生妹試車 # 蘿莉學生妹 第一次下海處女 #青澀嬌羞學生 #台灣制服店外約小姐 #台灣囡囡外約學生妹節福利單節折扣2-5k包夜福利更多哦 TG搜索@av8526官網www.ppp8669.com頻道茶單https://t.me/y43861

wyi2668 发表于:24/03/29 01:15】

糖小兮外送茶莊籟043861外約茶新享受高檔台北外送茶幾平價台中外送茶應有盡有漂亮外約妹上門**權威外送茶攻略讓您安心消費開心享受Telegram:av8526



台灣約妹第一首選 正妹兼差超優質
籟043861【Telegram:@av8526】

台灣在地老字號 全網信譽第一
快來預約我用腿替你量腰圍

營業時間早上13:00-凌晨03:00
現金消費 不買點數 不玩轉帳

全套**:口交 洗澡 按摩 愛愛 舌吻 69 口爆...
可送地區:台北 新北 林口龜山萬壽路 台中 高雄 新竹 彰化南投 台南均可**


新手必看流程:
不懂喝茶的新手必看哦籟043861備用籟bj5206【Telegram:av8526】
1.請告知您所在地區及稱呼,您的預算,以及喜歡什?類型的美眉,好讓我能為您安排更符合你理想的美眉
2.請告知您的手機門號,要約會的時間及要開哪家旅館或住家,確認後即可去開房
3.開好房間後,請告知您的房號及拍照房卡給小兮,我會請妹提前出發,大約10-20分鐘即可到達
4.見到妹滿意後才消費,現金交易,不買點數,不轉賬,不收定金,請您享受歡樂時光
ps口碑老字號茶莊,良心推薦,良心茶莊,客戶的每件小事都是小兮大事!


#甜美護士兼職:米粒158cm.C.45.25歲
#清純護士再次下海 可洩慾打針喲之前有兼職過 時間不久 現在來兼職為了洩慾有沒有想要來試試的哥哥 小隻清純類型超級溫柔的一個女生 哥哥們約她請溫柔一點


#清純學生妹兼職:田柒柒158.45.C奶.21歲
在校大學生 半工半讀學習優良 清純乖巧反差型 有交過男朋友 有性經驗 性經驗豐富 玩過很多姿勢 清純小悶騷 嫩乳敏感型 親吻撫摸就會想要 也會出水


#巨乳大奶妹兼職:美樂蒂165.E奶 48 24歲
F級白滑粉嫩啵啵 皮膚好好、Q彈 有光澤臉蛋妖嬈、艷麗 身材不錯、小蠻腰、有在練瑜伽、身段柔軟哦! 屁股翹翹的、很飽眼福的、摸起來也超爽 比較主動-熱情~ 今日想體驗一下69姿勢和老漢推車姿勢哦! 經常看A片手淫的、很刺激der


#高中學生妹兼職:糖心155cm.B.45.已成年(剛滿)
#限時預約正妹 無經驗 無性愛可多次射 全程可無套 現在是安全期 所以才嘗試無套內射 想找永久炮友必須配合一次喲第一次無技術 想玩一次內射~ 希望哥哥們喜歡我 兼職今天一天 就決定固定炮友


#高挑美腿模特兼職:喬恩167/F/49kg/24歲  
氣質修長美腿 時尚模特兼職 臉蛋很漂亮 身材火辣 亮麗的外型 標准正妹系的最佳選擇 性感迷人 挺拔的雙峰 男人中的最愛 床上很有女朋友FU   口交技術一流  很會舔蛋蛋


#小隻馬蘿莉學生妹兼職:優子156.B.42.19歲
乖巧白皙der學生妹白皙皮膚 懂事聽話乖小孩 很調皮的性格 讓你想要好好疼她  在床上你可以教她任何的姿勢 卡緊來找我約她探索她的蜜穴  超嫩 超緊 水超多的 希望哥哥好好調教她哦 配合度高  想要她配合什麼都可以跟她說哦



糖小兮主推北部優質(高中生)籟043861或bj5206
內湖高中 小熏155 E 45 18歲 舞蹈系女生
志平信中 小煙158 F 47 18歲 日系學生味
永平高中 兩煙160 C 45 18歲 日系蘿莉妹
淡水高中 球球152 D 45 18歲 活潑可愛妹
中和高中 柔兒154 B 45 18歲 貧乳蘿莉妹
錦和高中 清月162 E 47 18歲 超高顏值妹
薇閣高中 瑞瑞168 E 47 18歲 高挑巨乳妹
清傳高中 米?149 C 42 18歲 清純小芝麻


糖小兮主推中部優質(高中生)籟043861或bj5206
文華高中 楠楠172 D 48 18歲 高挑巨乳妹
西苑高中 安安158 E 45 18歲 高顏值正妹
大里高中 可兒152 C 42 18歲 日系學生妹
青年高中 可萱150 E 42 18歲 白虎一線鮑
清水高中 璇璇140 D 40 18歲 幼齒蘿莉妹
大甲高中 唯一155 A 44 18歲 鄰家小騷妹
嘉陽高中 小花152 E 45 18歲 日系巨乳妹
沙鹿高中 言言155 D 45 18歲 甜美反差妹


糖小兮主推南部優質(高中生)籟043861或bj5206
大同高中 茉莉158 F 45 18歲 大奶反差妹
美和高中 玫瑰152 D 44 18歲 白虎敏感妹
東港高中 秀秀150 B 41 18歲 嬌小蘿莉妹
新基高中 橘子155 C 41 18歲 幼齒主動妹
成功高中 小野160 E 44 18歲 床上反差妹
台東高中 路西162 C 44 18歲 超高顏值妹
公東高中 恩慈165 D 45 18歲 高挑長腿妹
文澡高中 迷路152 E 42 18歲 口暴吞精妹

#輔大學生妹:菜菜 158 C 21歲 44kg特色:清純 皮膚超級白皙 粉奶

#輔大學生妹:拉妹 160 D 20歲 47kg特色:大奶大眼蜜桃臀酒窩

#世新學生妹:冰冰  155 B 19歲 43kg特色:清純害羞被調教的類型 只有過一次性經驗

#淡大學生妹:妮妮 162 E奶 50kg 22歲特色:美腿巨乳兼職麻豆高顏值

#莊敬學生妹:安安 158 D 18歲 45kg特色:幼齒 火辣身材 床上騷

#中興學生妹:妞妞 158 D 20歲 44kg特殊:大奶高顏值小騷貨

#中興學生妹:小波 165 D 21歲 48kg特色:美腿水蛇腰兼職外拍麻豆

#中興學生妹:果果 152 A 18歲 42kg特殊:貧乳嬌小幼齒可愛清純

#朝陽學生妹:可蘭 160 C 22歲 45kg特色:皮膚超好聲音甜骨感正妹

#朝陽學生妹:小麗 160 D 20歲 44kg特色:口技強身材好清純反差感

#中山大學小伊 156 D 21歲 45kg特色:卡哇伊的學妹超甜妹

#中山大學花花 162 E奶 20歲 50kg特殊:巨乳穿天然混血長相優質

#輔英學生妹:小微 158 C+ 19歲 45kg特色:初戀女友付滿滿清純

#樹德學生妹:小可 155 B 18歲 44kg特色:幼齒甜美青澀

#僑光學生妹:曉曉 153 B 19歲 43kg非常嬌小的一個蘿莉 幼齒妹 粉嫩到爆

#靜宜學生妹:小雅165 E奶 22歲47kg 美胸漂亮美眉 顏值擔當 音樂系係花

#文澡學生妹:伊伊 155 B 19歲 45kg 纖細骨感酒窩超級甜美 非常好相處 顏值擔當

#慈濟學生妹:菲菲160cm D奶 21歲 45kg 五官立體 身材火辣 清新脫俗的氣質  超耐看

#輔仁學生妹;彩衣 158 D 21歲 45kg 清純的臉蛋嬌小的身軀胸部粉嫩粉嫩 絕對初戀女友fu 還有迷人的蜜桃臀推薦人群:百搭客群

#淡江學生妹:洋洋 163 E奶 22歲 47kg 身材很火辣純天然大奶不下垂顏值讚到爆‘’推薦人群:內向 放不開的客人

#世新學生妹:果果155 A奶19歲 43kg 超小芝麻 可愛到犯罪蘿莉類型 讓你有種幹未成年的fu 推薦人群:喜歡調教的客人 老司機

1152659530 发表于:23/08/01 17:44】

请问大家,在本文add函数计算的时候是直接访问栈中主函数的变量a,b吗?因为我在王爽老师的《汇编语言》附注4(p335)中看到的是需要复制实参到栈空间当作形参,这时栈中既有形参也有实参。本文的例子中没有复制实参当作形参,所以感到疑惑,请大家解答!

1152659530 发表于:23/07/31 22:05】

yanchao-student 发表于:20/08/21 22:16】

叔叔们好!,我学过c语言,但是目前还是很难写出像样的程序,经过朋友的推荐,我决定来此好好学习汇编,希望我能成功上岸

255299019 发表于:20/03/19 02:12】

程序运行时都是先要保存原来的bp栈值,然后mov bp,sp将开始栈处移动到bp,这里bp、和sp并不是寄存器这是栈ss==bp,sp==sp同理 32位汇编为esp==sp ebp==bp

feifeiluan2 发表于:18/11/23 17:10】

不用那么复杂,tcc -S a.c,直接生成a.asm汇编文件,不用去debug里面查看

jayroe 发表于:17/07/21 23:03】

还在疑惑,为什么我反编译出来的东西和书上的差别这么大。现在明白了

yfj8669 发表于:17/05/21 15:02】

原来如此多娇

edtwar 发表于:16/03/07 22:29】

真赞

当前1/4页 首页 上一页 下一页 尾页

我也跟评:

请输入验证码 (提示:点击验证码输入框,以获取验证码
    


更多文章

程序员眼中的世界

  本栏目所刊文章皆已获程序员眼中的世界期刊网<www.proeyes.net>授权,此处文章版权及解释权归程序员眼中的世界期刊网所有。凡转载本栏目文章的网站或个人,须于转载处声明“转载自汇编网www.asmedu.net”,并加注“文字来源为程序员眼中的世界期刊网”字样。

  程序员眼中的世界期刊网为程序员人文电子期刊杂志网,提供人文科学类的网络电子杂志。

  程序员眼中的世界期刊网立足IT行业,通过程序员的眼睛来观察世界,以程序员对行业和社会的认知反馈来影响行业和社会的发展,展现程序员的思想和人文科学价值,弘扬程序员群体积极向上的人文科学精神。