C/C++省略符形参原理和使用

本文介绍了C/C++中省略符形参的使用,包括va_start、va_arg和va_end的实现细节。通过实例展示了如何在不同环境下获取和使用省略参数。强调了正确指定参数数量和类型的重要性,并提出了几种常见的使用策略,如添加数量参数、使用标志性数据等。
摘要由CSDN通过智能技术生成

前言

最近重新翻了一遍《C++ Primer》,看了一下省略符形参的相关内容,发现只有半页左右的内容(可能后面还有),只是提了一下有这种形参语法,作用是兼容C语言,但没有提及如何使用这种形参,以及要注意什么。个人也很好奇不定量形参的使用以及实现方法,不定形参在最近个人的使用中也比较常见,比如格式化输出,就测试了一下,说点粗陋的发现。

测试环境

测试基于window10环境,使用的编译器是VS2019,在32和64位的编译配置下都测试过。

省略符形参的实现

个人使用到的几个用到省略符形参的方法都有一套类似的结构,
在这里插入图片描述
函数的所有参数都是原变量被拷贝后赋值到新的内存中的,因而所有参数在内存中是连续的,这是可以使用省略符形参的前提。
参数a是形参列表中最后一个明确的参数,va_list是char*类型。

va_start

va_start的作用是获取参数a后面第一个未知参数的地址,并赋值给args参数。

#define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )

在32位的编译环境中,va_start的底层实现如上。

_ADDRESSOF(v)

这个方法获取明确变量的地址并强制转换为char*类型处理,因为char类型的大小为1字节,等于字节大小单元,更方便寻址处理。

_INTSIZEOF(v)

这个宏定义用于获取不小于变量v(在实际使用中是参数a,亦即形参列表中最后一个明确类型的参数)类型大小的 4的 整数倍的字节长度,结果是4的整数倍。具体为什么这样设计我还在查询,但可能和指针以及内存地址有关,在32位的系统中,内存地址长度以及指针变量长度为4字节(int);而在64位系统中,这一长度也随着内存地址大小扩展变成了8字节(__int64),并且,当实际数据大于指针大小时,转换为指针处理。

va_arg

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

va_arg宏定义用于获取未知参数存储内存中的第一个参数数据,提取时类型为t,同时,也让args指向这个参数后的位置,用于获取(可能存在的)下一个未知参数,可以通过可控的遍历安全获取实际参数列表。

如 假设省略符形参的实参为

int a, float b, TypeNameA* c

则,可以通过依次顺序调用以下语句获取实参数据:

va_arg(args,int);//返回结果为 a
va_arg(args,float);//返回结果为 b
va_arg(args,TypeNameA*);//返回结果为 c

但一旦使用了错误的类型或者顺序提取参数,可能会造成未知的错误。

va_end

va_end将参数列表清零

#define _crt_va_end(ap)      ( ap = (va_list)0 )

省略符参数的使用

首先做个小实验,在函数的实现中不通过实参而获取它对应的值。

void show(int a, char b);
int main()
{
	int a = 10;
	char b = 'v';
	show(a, b);
	system("pause");
	return 0;
}
void show(int a, char b)
{
	int* aptr = &a;
	char b_ = 'e';
#ifdef _M_X64
	b_ = *(char*)((char*)aptr+sizeof(__int64));
#elif defined(_M_IX86)
	b_ = *(char*)((char*)(aptr) + sizeof(int));
#endif
	cout << b_ << endl;
}

在show方法中,我并没有显示的调用形参b,最终却依旧会输出显示b的值。

说回正题:
使用省略符形参的关键在于确定省略形参的数量和类型,因此你需要通过一定的标志去确定这两项数据。
通常使用的方法有,
1、在明确的参数中添加数量参数,让函数循环有限次数,省略参数的类型一般有默认要求
2、最后一个省略符参数显式标记为null或其他标志性数据,确保能在参数范围内停止。
3、格式化输出时,一般会明确类型为char*或者string的format参数,在format字符串中会有特殊字符标记嵌入位置、嵌入数量和嵌入类型。标准库中也有自带的vsnprintf函数,可用于代替处理va_arg的过程。

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值