虽然编了几年程序,但是对于程序到底是什么规则变成汇编代码的,在这里搞了一个小程序。用VC查看了一下汇编代码。在此之前先介绍一下关于函数运行是堆栈变化的细节。

在高级语言编写程序时,函数的调用是很常见的事情,但是在函数调用过程中堆栈的变化通常有几个细节:

1.父函数将函数的实参按照从右至左的顺序压入堆栈;

2.CPU将父函数中函数调用指令Call的下一条指令地址EIP压入堆栈;

3.父函数通过Push Ebp指令将基址指针EBP的值压入堆栈,并通过Mov Ebp,Esp指令将当前堆栈指针Esp值传给Ebp;

4.通过Sub Esp,m(m是字节数)指令可以为存放函数中的局部变量开辟内存。函数在执行的时候如果需要访问实参或局部变量,都可以通过EBP指针来指引完成。

windows系统下常用的函数调用通常有种,__cdecl和__stdCall。

1.在VC、.net等开发环境中,编写命令行程序时的Main或者_tmain函数,以及大家自己定义的很多函数都是默认采用__cdecl调用方式;

2.通过MFC编写图形界面程序的时候,其主函数声明为extern "C" int WINAPI tWinMain(参数),该函数的调用约定是__stdCall。WINAPI和PASCAL等都是__stdCall的宏定义,是一个意思,此外,大家平时调用的API函数,绝大多数都是采用__staCall的调用方式;

3.__cdecl调用方式的函数,父函数在调用子函数的时候,先将子函数的实参按照从右至左的顺序压入堆栈中,子函数返回后,父函数通过Sub Esp,n(n=函数实参个数*4)指令来恢复堆栈;

4.__stdCall调用约定函数,子函数调用时实参入栈顺序也是从左到右,但是堆栈恢复是子函数返回时自己通过Ret n指令来完成的。

下边就是针对这些知识进行的部分实践:

[cpp] view plaincopy
  1. #include<stdio.h>
  2. #include<windows.h>
  3. #include<stdlib.h>
  4. int fun(char *szIn, int nTest)
  5. {
  6. char szBuf[9];
  7. printf("%d\n",nTest);
  8. strcpy(szBuf,szIn);
  9. return 0;
  10. }
  11. int main(int argc, char *argv[])
  12. {
  13. char sz_In[] = "1234567";
  14. fun(sz_In,888);
  15. return 0;
  16. }

汇编代码

[plain] view plaincopy
  1. 00401003   int         3
  2. 00401004   int         3
  3. @ILT+0(?fun@@YAHPADH@Z):
  4. 00401005   jmp         fun (00401020)    //进入fun函数
  5. @ILT+5(_main):
  6. 0040100A   jmp         main (00401080)    //进入main函数,该位置是整段代码的入口
  7. 0040100F   int         3
  8. 00401010   int         3
  9. 00401011   int         3
  10. 00401012   int         3
  11. 00401013   int         3
  12. 00401014   int         3
  13. 00401015   int         3
  14. 00401016   int         3
  15. 00401017   int         3
  16. 00401018   int         3
  17. 00401019   int         3
  18. 0040101A   int         3
  19. 0040101B   int         3
  20. 0040101C   int         3
  21. 0040101D   int         3
  22. 0040101E   int         3
  23. 0040101F   int         3
  24. --- c:\project\heap1\heap1.cpp  --------------------------------------------------------------------------------------------------------------------------------------
  25. 1:    #include<stdio.h>
  26. 2:    #include<windows.h>
  27. 3:    #include<stdlib.h>
  28. 4:    int fun(char *szIn, int nTest)
  29. 5:    {
  30. 00401020   push        ebp
  31. 00401021   mov         ebp,esp               //保存基址指针,并将现在的栈顶保存为基址指针。
  32. 00401023   sub         esp,4Ch               //腾出一部分堆栈区用于存放局部变量。
  33. 00401026   push        ebx
  34. 00401027   push        esi
  35. 00401028   push        edi                   //保存三个寄存器的值。
  36. 00401029   lea         edi,[ebp-4Ch]
  37. 0040102C   mov         ecx,13h
  38. 00401031   mov         eax,0CCCCCCCCh
  39. 00401036   rep stos    dword ptr [edi]       //将腾出的4Ch的空间初始化值为0xCC。
  40. 6:        char szBuf[9];
  41. 7:        printf("%d\n",nTest);
  42. 00401038   mov         eax,dword ptr [ebp+0Ch]
  43. 0040103B   push        eax
  44. 0040103C   push        offset string "%d\n" (0042201c)    //先后压入栈中两个地址,nTest,一个是一个字符串指针。
  45. 00401041   call        printf (004011d0)                            //调用printf函数时,它会自动做到堆栈平衡。
  46. 00401046   add         esp,8                                        //由于刚才压入和两个参数,所以在这里手动将两个参数弹出堆栈
  47. 8:        strcpy(szBuf,szIn);
  48. 00401049   mov         ecx,dword ptr [ebp+8]
  49. 0040104C   push        ecx                           //压入szIn的指针。这个参数在高出基址的8位处,也就是调用该函数前压入栈中的。
  50. 0040104D   lea         edx,[ebp-0Ch]
  51. 00401050   push        edx                 //压入szBuf的指针,这个函数在低于基址的OCh位处,这是调用函数后分配的。局部变量的分配大
  52. 00401051   call        strcpy (004010e0)   //小都是按4的倍数分配的,所以尽管szBuf[9]但是也分配在了0Ch处。
  53. 00401056   add         esp,8
  54. 9:        return 0;
  55. 00401059   xor         eax,eax             //返回值在EAX中。
  56. 10:   }
  57. 0040105B   pop         edi
  58. 0040105C   pop         esi
  59. 0040105D   pop         ebx                 //弹出保存的数据。
  60. 0040105E   add         esp,4Ch             //消除为局部变量腾出的空间。
  61. 00401061   cmp         ebp,esp
  62. 00401063   call        __chkesp (00401250) //检验是否在用户自定义汇编代码中修改了ebp和esp的相对关系。一般情况下EBP>ESP
  63. 00401068   mov         esp,ebp             //将原基址恢复给栈顶寄存器。
  64. 0040106A   pop         ebp                 //弹出原调用函数的堆栈基址。
  65. 0040106B   ret                             //函数返回。
  66. --- No source file  --------------------------------------------------------------------------------------------------------------------------------------------------
  67. 0040106C   int         3
  68. 0040106D   int         3
  69. 0040106E   int         3
  70. 0040106F   int         3
  71. 00401070   int         3
  72. 00401071   int         3
  73. 00401072   int         3
  74. 00401073   int         3
  75. 00401074   int         3
  76. 00401075   int         3
  77. 00401076   int         3
  78. 00401077   int         3
  79. 00401078   int         3
  80. 00401079   int         3
  81. 0040107A   int         3
  82. 0040107B   int         3
  83. 0040107C   int         3
  84. 0040107D   int         3
  85. 0040107E   int         3
  86. 0040107F   int         3
  87. --- c:\project\heap1\heap1.cpp  --------------------------------------------------------------------------------------------------------------------------------------
  88. 11:   int main(int argc, char *argv[])
  89. 12:   {
  90. 00401080   push        ebp
  91. 00401081   mov         ebp,esp                       //保存堆栈基址
  92. 00401083   sub         esp,48h                       //腾出局部变量空间
  93. 00401086   push        ebx
  94. 00401087   push        esi
  95. 00401088   push        edi                           //保存3个寄存器
  96. 00401089   lea         edi,[ebp-48h]
  97. 0040108C   mov         ecx,12h
  98. 00401091   mov         eax,0CCCCCCCCh                //初始化局部变量空间
  99. 00401096   rep stos    dword ptr [edi]
  100. 13:       char sz_In[] = "1234567";
  101. 00401098   mov         eax,[string "1234567" (00422020)]
  102. 0040109D   mov         dword ptr [ebp-8],eax
  103. 004010A0   mov         ecx,dword ptr [string "1234567"+4 (00422024)]
  104. 004010A6   mov         dword ptr [ebp-4],ecx         //将字符串通过寄存器将字符拷贝到分配的空间中。
  105. 14:       fun(sz_In,888);
  106. 004010A9   push        378h
  107. 004010AE   lea         edx,[ebp-8]
  108. 004010B1   push        edx      //从右至左将参数压入堆栈中,数字直接压入数值,字符串则压入字符串指针
  109. 004010B2   call        @ILT+0(fun) (00401005)
  110. 004010B7   add         esp,8                        //恢复堆栈
  111. 15:       return 0;
  112. 004010BA   xor         eax,eax                      //返回值在EAX中
  113. 16:   }
  114. 004010BC   pop         edi
  115. 004010BD   pop         esi
  116. 004010BE   pop         ebx                          //恢复3个寄存器
  117. 004010BF   add         esp,48h                      //清除局部变量空间
  118. 004010C2   cmp         ebp,esp
  119. 004010C4   call        __chkesp (00401250)          //检测堆栈指针与堆栈基址
  120. 004010C9   mov         esp,ebp                      //恢复调用函数的栈顶
  121. 004010CB   pop         ebp                          //恢复调用函数的堆栈基址
  122. 004010CC   ret                                      //函数返回
  123. --- No source file  --------------------------------------------------------------------------------------------------------------------------------------------------
  124. 004010CD   int         3
  125. 004010CE   int         3

关于这个程序的堆栈使用情况也做了一下分析,如图:

C/C++拾遗录--关于一个C语言小程序的分析相关推荐

  1. 我的第一个c语言小程序

    标题:判断题答题小程序 Author: plc6666 软工专业 工科男 格言:总有人间一两风,填我十万八千梦. 文章目录 标题:判断题答题小程序 一.程序的由来 二.程序的状况 1.程序实现了颜色转 ...

  2. 一个C语言小程序,有10几个命令和MSDOS一样哦:)

    /*调用函数中的scanf前没有提示,其实在主函数中输入时,空格后还可以输入被调用函数中的参数*/ /*这样就避免了很多输出的麻烦,而且看着也比较的舒服:-)例如新建立一个文件,就可以直接输入mf a ...

  3. C语言第一行是1第二行是1和2,【C语言】第一个C语言小程序 —— 日期算法和万年历 2...

    1. 上一篇我们只完成了 a. 算出某年某月某日是星期几 b. 打印出某年某月的日历 这一次我写了一个打印某一年的日历.一开始我是不打算写的,因为可以调用之前的方法,分别打印出这一年12个月的日历.但 ...

  4. C语言小程序实现输出国际象棋棋盘

    分享一个C语言小程序,输出国际象棋的棋盘,摘自C语言网dotcpp.com. 题目: 要求输出国际象棋棋盘. 1.程序分析: 用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方格,还是白方格. ...

  5. c语言为什么要建项目,一个C语言小项目为什么都说牛逼

    原标题:一个C语言小项目为什么都说牛逼 意在鼓励C语言学者.更有兴趣,学习更富有创业和乐趣! 推荐加学习交流群:658807522 可以在一起学习交流,既是参赛选手,又是学者,也可以先学习再参赛,反正 ...

  6. c语言min函数_C语言探索之旅 | 第一部分第十课:第一个C语言小游戏

    内容简介 前言 准备工作和建议 我的代码 改进方案 第一部分第十一课预告 1. 前言 上一课是 C语言探索之旅 | 第一部分第九课:循环语句 . 经过前面这么多课的努力,我们终于迎来了第一个比较正式的 ...

  7. c语言小游戏vc,C语言探索之旅:第一个C语言小游戏

    C语言探索之旅:第一个C语言小游戏-1.jpg (37.05 KB, 下载次数: 0) 2018-10-8 19:23 上传 内容简介 1.课程纲领 2.第一部分第八课:第一个C语言小游戏 3.第一部 ...

  8. 我的第一个C语言小游戏

    文章目录 前言 一.我的小介绍 二.解决问题 目前还未解决的问题 三.代码如下 前言 一.我的小介绍 大家好,我是一位大一小白,之前并没有想到我会和编程打交道,接触C语言后,我才发现C语言如此有趣,今 ...

  9. 常用c语言小程序,c语言经典小程序汇总大全

    网上有很多的人说编程有多么多么无聊,其实:不要管别人怎么说,别人说什么,做你自己喜欢做的事就好.坚持下来,你会发现编程的乐趣的.当然,如果你觉得学习编程语言很痛苦,坚持了一段时间后无果,南无果断放弃未 ...

最新文章

  1. DotNetTextBox V3.0 所见即所得编辑器控件 For Asp.Net2.0(ver 3.0.9Beta)
  2. sql唯一约束怎么设置_20200923 SQL UNIQUE 约束
  3. PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决
  4. 【待填坑】LG_4996_咕咕咕
  5. 【详细解析】7-1 两个有序序列的中位数 (25 分)
  6. 【算法导论】归并排序
  7. 【图像超分辨率】(SPSR)Structure-Preserving SR with Gradient Guidance
  8. npoi excel 复制行_Excel的格式刷功能你真的会用吗?这样刷更加方便快捷
  9. RSA加密、解密、签名、校验签名
  10. SVN:show log问题
  11. 深度学习笔记_基本概念_神经网络中的epoch、batch_size和iteration
  12. X window 概念及原理图
  13. 集成海康威视Sadp SDK实现设备激活
  14. plm服务器 硬件性能,PLM 性能问题
  15. android启用hdcp_如何在Android的Google键盘中启用单手模式
  16. 解决HP ProLiant DL380 G5的安装与启动CentOS7时不能识别raid硬盘问题
  17. 全球资本市场竞争力指数排名发布,中国跃居第五
  18. 西影多媒体演示中心的消防应急照明和疏散指示系统
  19. MATLAB文件夹页面被隐藏后如何恢复
  20. 社保卡(深圳)在线激活

热门文章

  1. 访问网络共享时出现“拒绝访问”
  2. 24组合模式(Composite Pattern)
  3. java中synchronized(同步代码块和同步方法)详解及区别
  4. 【GUI开发】图像处理类软件的浏览功能实现模型
  5. 设计模式(五)行为型模式
  6. 数据结构 - 线索化二叉树(线索化与遍历)
  7. *【SGU - 114】Telecasting station (带权中位数 或 三分)
  8. 【POJ - 1182】 食物链(附超详细讲解)(并查集--种类并查集经典题)
  9. java生成16位随机数_java中如何产生一个16位数字组成的随机字符串?谢谢各位了...
  10. php ip 合法,什么是合法ip地址