C语言学习之路--第十七站 可变参数

在函数中定义一个接受可变数量的参数,定义方式为:int func_name(int arg1,...);

在使用时代码抽象如下:

go 复制代码
int func(int, ... )  {
   .
   .
   .
}

int main() {
   func(2, 2, 3);
   func(3, 2, 3, 4);
}
  • 需要使用 stdatg.h 头文件,该文件提供了实现可变参数功能的函数和宏
  • 最后一个参数写成省略号,三个点。 int 表示要传递的可变参数总和。
  • 使用步骤:
    • 定义一个函数,最后参数为省略号,省略号可以设置自定义参数
    • 在函数中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的
    • 使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。 宏 va_start() 是在 stdarg.h 头文件中定义的
    • 使用 va_arg() 宏 和 va_list 变量来访问参数列表中的每个项
    • 使用宏 va_end() 来清理赋予 va_list 变量的内存
  • 常见的宏使用
    • va_start(ap , last_arg) 初始化可变参数列表。 ap 是 va_list 类型的变量,va_arg 是最后一个固定参数的名称,也可以是可变参数之前的参数。该宏将 ap 指向可变参数列表中的第一个参数
    • va_arg(ap , type) 获取可变参数列表中下一个参数。ap 是va_list 类型的变量,type是下一个参数的变量。该宏返回类型为 type 的值,将 ap 指向下一个参数
    • va_end(ap) 结束可变参数列表的访问。 ap 是va_list 类型的变量,该宏将 ap 设置为 NULL

写一个带有可变参数的函数,返回他们的平均值:

scss 复制代码
#include<stdarg.h>

// 可变参数测试方法
double average(int num, ...)
{
        va_list valist;
        double sum = 0.0;
        int i;

// 为 num 个参数初始化
va_start(valist, num);

//访问所有赋给 valist 的参数
for (i = 0; i < num; i++)
{
	sum += va_arg(valist, int);
}

//清理为 valist 保留的内存
va_end(valist);

return sum / num;
}

printf("Average of 2,3,4,5 = %f \n", average(4, 2, 3, 4, 5));
printf("Average of 10,23,34,2,3,5 = %f\n", average(6,10,23,34,2,3,5));

其实看上面内容还很空洞,并没有理解。下面再参考一些网友的留言。

arduino 复制代码
#include <stdio.h>

void debug_arg(unsigned int num, ...) 
{
    unsigned int i = 0;
    unsigned int *addr = &num;
    for (i = 0; i <= num; i++) 
    {
        /* *(addr + i) 从左往右依次取出传递进来的参数,类似于出栈过程 */
        printf("i=%d,value=%d\r\n", i, *(addr + i));
    }
}
int main(void)
{
    debug_arg(3, 66, 88, 666);
    return 0;
}

下面是源代码, 进行拆分消化一下。

arduino 复制代码
// 64 位机器用 8 字节对齐, 32 位 4 位对齐
#ifdef X64
#defin t long long
#else
#define t int
#endif
  • 判断一下机器类型,64位机器用 8 字节对齐,32位 4 字节对齐。

arduino 复制代码
//VA_LIST套宏中可以使用,用来改变INTSIZEOF中t的类型
//固定参数详见
void test(int a, double b, char* c)
{
    char *p = (char*)&a;
    //因为&a = void 类型 需要转换,void * =&a 不需要转换但是使用时要转换
    printf("%p %p %p\n", &a, &b, &c);
    //观察地址变化
    printf("%p %s",(p+8),*(char**)(p+8+8));//64位机器时加8内存大小8字节对齐
    return;
}

//可变参数实验
void test1(char* s,char *st,...)
{
    char *ppt =(char*)&s;
    //printf("%p %p %p %p,",ppt,&s,&st,(char*)ppt+8);
    printf("%p %p %p %p\n", ppt, &s, &st, ppt + 8);
    printf(" %d\n",*(int*)(ppt + 4+4));//当是X64就加8 X86就加4因为内存对齐规则
    return;
}

int main()
{
    char *p = "Hello world";
    test1("111","eee",45234,23);
    //test(2, 2.2, "Hello world");x
    void *s = &p;
    printf("%s", *(char**)s);
    return 0;
}

其内涵主要在 va_list /start / arg/ end 的使用以及内存的理解。

此部分内容暂未完成,仍有很多需要理解的地方。

相关推荐
玩转C语言和数据结构2 小时前
C语言编程入门攻略(最新学习路线,适合新手小白)
c语言·c语言入门·c语言下载·c语言知识点总结·c语言自学·c语言教程·c语言怎么学
Bona Sun2 小时前
单片机手搓掌上游戏机(十四)—pico运行fc模拟器之电路连接
c语言·c++·单片机·游戏机
无限进步_3 小时前
C语言数组元素删除算法详解:从基础实现到性能优化
c语言·开发语言·windows·git·算法·github·visual studio
松涛和鸣3 小时前
16、C 语言高级指针与结构体
linux·c语言·开发语言·数据结构·git·算法
口袋物联5 小时前
设计模式之适配器模式在 C 语言中的应用(含 Linux 内核实例)
c语言·设计模式·适配器模式
!停6 小时前
函数递归的应用
c语言
feng_you_ying_li8 小时前
Detailed explanation of being processing
c语言
玖剹8 小时前
递归练习题(四)
c语言·数据结构·c++·算法·leetcode·深度优先·深度优先遍历
序属秋秋秋12 小时前
《Linux系统编程之进程环境》【环境变量】
linux·运维·服务器·c语言·c++·操作系统·系统编程
Yue丶越13 小时前
【C语言】数据在内存中的存储
c语言·开发语言·网络