自己实现一个简单可变参数函数

什么是可变参数

在C语言编程中有时会遇到一些参数可变的函数、例如printf()、scanf(),其函数原型为:

int printf(const char *format,...)
int scanf(const char *format,...)

它除了有一个参数format固定以外,后面的参数其个数和类型都是可变的,用三个点"..."作为参数占位符号。

参数列表的构成

任何一个可变参数都可以分为两个部分:固定参数和可选参数。至少有一个固定参数,其声明与普通函数声明相同;可选参数由于数目不定(0个或以上),声明时必须用"..."表示。固定参数和可选参数共同构成的可变参数函数的参数列表。

实现原理

C语言中使用va_list系列变参宏实现变参函数,此处va意为variable-argument(可变参数)。
x86平台VC6.0集成开发环境中,stdarg.h头文件内变参宏定义如下:

typedef char *va_list;

// 把n 圆整到sizeof(int)的倍数
#define _INTSIZEOF(n)((sizeof(n)+sizeof(int)-1)& ~(sizeof(int)-1))

//初始化 ap指针,使其指向第一个可变参数。v是变参列表的前一个参数
#define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v))

//该宏返回当前变参值,并使ap指向列表中的下一个变参
#define va_arg(ap,type) (*(type *)((ap +=_INTSIZEOF(type) - _INTSIZEOF(type))))

//将指针ap置为无效,结束变参的获取
#define va_end(ap)  (ap = (va_list)0)

_INTSIZEOF(n)****
_INTSIZEOF宏考虑到某些系统需要内存地址对齐。从宏名看应按照sizeof(int)即栈粒度对齐,参数在内存中的地址均为sizeof(int) = 4的倍数。
例如,若1<= sizeof(n)<=4,则_INTSIZEOF(n) = 4;
若5<=sizeof(n)<=8,则_INTSIZEOF(n) = 8。

va_start(ap,v)
va_start宏首先根据(va_list)&v得到参数v在栈中的内存地址,加上_INTSIZEOF(v)所占内存大小后,使ap指向v的下一个参数。在使用的时候,一般用这个宏初始化ap指针,v是变参列表的前一个参数,即最后一个固定参数,初始化的结果是ap指向第一个变参。

va_arg(ap,type)
这个宏取得type类型的可变参数值。首先ap +=_INTSIZEOF(type),即ap跳过当前可变参数而指向下个变参的地址;然后ap-_INTSIZEOF(type)
得到当前变参的内存地址,类型转换后解引用,最后返回当前变参值。

自己实现一个可变参函数

#include <stdio.h>
//模拟可变参数函数,用于计算多个整数的和
int sum(int count, int first, ...)
{
    int total = first;
    //创建指向第一个可变参数的指针
    int* p = &first;
    //遍历所有的参数
    for (size_t i = 1; i < count; i++)
    {
        p++;//移动到下一个参数位置
        total += *p;//解引用并添加
    }
    return total;
}
int main()
{
    int result = sum(5,1,2,3,4,5);
    printf("sum::%d\r\n", result);
    return 0;
}

可变参应注意的事项

  • 确保第一个参数指定可变参数的数量和类型
  • 使用正确的类型读取参数
  • 可变参数的类型一致性
  • 可变参数不能是数组和结构体
  • 可变参函数不进行类型检查
  • 小心浮点类型的默认提升
  • 避免在循环中使用可变参数
  • 使用标准库宏来避免手动解析
posted @ 2024-11-03 22:16  不会笑的孩子  阅读(13)  评论(0编辑  收藏  举报