c – 为什么printf需要强制参数?
C语言中printf函数的定义是:
int printf(const char * _Format,...); 对于scanf和许多类似的函数也是如此,其中管理了可变数量的参数. 为什么有_Format必需参数? 解决方法
格式字符串是必需的,因为C的可变参数宏的工作方式取决于至少存在一个参数,并使用它来查找其他参数.
具体来说,要读取其他变量参数,可以使用va_start(然后重复使用va_arg,对于要读取的每个变量参数,使用一次).当你调用va_start时,你需要传递格式字符串(或者更常见的是,函数的最后一个非变化参数). 例如,这就像printf,但是打印到stdout和你选择的另一个文件: void tee(FILE *f,char const *fmt,...) { va_list ap; va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); } 这使用了vprintf和vfprintf,所以它没有(直接)使用va_arg本身,只使用va_start和va_end,但这足以说明fmt如何使用va_start. 有一段时间,这实际上并不需要.当C闪亮而且新的时候,你可以拥有一个等同于:int f(…);的函数. 然而,在第一次C标准化工作期间,这被取消,有利于上面提到的需要至少一个命名参数的宏(va_start,va_arg,va_end).较旧的宏对调用约定提出了许多要求: >无论类型或编号如何,参数始终以相同的方式传递. 使用传统的C调用约定(所有参数都在栈上传递,参数从右向左推送)这是真的.你基本上只是查看堆栈的顶部,向后移动到返回地址,并且有第一个参数. 对于其他调用约定,事情并非如此简单.例如,只需从左向右推送参数意味着第一个参数(格式字符串,在printf的情况下)被埋在堆栈中的任意距离,其后有任意数量的其他参数. 他们提出处理这个问题的方法是将前一个(命名)参数传递给va_start(而va_start是一个通常使用该参数地址的宏).如果你从右向左推,那将给你一个地址,无论距离堆栈需要什么距离,然后va_arg可以向上走回堆栈以检索其他变量参数. 这显然被视为可接受的折衷方案,特别是因为采用可变参数的函数几乎总是至少采用一个命名参数. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |