精华内容
下载资源
问答
  • 什么是压栈操作?指令:PUSH src ;src为16位操作数 功能:SP (SP)-2 (SP) PUSH [BX] PUSH DS

    什么是压栈操作?指令:PUSH src ;src为16位操作数
    功能:SP <=(SP)-2
    (SP) <=src

    PUSH AX
    PUSH [BX]
    PUSH DS

    展开全文
  • 之前在Linux和VC上函数的局部变量的压栈一个字长,但是昨天 在验证引用到底占不占内存时,突然发现VS2017上函数局部变量压栈是 12个字节这就让我很抓狂了,下面时内存以及代码图 x86环境调试的 ...

    #函数的压栈
    函数的压栈这里就不多废话。直接附上其他大佬的解释。
    函数局部变量入栈顺序与变量输出关系

    之前在Linux和VC上函数的局部变量的压栈都是一个字长,但是昨天
    在验证引用到底占不占内存时,突然发现VS2017上函数局部变量压栈是12个字节这就让我很抓狂了,下面时内存以及代码图
    是x86debug环境调试的
    变量a的地址0x00affd8d0和变量b的地址0x00affdc4相差12个字节
    但是在Release版本下调试就是按4个字节压栈

    在这里插入图片描述

    在这里插入图片描述
    可能是项目的属性没有设置好,现在还没有精力去深挖希望看到的大神能指点一二,只能甩锅给Windows系统了。

    展开全文
  • 网上找的问题 现在贴出来应为比较常碰到 什么是堆和栈? 一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据...

     网上找的问题 现在贴出来应为比较常碰到

     

     

    什么是堆和栈?
    一个由c/C++编译的程序占用的内存分为以下几个部分
    1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
    2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
    3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
    4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
    5、程序代码区—存放函数体的二进制代码。



    函数压栈是怎么回事?
    函数压栈的本质是参数传递
    这又跟汇编语言连系起来了.汇编语言的过程即proc可以理解成函数
    比如一个最简单的计算两数之和函数
    如果用汇编来写估计是这样的

    sub proc
    pop ax ;从stack取a 并放在AX寄存器中
    pop bx ;从stack取b 并放在BX寄存器中
    add ax,bx ; 计算a+b
    ret //返回
    sub endp

    显然要调用这个函数,你应当先把b值push进stack,然后再push a
    因为stack是先进后出的
    所以调用汇编像这样
    比如计算4+5
    push 5;
    push 4;
    call sub; //返回值在AX中
    在这个例子中先压5或先压4得到的结果没有变化
    但大多数程序,如果参数的顺序错误将是灾难性的


    因为不管什么高级语言最终都要编译成汇编语言,然后是机器语言
    同样下面这个C程序,计算a+b值,必然会编译成上面的汇编代码
    int sub(int a ,int b) {return a+b;}
    所以C在调用这个函数sub时,必须要压栈(即传入参数)但这些工作,在C语言里,并不需要你来完成.你只要写出
    sub(7,9);
    编译器在编译成汇编时就会自动完成相关的压栈工作.

    根据函数调用方式和参数压入顺序目前存在三种约定:

    stdcall
    cdecl
    fastcall
    这都相关压栈顺序和栈的清理工作约定
    他们的细节都不相同,但有一点是肯定的,参数比须从右向左压入栈中
    stdcall中 函数必须自已清理栈
    cdecall 由调用者清除堆栈 C的默认函数调用方式 所以这样C支持可变参数
    fastcall 是把函数参数列表的前三个参数放入寄存器eax,edx,ecx,其他参数压栈

    源代码:
    int function(int a, int b)
    {
    return a + b;
    }

    void main()
    {
    function(10, 20);
    }

    1.__cdecl

    _function
    push ebp
    mov ebp, esp
    mov eax, [ebp+8] ;参数1
    add eax, [ebp+C] ;加上参数2
    pop ebp
    retn
    _main
    push ebp
    mov ebp, esp
    push 14h ;参数 2入栈
    push 0Ah ;参数 1入栈
    call _function ;调用函数
    add esp, 8 ;修正栈
    xor eax, eax
    pop ebp
    retn

    2.__fastcall

    @function@8
    push ebp
    mov ebp, esp ;保存栈指针
    sub esp, 8 ;多了两个局部变量
    mov [ebp-8], edx ;保存参数 2
    mov [ebp-4], ecx ;保存参数 1
    mov eax, [ebp-4] ;参数 1
    add eax, [ebp-8] ;加上参数 2
    mov esp, ebp ;修正栈
    pop ebp
    retn
    _main
    push ebp
    mov ebp, esp
    mov edx, 14h ;参数 2给EDX
    mov ecx, 0Ah ;参数 1给ECX
    call @function@8 ;调用函数
    xor eax, eax
    pop ebp
    retn

    3.__stdcall

    _function@8
    push ebp
    mov ebp, esp
    mov eax, [ebp] ;参数 1
    add eax, [ebp+C] ;加上参数 2
    pop ebp
    retn 8 ;修复栈
    _main
    push ebp
    mov ebp, esp
    push 14h ;参数 2入栈
    push 0Ah ;参数 1入栈
    call _function@8 ;函数调用
    xor eax, eax
    pop ebp
    retn

    展开全文
  • 通常所说的压栈是什么意思呢?
  • static void Main(string[] args) { Stack<Class1> stack = new Stack(); Class1 c=new Class1(); for (int i = 0; i ;i++ ) { c.n = i; stack.Push(c);... 我不明白为什么是这样?求解答,谢谢了
  • 在《c和指针》中已经说明了从右向左压栈的原因,这样可以保证生成汇编语言时这些参数相对于BP指向的栈位置的偏移量固定的,因为程序员有时为函数传递的参数会或多或或少,如果从左向右压栈,则从BP指向的位置到...

    从论坛回答中摘出3个比较有说服力的观点:

    一、

    先通过一个小程序来看一看:


    #include
    void foo(int x, int y, int z)
    {
    printf("x = %d at [%X]n", x, &x);
    printf("y = %d at [%X]n", y, &y);
    printf("z = %d at [%X]n", z, &z);
    }
    int main(int argc, char *argv[])
    {
    foo(100, 200, 300);
    return 0;
    }
    运行结果:
    x = 100 at [BFE28760]
    y = 200 at [BFE28764]
    z = 300 at [BFE28768]
     
    C程序栈底为高地址,栈顶为低地址,因此上面的实例可以说明函数参数入栈顺序的确是从右至左的。可到底为什么呢?查了一直些文献得知,参数入栈顺序是和具体编译器实现相关的。比如,Pascal语言中参数就是从左到右入栈的,有些语言中还可以通过修饰符进行指定,如Visual C++.即然两种方式都可以,为什么C语言要选择从右至左呢?
    进一步发现,Pascal语言不支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。
    因此,C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式。换句话说,如果不支持这个特色,C语言完全和Pascal一样,采用自左向右的参数入栈方式。
     
    这儿其实还涉及到C语言中调用约定所采用的方式,下面简单的介绍一下:
    __stdcall与C调用约定(__cdecl)的区别
     
    C调用约定在返回前,要作一次堆栈平衡,也就是参数入栈了多少字节,就要弹出来多少字节.这样很安全.
    有一点需要注意:stdcall调用约定如果采用了不定参数,即VARARG的话,则和C调用约定一样,要由调用者来作堆栈平衡.
    (1)_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 int f(void *p) -->> _f@4(在外部汇编语言里可以用这个名字引用这个函数)
    在WIN32 API中,只有少数几个函数,如wspintf函数是采用C调用约定,其他都是stdcall
    (2)C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。
    (3)__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。
    (4)thiscall仅仅应用于"C++"成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
    (5)naked call。 当采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。
      但是naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
      关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
      要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。
    综上,其实只有PASCAL调用约定的从左到右入栈的.而且PASCAL不能使用不定参数个数,其参数个数是一定的。
    简单总结一下上面的几个调用方式:
    调用约定
    堆栈清除
    参数传递
    __cdecl
    调用者
    从右到左,通过堆栈传递
    __stdcall
    函数体
    从右到左,通过堆栈传递
    __fastcall
    函数体
    从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈
    thiscall
    函数体

      (这些代码称作 prolog and epilog code,一般,ebp,esp的保存是必须的).


    this指针默认通过ECX传递,其他参数从右到左入栈









    二、

    从右向左压栈,不是规定,也不是因为栈先进后出的特性。在《c和指针》中已经说明了从右向左压栈的原因,这样可以保证生成汇编语言时这些参数相对于BP指向的栈位置的偏移量是固定的,因为程序员有时为函数传递的参数会或多或或少,如果从左向右压栈,则从BP指向的位置到参数的偏移量就会根据用户传入的参数数量而发生改变,这时编译器的识别难度就会大大增加,因此才会从右向左压栈。其实从我们平时的程序中就可以发现,如果我们的C程序函数参数比较少的话,传进去的前几个参数是可以正常接收的;如果参数传递地多的话,多余的参数也会丢弃,就是这个原理,你也会发现,从右向左可以非常好地降低编译器的操作难度。建议阅读C语言三剑客:《c和指针》《C专家编程》《C陷阱和缺陷》









    三、

    因为C++支持可变长函数参数。正是这个原 因使得C语言函数参数入栈顺序为从右至左。具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。C 程序栈底为高地址,栈顶为低地址。函数最左边确定的参数在栈上的位置必须是确定的,否则意味着已经确定的参数是 不能定位和找到的,这样是无法保证函数正确执行的。衡量参数在栈上的位置,就是离开确切的 函数调用点(call f)有多远。已经确定的参数,它在栈上的位置,不应该依 赖参数的具体数量,因为参数的数量是未知的!












    展开全文
  • 在本文的开头,我必须声明这一篇有目的性的文章。我有一个长辈最近得了尿毒症,所以我联系芯片之家的管理员并且得到他非常爽快的允许,在芯片之家的平台将人体的肾功能与开发技术结合起来写一篇文章发表在这里,...
  • 据说为了支持可变长参数 比如printf("%s%d%d\n", s, a, b); 格式字符串确定存在的,其他的都不确定的。 格式字符串最后一个入栈,位于栈顶,第一个出栈,这样就很方便。 函数调用的...
  • 什么printf的参数从右向左压栈

    千次阅读 2005-01-12 16:07:00
    两个call _printf调用的同一个地址的函数,在 * Referenced by a CALL at Addresses: |:0001.0210, :0001.022D | :0001.0AEB 55 push bp ;bp中存放着返回地址 :0001.0AEC 8BEC mov bp, sp  :0001.0AEE B8630C...
  • 大家都知道push和pop,在调用一个函数时CPU会将当前的环境保存起来我们称之为压栈(Push),在推出函数时CPU会恢复进入函数前的环境我们称之为出栈(pop),那么问题来了,你知道栈里面是什么样的吗?每一次压栈出栈...
  • printf压栈顺序

    2019-02-17 15:34:32
    先看一段代码,猜猜会是什么输出: x=1; printf(&quot;%d %d\n&quot;,x,x++); x=1; printf(&quot;%d %d\n&quot;,x++,x); x=1; printf(&quot;%d %d %d\n&quot;,x,x++,x); x=1; printf(&...
  • 明白了a++和++a的区别,我们再来看看作用在指针上面时会发生什么 https://zhidao.baidu.com/question/1696905843248898428.html 请问一下这步char *p=(char*)&a怎么理解 回答: &aint型变量a...
  • 对于OpenGL中矩阵堆栈的频繁压栈和出栈操作,一直不是很理解。今天搜集了一些资料,对频繁压栈、出栈的操作有了一定的了解。 OpenGL的矩阵堆栈类GLMatrixStack, 这个矩阵堆栈在初始化时候包含了单位矩阵。它的应用...
  • printf() 函数压栈方式

    千次阅读 2014-01-06 10:16:05
    C语言,C++函数调用压栈方式取决与编译器。但是一般编译器右序压栈的。下面介绍一下C 语言如何右序...}一般人会认为输出结果:3,45可是实际结果却:4,35为什么呢,原因就取决于C 语言的函数压栈方式右序的。
  • printf函数参数压栈顺序

    千次阅读 2015-08-26 20:51:43
    printf函数的参数的压栈顺序和求值顺序(VC++6.0编译器) 有以下程序段: ...答案为什么是:8,8 这是一道华为面试题。 这个题考的关键就是printf的运算顺序。这个是比较绕的一个问题,主要考验的是i++ 和++
  • 以下代码的输出是什么? #include main() { int b=3; int array[]={6,7,8,9,10}; int *ptr=arr; *(ptr++)+=123; printf("%d,%d\n",*ptr,*(++ptr)); } 几个输出结果分别如下: printf("%d\n",*ptr);此时ptr应指向第一...
  • 什么函数的压栈顺序从右向左呢,我们看一下printf函数的原型: printf(const char* format,…) printf函数一个不定参函数。 编译器通过format的%占位符的个数来获取参数的个数。 假设函数压栈顺序从左至...
  • <code class="language-java">public class MyStack { private int idx=0; private char[] data=new char[6]; public int getIdx() { return idx;...想请教各位这什么?</p>
  • 一直很疑惑为什么printf可以用%f输出double和float类型,因为一个8字节一个4字节,如何正确弹出堆栈呢? 在阅读arm汇编码后发现: 1.可变长度参数列表前必须有1个参数,从该参数就开始压栈了,并且压栈从右向左...
  • 当知道C函数的参数压栈顺序从右到左时,我觉得很奇怪,因为大多数情况下,人们的习惯从左到右的,难不成设计者学咱们中国古代写字从右到左的习惯不成?...那么,这什么呢? 要回答这个问题,就不得不谈一...
  • C语言初探 之 printf压栈顺序

    千次阅读 2013-01-08 19:53:44
    写在篇头: 在不同的编译器下结果不同,本例测试的环境为 devcpp5.3.0.1 。  ————————————————... 先看一段代码,猜猜会是什么输出: x=1; printf("%d %d\n",x,x++); x=1; printf("%d %d\n",x
  • 我们都知道Pascal的参数入栈顺序时自左向右的,但是为什么C语言会选择自右向左呢?这也C语言比pascal高级的一个地方-C语言通过这种参数入栈的顺序实现了对变长参数函数的支持! #include int Add1(int a, int b)...
  • cout和printf的压栈与a++和++a

    千次阅读 2015-04-01 17:01:37
    cout和printf的压栈与a++和++a   预先声明:这个问题在不同的编译器下,结果也相应不同。...先看一段代码,猜猜会是什么输出: x=1; printf("%d %d\n",x,x++); x=1; printf("%d %d\n",x++,x);
  • 闭包是js中的一个重要概念,我刚开始接触js时就对原型链和闭包非常的迷,刚出来工作面试的时候面了5、6家公司基本都会问什么是原型链,闭包知道吗,闭包有什么用这些问题;之前《JavaScript面向对象编程》讲了原型链...
  • 我们知道集合想要实现迭代,就必须实现Iterable接口,然后重写iterator()方法,从而返回Iterator对象,最后再利用Iterator对象的hasNext()方法和next()方法实现迭代,那么为什么不直接实现Iterator接口呢?...
  • 什么是栈?

    万次阅读 多人点赞 2019-05-08 10:57:59
    什么是栈? ps:文章来自于网络 当提及“栈”这个概念,很多初学者都会很迷茫。在C语言里,我们有一个内存区域叫做栈区。在单片机里,我们又常常听到一个操作叫做压栈。而在算法中,我们也有一个同名结构叫做栈。 ...
  • 文章目录1 函数的执行流程1.1 字节码了解压栈过程1.2 嵌套函数的压栈2 递归2.1 递归函数2.2 递归的性能2.3 递归的...函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放...
  • 文章目录1. 函数的执行流程1.1. 字节码了解压栈过程1.2. 嵌套函数的压栈2. 递归2.1. 递归函数2.2. 递归的性能2.3. 递归的优化2.4. 间接递归2.5....函数的执行需要对函数进行压栈的,什么是压栈呢,简而言...
  • 凡是-- ++ 以及 没有++ --的在前的都最后的结果 ,++ --再后的就是该是什么值就是什么值对于a++的结果,有ebp寻址函数栈空间来记录中间结果的,在最后给printf压栈的时候,再从栈中把中间结果取出来;...
  • 1 函数的执行流程函数的执行需要对函数进行压栈的,什么是压栈呢,简而言之就是在函数执行时在栈中创建栈帧存放需要变量以及指针的意思。具体涉及的知识非常多,这里就已一个Python脚本简单进行分析。当我们运行上面...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 640
精华内容 256
关键字:

什么是压栈