printf压栈出栈
printf函数的压栈顺序是从右到左压栈,计算表达式值的顺序也是从右到左,由于输入数据类型的多样性,压栈的时候将数据或数据的地址mov到寄存器中,然后将寄存器压栈
例子:
#include <iostream>
using namespace std;
int main(){int arr[] = { 6, 7, 8, 9, 10 };int *ptr = arr; //ptr指向6*(ptr++) += 123;//先执行6+123,然后ptr指向7printf("%d,%d\n", *ptr, *(++ptr));//先执行ptr+4使得ptr指向8,然后把ptr压栈2次printf("%d,%d,%d,%d,%d\n", *ptr--,*ptr+20, *(ptr--), *ptr, *(++ptr));int i = 5;char ch = 't';char str[10] = "test";printf("%s %c %d\n", str, ch, i);printf("%d %d %d %d\n", i, --i, i, i--);system("pause");return 0;
}
程序在VS2013中的输出如下:
分析:
上述图片中是调用printf函数时的汇编代码,主要流程是:将ptr指针增加4B,保存main的栈顶指针,把printf函数所需的2个参数压栈,调用printf函数,恢复main函数的帧顶指针。
dword表示指针是4B的,dword ptr [X]表示X所指地址对应的数据类型是双字指针。
move esi,esp表示保存main函数的栈帧的栈顶地址,以便调用printf函数返回时回到该位置处。
call dword ptr ds:[0C82184h]表示data segment寄存器中地址向后偏移0C82184h处的双字指针,此处是printf函数的代码段,call表示调用printf函数。
00C73729至00C7372E是VS编译器自带的检查栈帧是否正确恢复到原始地址的,而esp是栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶,在printf函数随着push操作而改变,因为前面往栈中push了3次,由于栈向地址减少的方向增长,那么esp就减少了12,printf结束时esp要回到mian函数的栈顶,那就是add esp, 0Ch 了。cmp指令比较esp值与esi值,结果不保存,只是修改标志寄存器值。最后__RTC_CheckEsp是调试的时候VS编译器自己加入的,发布版本会过滤掉此函数 功能就是检查堆栈平。
从上述图片中的汇编代码中可以看出,对printf函数的参数压栈过程是:
1)先从右到左计算ptr指针的变化,并把ptr–操作的结果地址保存到临时寄存器中;该步骤到00E83764 mov esi,esp结束;
2)为printf函数执行参数压栈操作,对于ptr–操作从临时寄存器中取出地址并压栈,对于其他*ptr操作和++ptr则使用ptr的最新值并压栈。
似乎也可以从中看出printf函数参数中,(++ptr)和ptr–操作的区别是:前缀++在压栈前执行,压栈时使用最新ptr值;而后缀–操作在压栈中进行并保持操作结果,压栈时使用保存的ptr值。
因此最后输出值为:8,27,9,7,7.
根据前面的解释:前缀操作在压栈前执行,压栈时使用最新指针值;后缀操作也在压栈前进行但保存结果到临时寄存器,压栈时压入临时寄存器的指针。
那么上面myprintf("%d,%d,%d,%d\n",i,–i,i,i–)代码中,从右往左执行,最后一个参数是后缀i–操作,把i的地址保存到临时寄存器,值为5;然后i的值减为4;倒数第二个参数为i,地址不用变;倒数第3个操作为–i,先把值减1得3,地址不保存;第一个操作为i,地址不保存。压栈时从右到左压栈,倒数第一个操作为i–,后缀操作,取临时寄存器的地址,值为5,压栈;倒数第2个操作,取i的最新地址,值为3,压栈;第2个参数和第1个参数都是取i的最新值,即3,压栈。输出时从栈顶到栈底依次输出,结果为:5,3,3,3。
说明:
真正开始压栈操作是从mov esi,esp开始。
eax是加法寄存器
mov eax,dword ptr[ptr] 是源操作数的间接寻址。
360笔试题如下:
int a[] = { 3, 4 }, b[] = { 5, 6 };
struct st{int num;int *p;
}s[2] = {100,a,100,b},*q = s;
int main(){int n;printf("%d%d%d",*q->p,++q->num,(q++)->num);return 0;
}
输出:5101100
解释:压栈前,计算q的地址变化,q初始时指向s[0],在printf函数的参数中,首先是q后缀++操作,需要先保存q的地址到临时寄存器中,然后执q++,这时q指向S[1],并把地址保存;然后是++q->num操作,++操作优先级低于->操作,因此是把s[1].num进行加运算,值由100变为101,修改的不是q的值;最后是*q->p操作,q地址没变;压栈时,从右到左,首先取临时寄存器中q的地址,即S[0].num,值100压栈;最新q指向s[1],然后取s[1].num,值为101压栈;最后取s[1].p的值,s[1].p指向数组b,值为5压栈。最后从栈顶到栈底输出,即:5,101,100.
printf压栈出栈相关推荐
- c语言局部变量压栈顺序,C 程序局部变量压栈出栈的理解
写这篇总结的缘由仅仅出于巧合,五一前帮一位同学看51的程序,在查看汇编代码的时候(事实上我当时的汇编知识基本都还给了老师),无意中问起我"某个局部变量的声明怎么没有对应的汇编语句" ...
- Java 数据结构(链表LinkedList增删改查、数组Vector、获取Vector最大值、交换Vector两成员位置、栈的实现、压栈出栈实现反转、队列Queue)
在链表(LinkedList)的开头和结尾添加元素 import java.util.LinkedList;public class Main {public static void main(Str ...
- java压栈重复字符串_Java 实例 – 压栈出栈的方法实现字符串反转
Java 实例 - 压栈出栈的方法实现字符串反转 以下实例演示了使用用户自定义的方法 StringReverserThroughStack() 来实现字符串反转: /* author by w3csc ...
- 【OpenGL】二十一、OpenGL 矩阵压栈与出栈 ( 不同类型矩阵变换先后顺序 | 渲染前不设置单位阵 | 压栈出栈原理分析 | 代码示例 )
文章目录 一.不同类型矩阵变换先后顺序 二.渲染前不设置单位阵 三.矩阵的压栈和出栈原理分析 四.矩阵的压栈和出栈代码示例 五.相关资源 一.不同类型矩阵变换先后顺序 对 OpenGL 中的 模型视图 ...
- 【数据结构】栈详解——压栈/入栈 | 弹栈/出栈 | 获取栈顶元素
栈 顺序栈 栈的定义 栈(stack)又名堆栈,它是一种运算受限的线性表.限定仅在表尾进行插入和删除操作的线性表.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈, ...
- JVM常用指令:常量,变量的压栈出栈指令
目录 常量压栈指令 局部变量表压栈指令 操作数栈出栈指令 通用指令 上一篇日志里用到的指令如bipush,iload等都是JVM常用的指令,它们有各自的分类,如bipush是常量压入操作数栈,iloa ...
- Go语言_数据结构_栈(包括入栈和出栈,表达式的入栈出栈详细过程代码实现)
入栈和出栈代码实现如下: package main import ("fmt""errors" )//使用数组来模拟一个栈的使用 type Stack stru ...
- 数据结构 - 栈(链表实现栈的入栈出栈)
学完链表和栈,数组模拟栈学完后,自己根据链表写了个链栈,虽然只是简单的实现了入栈出栈遍历.收获还是很大的. 在push这里思考了一会,思路是:传入一个数字,push里创建一个节点node,把数据加入n ...
- 栈的初始化,入栈,出栈,遍历操作(代码实现) [数据结构][Java]
栈的初始化,入栈,出栈,遍历操作(代码实现) 具体代码如下: package com.ffyc.stack;/*** 创建一个栈结构(使用数组实现)*/ public class ArrayStack ...
- 【Android 逆向】x86 汇编 ( push / pop 入栈 / 出栈 指令 | ret / retn 函数调用返回指令 | set 设置目标值指令 )
文章目录 一.push / pop 入栈 / 出栈 指令 二.ret / retn 函数调用返回指令 三.set 设置目标值指令 总结 一.push / pop 入栈 / 出栈 指令 push / p ...
最新文章
- linux笔记本没有insert,无法在Linux中将UTF8插入数据库MySQL(Can not insert UTF8 to Database MySQL in Linux)...
- kettle 日志存到mysql_kettle作业(job)调用转换,设置变量,写日志到数据库中【转】...
- java修改JFrame默认字体
- GCD 深入理解:第一部分
- cloud 异步远程调用_异步远程工作的意外好处-以及如何拥抱它们
- filepermission java,Java FilePermission getActions()方法与示例
- aop java 接口_Spring AOP实现接口验签
- 盒子模型代码_果冻公开课第五课:五分钟理清盒模型的前世今生
- Linux之find学习--详解
- Python 学习第一周
- 计算机网络(第八版) 谢希仁——知识点
- 游戏计算机重要参数,这真的很重要吗 游戏鼠标三大参数之谜
- 02 python入门
- Mysql8.0下载(网盘云盘)
- 16种设计思想 - Design for failure
- 新浪微博2012校招笔试题
- 传说中的移动员工工资表,也不知是真是假
- Java变态题目(持续更新)
- 安徽省计算机二级各部分分值分布,计算机二级各部分分值
- ORACLE:子查询
热门文章
- NLP学习笔记6--Lecture/语言模型/预处理/独热编码/word2vec/文本特征工程
- 解读西门子的工业软件帝国,巨头的数字化工业战略
- 微信平台开发——日历服务
- windows下判断网卡是否是虚拟网卡的方式
- 尝试寻找一些合作伙伴,产品相关培训咨询服务介绍(2B/支持在线)
- 我的世界服务器怎么添加信息框,我的世界服务器怎么添加指定建筑
- 虚拟机创建和配置的详细步骤
- unity加入屏幕左右边界碰撞体
- ubuntu 20使用kubeadm安装k8s 1.26
- cannot mysql server on_轻松解决cant connect to MySQL server on 'xxx'(10038)错误