C系列总结3 & 剖析函数调用及可变参数--详解栈帧
2017-07-27 00:25
513 查看
- - - - -草稿- - - - -栈帧部分待细化
前言:
不积跬步,无以至千里栈帧是编程书目中鲜有提及的概念,但其与函数调用细节息息相关,在此做简单总结。
无参考书目
主要参考资料:
互联网
以及
Write by 张鹏霄, zpx736312737@126.com
概要:
定义调用细节
可变参数函数
定义
易知,函数的调用需要申请栈,建立栈帧栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构
栈帧对应着一个未运行完的函数,其中保存了该函数的返回地址和局部变量。
调用细节
首先,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。
通过寄存器ebp(维护栈底),esb(维护栈顶),我们维护一个正在运行的函数的栈帧:
定义一个函数fuc
int fuc() { int a = 1; return a; } int main() { int num=fuc(); Next command: getchar(); getchar(); return 0; }
Vs2017下调试代码转到反汇编
int fuc() { 001E16A0 push ebp 001E16A1 mov ebp,esp 001E16A3 sub esp,0CCh 001E16A9 push ebx 001E16AA push esi 001E16AB push edi 001E16AC lea edi,[ebp-0CCh] 001E16B2 mov ecx,33h 001E16B7 mov eax,0CCCCCCCCh 001E16BC rep stos dword ptr es:[edi] int a = 1; 001E16BE mov dword ptr [a],1 return a; 001E16C5 mov eax,dword ptr [a] }
图解汇编代码:(图片来自网络,部分内容与上述代码有出入,不影响意思)
我们将状态分为入栈前和入栈后,在入栈前,main函数作为新栈帧之前的栈帧如下
之后开始入栈,push mov后,将参数与返回地址(开辟一空间记录返回地址,可以理解为代码块Next command:的地址)
移动ebp指向当前栈帧
之后为a开辟空间并记录值,移动esp
之后出栈,将值返回给返回地址的值
可变参数函数的设计
假设我们有一需求,将未知个数字求和。以函数重构的形式
int fuc(int a) int fuc(int a, int b) int fuc(int a, int b, int c) ...
显然不合理
易想到,printf参数为可变参数
vs下调取帮助,查看printf函数参数表
int printf( const char *format [, argument]... );
事实上,编译器提供一种语法,实现函数可变参数,以求和函数为例
int(int n,...)//n为参与求和的变量个数,...表示可变参数
求和函数代码如下
#include<stdarg.h>//va_list va_start等的定义 int fuc(const int n,...) { va_list arg;//va_list为宏定义,可以建立一个数组 va_start(arg, n);//va_list为宏定义,可以根据第一个参数n的地址找到可变参数第一个元素(联想栈帧的原理) int sum = 0; for (int i = 0; i < n; i++) { sum += va_arg(arg, int);//va_list为宏定义,访问该元素并将地址跳到下一元素 } va_end(arg);//va_list为宏定义,出于安全性考虑,将指针指为空 return sum; }
其中上述涉及到的宏定义在新版vs中多层嵌套,研究vs6.0源代码可得到可读性较强的宏定义如下(可不必研究细节)
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 )
综上,结合栈帧原理,我们可以使用可变参数接受未知个数参数。
相关文章推荐
- 黑马程序员—反射调用main方法的问题总结(涉及可变参数)(反射参数是一个数组的函数要小心)
- Swift如何调用Objective-C的可变参数函数详解
- 详解Objective-C可变参数函数定义
- C可变参数详解及其函数设计
- 可变参数列表的函数调用 va_list va_start va_arg va_end
- javascript入门系列演示·函数的定义以及简单参数使用,调用函数
- C/C++中用va_start/va_arg/va_end实现可变参数函数的原理与实例详解
- C可变参数详解及其函数设计
- 【应聘笔记系列】堆栈、栈帧与函数调用过程分析
- C/C++中可变参数的原理 与函数调用约定
- printf 系列函数 与 可变参数函数
- C语言可变参数函数详解示例
- 动态的调用可变参数函数
- sprintf系列函数和可变参数函数
- sprintf系列函数和可变参数函数
- 可变参数函数总结
- php引用(&)变量引用,函数引用,对象引用和参数引用用法详解
- 动态的调用可变参数函数
- Java常见笔试面试题目深度剖析系列之:Java方法参数传递详解
- 关于C语言可变参数函数的一些研究和总结