函数分析
1. 说明
程序的内存布局
- 堆栈段在程序运行后才正式存在,是程序运行的基础
.bss
段存放的是未初始化的全局变量和静态变量.text
段存放的是程序的可执行代码.data
段保存的是已经初始化了的全局变量和静态变量.rodata
段存放程序中的常量值,如字符串常量
- 如图
函数的活动记录
- 临时变量域:存放临时变量的值,如k++的中间结果
- 局部变量域:用来存放函数本次执行中的局部变量
- 机器状态域:用来保存调用函数之前有关机器状态的信息,包各种寄存器的当前值和返回地址等
- 实参数域:存放函数的实参信息
- 返回值域:为调用者函数存放返回值
- 如图:
2.函数的顺序点
说明:
- 程序中存在顺序点,顺序点是执行过程中修改变量值最晚的时刻
- 在程序到达顺序点的时候,之前做的一切修改必须反映到后续的访问中
- 顺序点的判断
- 每个完整的表达式结束时
&&
,||
,?:
,逗号表达式
的每个运算对象计算之后- 函数调用中对所有实际参数的求值完成之后(进入函数体之前)
示例:
void f(int k, int j) { /*结果是i = 2, j = 1*/ /* 在执行函数前,先对实参i++进行计算的到2,把内存中指向实参i的内容改变为2, 然后复制给形参k,由于i++是告诉系统我自己可以最后取值(目前是在临时变量域取值1),所以i的值还是1并且复制给j, 在执行完后,i才真正的变成2 */ printf("k = %d, j = %d\n", i, j); } int main() { int k = 2; int a = 1; k = k++ + k++; //顺序点是最后的分号 printf("k = %d\n", k); //k的值最后为6(考虑运算符优先级) /*先进行两个k++操作,但是没有到顺序点,所不能立即生效, 然后执行中间的加法操作 k = 4 然后到达顺序点,然后进行两次++操作,然后赋值给最终的k*/ if(a-- && a) //a-- begin a = 0 { printf("a = %d\n", a); } int i = 1; f(i, i++); //顺序点是函数执行前,参数列表要计算完 return 0; }
3.可变参数
说明:
- 参数可变函数依赖于头文件
stdarg.h
- 可变参数必须从头到尾按照顺序逐个访问
- 参数列表中至少存在一个确定的明名参数
- 可变参数宏无法判断实际存在的参数的数量
- 可变参数宏无法判断参数的实际类型
va_list
变量与va_start
,va_end
,和va_arg
配合使用能够访问参数值va_arg
中如果指定了错误的类型,那么结果是不可预测的
无法直接访问可变参数列表中间的参数值
- 参数可变函数依赖于头文件
示例:
#include <stdarg.h> float average(int n, ...) { va_list args; //使用可变参数列表声明变量 int i = 0; float sum = 0; va_start(args, n); //开始可变参数的读取 for(i = 0; i < n; ++i) { sum += va_arg(args, int); //按顺序读取可变参数 } va_end(args); //结束可变参数的读取 return sum / n; } int main() { printf("%-.3f\n", average(5, 1, 2, 3, 4, 5)); printf("%-.3f\n", average(4, 1, 2, 3, 4)); return 0; }