可变参数列表源码分析及函数设计

本文深入解析C语言中可变参数的实现原理,包括stdarg.h头文件的使用,va_list、va_start、va_arg和va_end宏的详细解释,以及如何模拟实现printf函数。通过实例演示如何求不同个数的平均数,揭示了可变参数列表的访问规则和限制。

目录

一、什么是可变参数?

二、可变参数的实现

三、可变参数源码

四、模拟实现printf函数


一、什么是可变参数?

    可变参数是一个比较有意思的实现,通过将函数实现为可变参数的形式,可以使得函数可以接受1个以上的任意多个参数。      

    一般情况下,我们写的函数参数的数目是固定不变的,调用函数时要给出相应的实参,但有些时候,为了做同样的事情,由于参数个数不同,我们总不能写很多个函数,这就有点冗余了。比如:我们想求两个数或10个数的平均数,可以用同一个函数实现。

二、可变参数的实现

C语言头文件stdarg.h中提供了一个va_list的数据类型和va_start,va_arg,va_end三个宏

        1.声明一个 va_list 类型的变量 arg ,它用于访问参数列表的未确定部分。
        2.这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量名,第2个参数是省略号前最后一个有名字的参数。初始化过程把 arg 变量设置为指向可变参数部分的第一个参数。
        3.为了访问参数,需要使用 va_arg ,这个宏接受两个参数: va_list 变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。va_arg````返回这个参数的值,并使用 va_arg```指向下一个可变参数。
        4.最后,当访问完毕最后一个可变参数之后,我们需要调用 va_end 。

举例:求不同个数的平均数

int avg(int n, ...)
{
	va_list arg;
	va_start(arg, n);//arg指向可变参数的第一个参数
	int sum = 0;
	for (int i = 0; i < n; i++)
	{
		sum += va_arg(arg, int);//返回该参数的值并自动指向下一个参数
	}
	va_end(arg);
	return sum / n;
}
int main()
{
	int a = 1, b = 2, c = 3,d=10;
        printf("%d\n", avg(4,a, b, c,d));
	printf("%d\n", avg(2,a, c));
}

并不是所有的函数都可以使用可变参数列表,使用时要注意:

1>可变参数必须从头到尾逐个访问。如果在访问了几个可变参数之后想半途终止,这是可以的,但是,如果一开始就访问参数列

表中间的参数,那是不行的。

2>参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。

3>这些宏是无法直接判断实际存在参数的数量;这些宏无法判断每个参数类型。
 

三、可变参数源码

可变参数的实现过程是使用宏的封装。只要完成替换,就可以自行分析了。

1.va_list:是一种数据类型,可以说就是char*。

        typedef char* va_list;

2.va_start:初始化arg为未知参数列表的第一个参数的地址,va_start其实是一个宏函数,第一个参数是一个可以表示后面的参数个数和参数类型的char* ,第二个参数表示就是用于指明arg是指针名,n代表第一个参数,用于表示起始端。

  #define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

  #define _ADDRESSOF(v) (&(v))
  
  #define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))

3.va_arg:返回当前参数再指向下一个参数。

 #define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

 #define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))

通过 va_arg(arg, int) 拿到后面的参数:

1.ap表示可变参数指针,而t表示数据类型。使用 ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) ,传入char,float,double等小于4字节的类型,都会返回4。如果类型大小是,5,6,7的话,则返回8。即返回当前一组数中靠近4的倍数的值。 

2.( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) 该表达式先让指针ap加上4字节的大小,再把减去4字节大小处所对应的值返回。用t强制类型转换,再解引用,此处 t是传入参数的数据类型。

4.va_end:结束。arg赋值为空指针。

  #define __crt_va_end(ap)        ((void)(ap = (va_list)0))

四、模拟实现printf函数

模拟实现printf函数,可完成下面的功能 
//能完成下面函数的调用。 
//print("s ccc d.\n","hello",'b','o','y',100); 
 

void print_num(int n)
{
	if (n > 9)
		print_num(n / 10);
	putchar(n % 10 + '0');
}
void print(const char* format, ...)
{
	va_list arg;
	va_start(arg, format);
	
	while (*format)
	{
		switch (*format)
		{
			case 's':
			{
				char *str = va_arg(arg, char*);
				while (*str)
				{
					putchar(*str);
					str++;
				}
			}
				break;
			case 'c':
			{
				char ch = va_arg(arg, char);
				putchar(ch);
			}
			break;
			case 'd':
			{
				int tmp = va_arg(arg, int);
				print_num(tmp);
			}
			break;
			default:
				putchar(*format);
				break;
		}
		format++;
	}
}

 

 

 

 

 

 

内容概要:本文详细介绍了一个基于Java和Vue的联邦学习隐私保护推荐系统的设计与实现。系统采用联邦学习架构,使用户数据在本地完成模型训练,仅上传加密后的模型参数或梯度,通过中心服务器进行联邦平均聚合,从而实现数据隐私保护与协同建模的双重目标。项目涵盖完整的系统架构设计,包括本地模型训练、中心参数聚合、安全通信、前后端解耦、推荐算法插件化等模块,并结合差分隐私与同态加密等技术强化安全性。同时,系统通过Vue前端实现用户行为采集与个性化推荐展示,Java后端支撑高并发服务与日志处理,形成“本地训练—参数上传—全局聚合—模型下发—个性化微调”的完整闭环。文中还提供了关键模块的代码示例,如特征提取、模型聚合、加密上传等,增强了项目的可实施性与工程参考价值。 适合人群:具备一定Java和Vue开发基础,熟悉Spring Boot、RESTful API、分布式系统或机器学习相关技术,从事推荐系统、隐私计算或全栈开发方向的研发人员。 使用场景及目标:①学习联邦学习在推荐系统中的工程落地方法;②掌握隐私保护机制(如加密传输、差分隐私)与模型聚合技术的集成;③构建高安全、可扩展的分布式推荐系统原型;④实现前后端协同的个性化推荐闭环系统。 阅读建议:建议结合代码示例深入理解联邦学习流程,重点关注本地训练与全局聚合的协同逻辑,同时可基于项目架构进行算法替换与功能扩展,适用于科研验证与工业级系统原型开发。
源码来自:https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值