• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 浅谈C语言函数调用参数压栈的相关问题

浅谈C语言函数调用参数压栈的相关问题

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了浅谈c语言的位运算,浅谈c语言,浅谈,浅谈小学语文阅读教学,浅谈如何转化后进生等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

参数入栈的顺序

以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左。参数的入栈顺序主要看调用方式,一般来说,__cdecl 和__stdcall 都是参数从右到左入栈。

看下面的代码:

#include <stdio.h>

int test(int a, int b)
{
  printf("address of a %x.\n", &a);
  printf("address of b %x.\n", &b);
  return 0;
}

int main()
{
  test(1, 2);
  return 0;
}
</div>

在64位Ubuntu的系统下的运行结果是:

address of a 1ec62c. 
address of b 1ec628.
</div>

32位Ubuntu的结果是:

address of a bfd03290. 
address of b bfd03294.
</div>

可以看出,首先,不同的体系结构,栈增长的方向也不同,有的是从低地址向高地址方向增长,有的是从高地址向低地址方向增长。

可以用以下的代码来判断栈的增长方向:

typedef enum {
  LOW_TO_HIGH,
  HIGH_TO_LOW,
  LEFT_TO_RIGHT,
  RIGHT_TO_LEFT,
}stack_direc_t;

int stack_grow_direc()
{
  static char *p = NULL;
  char c;

  if (p == NULL) {
    p = &c;
    stack_grow_direc();
  }
  else {
    printf("First in stack address is %x.\n", p);
    printf("Second in stack address is %x.\n", &c);
    if (&c > p) {
      printf("Stack grows from low address to high address!\n");
      return LOW_TO_HIGH;
    }
    else {
      printf("Stack grows from high address to low address!\n");
      return HIGH_TO_LOW;
    }
  }
}
</div>

函数调用时栈里都有什么

以参数从左到右入栈为例:

push arg0 -- High Address
push arg1
...
push argn
push eip
push ebp -- Low address
</div>

32位系统和64位系统函数调用时,参数入栈方式有不同么?

这个问题在不久之前被人问题,当时傻了,我一直以来只关注过32位系统的参数入栈方式,一直以为64位系统也是一样,没有什么不同,现在归纳起来有两点:

64位系统先把传入参数放在寄存器里面,在被调函数的具体实现中把寄存器的值入栈,然后再去栈中取参数

64位系统栈中参数存放的顺序是从左至右的(因为先经历了寄存器传值)

看下面的反汇编:


C代码同上面一样
Ubuntu 32位反汇编:
int main()
{
 804846d:  55           push  %ebp
 804846e:  89 e5          mov  %esp,%ebp
 8048470:  83 e4 f0        and  $0xfffffff0,%esp
 8048473:  83 ec 10        sub  $0x10,%esp
  test(1, 2);
 8048476:  c7 44 24 04 02 00 00  movl  $0x2,0x4(%esp)
 804847d:  00 
 804847e:  c7 04 24 01 00 00 00  movl  $0x1,(%esp)
 8048485:  e8 8a ff ff ff     call  8048414 <test>
  return 0;
 804848a:  b8 00 00 00 00     mov  $0x0,%eax
}
int test(int a, int b)
{
 8048414:  55           push  %ebp
 8048415:  89 e5          mov  %esp,%ebp
 8048417:  83 ec 18        sub  $0x18,%esp
  printf("address of a %x.\n", &a);
 804841a:  b8 60 85 04 08     mov  $0x8048560,%eax
 804841f:  8d 55 08        lea  0x8(%ebp),%edx
 8048422:  89 54 24 04       mov  %edx,0x4(%esp)
 8048426:  89 04 24        mov  %eax,(%esp)
 8048429:  e8 12 ff ff ff     call  8048340 <printf@plt>

  return 0;
 8048466:  b8 00 00 00 00     mov  $0x0,%eax
}
Ubuntu 64位反汇编:
int main()
{
 40056e:  55           push  %rbp
 40056f:  48 89 e5        mov  %rsp,%rbp
  test(1, 2);
 400572:  be 02 00 00 00     mov  $0x2,%esi
 400577:  bf 01 00 00 00     mov  $0x1,%edi
 40057c:  e8 ac ff ff ff     callq 40052d <test>
  return 0;
 400581:  b8 00 00 00 00     mov  $0x0,%eax
}
int test(int a, int b)
{
 40052d:  55           push  %rbp
 40052e:  48 89 e5        mov  %rsp,%rbp
 400531:  48 83 ec 10       sub  $0x10,%rsp
 400535:  89 7d fc        mov  %edi,-0x4(%rbp)
 400538:  89 75 f8        mov  %esi,-0x8(%rbp)
  printf("address of a %x.\n", &a);
 40053b:  48 8d 45 fc       lea  -0x4(%rbp),%rax
 40053f:  48 89 c6        mov  %rax,%rsi
 400542:  bf 14 06 40 00     mov  $0x400614,%edi
 400547:  b8 00 00 00 00     mov  $0x0,%eax
 40054c:  e8 bf fe ff ff     callq 400410 <printf@plt>

  return 0;
 400567:  b8 00 00 00 00     mov  $0x0,%eax
}
</div>

看32位的ubuntu操作系统, 8048476: 的确是把参数直接入栈,2先入栈,1后入栈。

 8048476:  c7 44 24 04 02 00 00  movl  $0x2,0x4(%esp)
 804847d:  00 
 804847e:  c7 04 24 01 00 00 00  movl  $0x1,(%esp)
 8048485:  e8 8a ff ff ff     call  8048414 <test>
</div>

再来看64位的ubuntu操作系统,2 和1根本就没有放入到栈中,而是放到了寄存器esi和edi中。

 40056f:  48 89 e5        mov  %rsp,%rbp
 test(1, 2);
 400572:  be 02 00 00 00     mov  $0x2,%esi
 400577:  bf 01 00 00 00     mov  $0x1,%edi
 40057c:  e8 ac ff ff ff     callq 40052d <test>
</div>

再来看64位系统test的实现,先把edi入栈,再把esi入栈,这就是为什么函数看起来像是从左到右入栈的原因了。

40052d:  55           push  %rbp
40052e:  48 89 e5        mov  %rsp,%rbp
400531:  48 83 ec 10       sub  $0x10,%rsp
400535:  89 7d fc        mov  %edi,-0x4(%rbp)
400538:  89 75 f8        mov  %esi,-0x8(%rbp)
</div>

以上就是小编为大家带来的浅谈C语言函数调用参数压栈的相关问题的全部内容了,希望对大家有所帮助,多多支持~

</div>
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • 浅谈c语言中一种典型的排列组合算法
  • 浅谈C语言共用体和与结构体的区别
  • 浅谈C语言函数调用参数压栈的相关问题
  • 浅谈C语言之字符串处理函数
  • 浅谈c语言中类型隐性转换的坑
  • 浅谈时间戳与日期时间互转C语言
  • 浅谈C语言的字符串分割
  • 浅谈C语言编程中程序的一些基本的编写优化技巧
  • 浅谈C语言中的强符号、弱符号、强引用和弱引用
  • 浅谈C语言中结构体的初始化

相关文章

  • 2017-05-28VC中使用ADO开发数据库应用程序简明教程
  • 2017-05-28求32位机器上unsigned int的最大值及int的最大值的解决方法
  • 2017-05-28C++实现对输入数字组进行排序
  • 2017-05-28使用C++绘制GDI位图的基本编写实例
  • 2017-05-28C++设计模式编程中Template Method模板方法模式的运用
  • 2017-05-28内联函数inline与宏定义深入解析
  • 2017-05-28C++中与输入相关的istream类成员函数简介
  • 2017-05-28C++中带空格字符串的输入问题解决
  • 2017-05-28C语言 常量详解及示例代码
  • 2017-05-28C++ decltype类型说明符

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 编写C++程序使DirectShow进行视频捕捉
    • C++加密解密php代码的方法
    • C++实现Linux下弹出U盘的方法
    • C/C++的浮点数在内存中的存储方式分析及实例
    • c++类构造函数详解
    • C++中给二维指针分配内存(实现代码)
    • C语言中的函数指针基础学习教程
    • 贪心算法的C语言实现与运用详解
    • C++动态内存分配(new/new[]和delete/delete[])详解
    • C++卸载程序功能示例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有