C/C++拾遗录--关于一个C语言小程序的分析
虽然编了几年程序,但是对于程序到底是什么规则变成汇编代码的,在这里搞了一个小程序。用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指令来完成的。
下边就是针对这些知识进行的部分实践:
- #include<stdio.h>
- #include<windows.h>
- #include<stdlib.h>
- int fun(char *szIn, int nTest)
- {
- char szBuf[9];
- printf("%d\n",nTest);
- strcpy(szBuf,szIn);
- return 0;
- }
- int main(int argc, char *argv[])
- {
- char sz_In[] = "1234567";
- fun(sz_In,888);
- return 0;
- }
汇编代码
- 00401003 int 3
- 00401004 int 3
- @ILT+0(?fun@@YAHPADH@Z):
- 00401005 jmp fun (00401020) //进入fun函数
- @ILT+5(_main):
- 0040100A jmp main (00401080) //进入main函数,该位置是整段代码的入口
- 0040100F int 3
- 00401010 int 3
- 00401011 int 3
- 00401012 int 3
- 00401013 int 3
- 00401014 int 3
- 00401015 int 3
- 00401016 int 3
- 00401017 int 3
- 00401018 int 3
- 00401019 int 3
- 0040101A int 3
- 0040101B int 3
- 0040101C int 3
- 0040101D int 3
- 0040101E int 3
- 0040101F int 3
- --- c:\project\heap1\heap1.cpp --------------------------------------------------------------------------------------------------------------------------------------
- 1: #include<stdio.h>
- 2: #include<windows.h>
- 3: #include<stdlib.h>
- 4: int fun(char *szIn, int nTest)
- 5: {
- 00401020 push ebp
- 00401021 mov ebp,esp //保存基址指针,并将现在的栈顶保存为基址指针。
- 00401023 sub esp,4Ch //腾出一部分堆栈区用于存放局部变量。
- 00401026 push ebx
- 00401027 push esi
- 00401028 push edi //保存三个寄存器的值。
- 00401029 lea edi,[ebp-4Ch]
- 0040102C mov ecx,13h
- 00401031 mov eax,0CCCCCCCCh
- 00401036 rep stos dword ptr [edi] //将腾出的4Ch的空间初始化值为0xCC。
- 6: char szBuf[9];
- 7: printf("%d\n",nTest);
- 00401038 mov eax,dword ptr [ebp+0Ch]
- 0040103B push eax
- 0040103C push offset string "%d\n" (0042201c) //先后压入栈中两个地址,nTest,一个是一个字符串指针。
- 00401041 call printf (004011d0) //调用printf函数时,它会自动做到堆栈平衡。
- 00401046 add esp,8 //由于刚才压入和两个参数,所以在这里手动将两个参数弹出堆栈
- 8: strcpy(szBuf,szIn);
- 00401049 mov ecx,dword ptr [ebp+8]
- 0040104C push ecx //压入szIn的指针。这个参数在高出基址的8位处,也就是调用该函数前压入栈中的。
- 0040104D lea edx,[ebp-0Ch]
- 00401050 push edx //压入szBuf的指针,这个函数在低于基址的OCh位处,这是调用函数后分配的。局部变量的分配大
- 00401051 call strcpy (004010e0) //小都是按4的倍数分配的,所以尽管szBuf[9]但是也分配在了0Ch处。
- 00401056 add esp,8
- 9: return 0;
- 00401059 xor eax,eax //返回值在EAX中。
- 10: }
- 0040105B pop edi
- 0040105C pop esi
- 0040105D pop ebx //弹出保存的数据。
- 0040105E add esp,4Ch //消除为局部变量腾出的空间。
- 00401061 cmp ebp,esp
- 00401063 call __chkesp (00401250) //检验是否在用户自定义汇编代码中修改了ebp和esp的相对关系。一般情况下EBP>ESP
- 00401068 mov esp,ebp //将原基址恢复给栈顶寄存器。
- 0040106A pop ebp //弹出原调用函数的堆栈基址。
- 0040106B ret //函数返回。
- --- No source file --------------------------------------------------------------------------------------------------------------------------------------------------
- 0040106C int 3
- 0040106D int 3
- 0040106E int 3
- 0040106F int 3
- 00401070 int 3
- 00401071 int 3
- 00401072 int 3
- 00401073 int 3
- 00401074 int 3
- 00401075 int 3
- 00401076 int 3
- 00401077 int 3
- 00401078 int 3
- 00401079 int 3
- 0040107A int 3
- 0040107B int 3
- 0040107C int 3
- 0040107D int 3
- 0040107E int 3
- 0040107F int 3
- --- c:\project\heap1\heap1.cpp --------------------------------------------------------------------------------------------------------------------------------------
- 11: int main(int argc, char *argv[])
- 12: {
- 00401080 push ebp
- 00401081 mov ebp,esp //保存堆栈基址
- 00401083 sub esp,48h //腾出局部变量空间
- 00401086 push ebx
- 00401087 push esi
- 00401088 push edi //保存3个寄存器
- 00401089 lea edi,[ebp-48h]
- 0040108C mov ecx,12h
- 00401091 mov eax,0CCCCCCCCh //初始化局部变量空间
- 00401096 rep stos dword ptr [edi]
- 13: char sz_In[] = "1234567";
- 00401098 mov eax,[string "1234567" (00422020)]
- 0040109D mov dword ptr [ebp-8],eax
- 004010A0 mov ecx,dword ptr [string "1234567"+4 (00422024)]
- 004010A6 mov dword ptr [ebp-4],ecx //将字符串通过寄存器将字符拷贝到分配的空间中。
- 14: fun(sz_In,888);
- 004010A9 push 378h
- 004010AE lea edx,[ebp-8]
- 004010B1 push edx //从右至左将参数压入堆栈中,数字直接压入数值,字符串则压入字符串指针
- 004010B2 call @ILT+0(fun) (00401005)
- 004010B7 add esp,8 //恢复堆栈
- 15: return 0;
- 004010BA xor eax,eax //返回值在EAX中
- 16: }
- 004010BC pop edi
- 004010BD pop esi
- 004010BE pop ebx //恢复3个寄存器
- 004010BF add esp,48h //清除局部变量空间
- 004010C2 cmp ebp,esp
- 004010C4 call __chkesp (00401250) //检测堆栈指针与堆栈基址
- 004010C9 mov esp,ebp //恢复调用函数的栈顶
- 004010CB pop ebp //恢复调用函数的堆栈基址
- 004010CC ret //函数返回
- --- No source file --------------------------------------------------------------------------------------------------------------------------------------------------
- 004010CD int 3
- 004010CE int 3
关于这个程序的堆栈使用情况也做了一下分析,如图:
C/C++拾遗录--关于一个C语言小程序的分析相关推荐
- 我的第一个c语言小程序
标题:判断题答题小程序 Author: plc6666 软工专业 工科男 格言:总有人间一两风,填我十万八千梦. 文章目录 标题:判断题答题小程序 一.程序的由来 二.程序的状况 1.程序实现了颜色转 ...
- 一个C语言小程序,有10几个命令和MSDOS一样哦:)
/*调用函数中的scanf前没有提示,其实在主函数中输入时,空格后还可以输入被调用函数中的参数*/ /*这样就避免了很多输出的麻烦,而且看着也比较的舒服:-)例如新建立一个文件,就可以直接输入mf a ...
- C语言第一行是1第二行是1和2,【C语言】第一个C语言小程序 —— 日期算法和万年历 2...
1. 上一篇我们只完成了 a. 算出某年某月某日是星期几 b. 打印出某年某月的日历 这一次我写了一个打印某一年的日历.一开始我是不打算写的,因为可以调用之前的方法,分别打印出这一年12个月的日历.但 ...
- C语言小程序实现输出国际象棋棋盘
分享一个C语言小程序,输出国际象棋的棋盘,摘自C语言网dotcpp.com. 题目: 要求输出国际象棋棋盘. 1.程序分析: 用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方格,还是白方格. ...
- c语言为什么要建项目,一个C语言小项目为什么都说牛逼
原标题:一个C语言小项目为什么都说牛逼 意在鼓励C语言学者.更有兴趣,学习更富有创业和乐趣! 推荐加学习交流群:658807522 可以在一起学习交流,既是参赛选手,又是学者,也可以先学习再参赛,反正 ...
- c语言min函数_C语言探索之旅 | 第一部分第十课:第一个C语言小游戏
内容简介 前言 准备工作和建议 我的代码 改进方案 第一部分第十一课预告 1. 前言 上一课是 C语言探索之旅 | 第一部分第九课:循环语句 . 经过前面这么多课的努力,我们终于迎来了第一个比较正式的 ...
- c语言小游戏vc,C语言探索之旅:第一个C语言小游戏
C语言探索之旅:第一个C语言小游戏-1.jpg (37.05 KB, 下载次数: 0) 2018-10-8 19:23 上传 内容简介 1.课程纲领 2.第一部分第八课:第一个C语言小游戏 3.第一部 ...
- 我的第一个C语言小游戏
文章目录 前言 一.我的小介绍 二.解决问题 目前还未解决的问题 三.代码如下 前言 一.我的小介绍 大家好,我是一位大一小白,之前并没有想到我会和编程打交道,接触C语言后,我才发现C语言如此有趣,今 ...
- 常用c语言小程序,c语言经典小程序汇总大全
网上有很多的人说编程有多么多么无聊,其实:不要管别人怎么说,别人说什么,做你自己喜欢做的事就好.坚持下来,你会发现编程的乐趣的.当然,如果你觉得学习编程语言很痛苦,坚持了一段时间后无果,南无果断放弃未 ...
最新文章
- DotNetTextBox V3.0 所见即所得编辑器控件 For Asp.Net2.0(ver 3.0.9Beta)
- sql唯一约束怎么设置_20200923 SQL UNIQUE 约束
- PDO(PHP Data Object),Mysqli,以及对sql注入等问题的解决
- 【待填坑】LG_4996_咕咕咕
- 【详细解析】7-1 两个有序序列的中位数 (25 分)
- 【算法导论】归并排序
- 【图像超分辨率】(SPSR)Structure-Preserving SR with Gradient Guidance
- npoi excel 复制行_Excel的格式刷功能你真的会用吗?这样刷更加方便快捷
- RSA加密、解密、签名、校验签名
- SVN:show log问题
- 深度学习笔记_基本概念_神经网络中的epoch、batch_size和iteration
- X window 概念及原理图
- 集成海康威视Sadp SDK实现设备激活
- plm服务器 硬件性能,PLM 性能问题
- android启用hdcp_如何在Android的Google键盘中启用单手模式
- 解决HP ProLiant DL380 G5的安装与启动CentOS7时不能识别raid硬盘问题
- 全球资本市场竞争力指数排名发布,中国跃居第五
- 西影多媒体演示中心的消防应急照明和疏散指示系统
- MATLAB文件夹页面被隐藏后如何恢复
- 社保卡(深圳)在线激活
热门文章
- 访问网络共享时出现“拒绝访问”
- 24组合模式(Composite Pattern)
- java中synchronized(同步代码块和同步方法)详解及区别
- 【GUI开发】图像处理类软件的浏览功能实现模型
- 设计模式(五)行为型模式
- 数据结构 - 线索化二叉树(线索化与遍历)
- *【SGU - 114】Telecasting station (带权中位数 或 三分)
- 【POJ - 1182】 食物链(附超详细讲解)(并查集--种类并查集经典题)
- java生成16位随机数_java中如何产生一个16位数字组成的随机字符串?谢谢各位了...
- php ip 合法,什么是合法ip地址