1. 参数入栈的顺序
问题:函数参数的计算次序是依赖编译器实现的,那么函数参数的入栈次序是如何确定的?
调用约定
2. 调用约定——规定参数是如何传递到栈给栈,以及栈的维护方式
- 当函数调用发生时,参数会传递给被调用的函数,而返回值会被返回给函数调用者;
-
调用约定描述参数如何传递到栈中以及栈的维护方式。
(1) 阐述传递顺序
(2)调用栈清理 - 调用约定是预定义的,可理解为调用协议
- 调用约定通常用于库调用和库开发的时候
- 从右到左依次入栈:__stdcall, __cdecl(C语言中默认), __thiscall
- 从左到右依次入栈:__pascal, __fastcall
- 调用约定并不是语言的一部分,是编译器的一部分,因为语言仅仅规定了语法和语义,并没有强调如何实现,编译器是规范的一种实现,考虑的因素较多,调用约定就是其中的细节之一。
3. 问题:如何编写一个计算n个数平均值的函数?
程序说明:编写函数计算平均值
#include <stdio.h>
float average(int arr[], int n)
{
float sum = 0;
int i = 0;
if(arr != NULL)
{
for(i=0; i<n; i++)
{
sum += arr[i];
}
}
return sum / n;
}
int main()
{
int arr[] = {1, 2, 3, 4, 5};
printf("the average = %f\n", average(arr, 5));
return 0;
}
输出结果:
the average = 3.000000
问题: 如果不先生成数组,而是直接输入数据该如何求平均数?
4. 可变参数
- C语言中可以定义参数可变的函数
- 参数可变的函数的实现依赖stdarg.h头文件
- stdarg由standard arguments简化而来;
- va_list——函数参数集合
- va_arg——具体参数值
- va_start——标识参数访问的开始
- va_end——标识参数访问的结束
编程说明:通过可变参数计算平均值
#include <stdio.h>
#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("%f\n", average(5, 1, 2, 3, 4, 5));
printf("%f\n", average(4, 1, 2, 3, 4));
return 0;
}
计算结果:
3.000000
2.500000
5. 可变参数的限制
- 可变参数必须从头到尾按照顺序逐个访问
- 参数列表中至少要存在一个确定的命名参数
- 可变参数函数无法确定实际存在的参数的数量
- 可变参数函数无法确定参数的实际类型
注意: 在va_arg中第二个参数需要指定类型,如上面程序的int。如果指定了错误的类型,那莪结构是不可预测的。
6. 小结
- 调用约定指定了函数参数的入栈顺序以及栈的清理方式;
- 可变参数是C语言提供的一种函数设计技巧;
- 可变参数的函数提供了一种更方便的函数调用方式;
- 可变参数必须顺序的访问, 无法直接访问中间的参数值。