我有以下函数将传递的参数写入二进制文件。
void writeFile(FILE *fp, const int numOfChars, ...)
{
va_list ap;
va_start(ap, numOfChars);
for(int i = 0; i < numOfChars; i++)
{
const char c = va_arg(ap, char);
putc(c, fp);
}
va_end(ap);
}
编译后,我从编译器收到以下警告
warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg
has undefined behavior because arguments will be promoted to 'int' [- Wvarargs]
据我了解,C想要将char类型提升为int。为什么C想要这样做?第二,是将int转换回char的最佳解决方案吗?
答案 0 :(得分:7)
据我了解,C想要将char类型提升为int。为什么C想要这样做?
因为这就是标准所说的。如果您将转化等级小于int
(例如char
,bool
或short
)的积分值传递给采用可变数量参数的函数,它将会转换为int
。据推测,其原因在于其性能,其中(实际上,现在通常仍然是如此)更好地传递与机器字边界对齐的值。
其次,是将int转换回char的最佳解决方案吗?
是的,但你甚至不需要强制转换,隐式转换会:
char ch = va_arg(ap, int);
答案 1 :(得分:5)
特殊处理变体函数。
对于非可变函数,prototype(声明)指定所有参数的类型。参数可以是任何(非数组,非函数)类型 - 包括比int
窄的类型。
对于可变函数,编译器不知道与, ...
对应的参数类型。由于历史原因,为了使编译器的工作更容易,任何比int
更窄的类型的相应参数都被提升为int
或unsigned int
,以及任何类型为{的参数{1}}被提升为float
。 (这就是double
对printf
或float
参数使用相同格式说明符的原因。)
因此,可变参数函数无法接收类型为double
的参数。您可以使用char
参数调用此类函数,但会将其提升为char
。
(在C的早期版本中,在引入原型之前,所有函数都以这种方式运行。甚至C11也允许非原型声明,其中窄参数被提升为int
,{ {1}},或int
。但鉴于原型的存在,我们没有理由编写依赖于此类促销的代码 - 除了可变函数的特殊情况。)
正因为如此,unsigned int
接受double
作为类型参数毫无意义。
但该语言并未禁止 va_arg()
的这种调用;实际上,描述char
的标准部分并未提及论证推广。该规则在函数调用部分N1570 6.5.2.2第7段:
如果表示被调用函数的表达式具有类型 确实包含一个原型,参数被隐式转换为 如果通过赋值,对相应参数的类型,采取 每个参数的类型是其不合格的版本 声明的类型。函数原型中的省略号表示法 声明器导致参数类型转换在最后一个之后停止 声明参数。执行默认参数促销 尾随论据。
&#34;默认参数促销&#34;将较窄的参数转换为va_arg()
,<stdarg.h>
或int
。 (最大值超过unsigned int
的无符号整数类型的参数将被提升为double
。从理论上讲,INT_MAX
可能会以这种方式运行,但仅限于不寻常的实施。)
其次,是将int转换回char的最佳解决方案吗?
不,不是在这种情况下。铸造很少是必要的;在大多数情况下,隐式转换可以完成相同的工作。在这种特殊情况下:
unsigned int
char
的第一个参数已经是const char c = va_arg(ap, char);
putc(c, fp);
类型,因此最好写成:
putc
int
值由const int c = va_arg(ap, int);
putc(c, fp);
转换为int
并写入putc
。