字符类型 va_arg

char type in va_arg

我有以下函数将传递的参数写入二进制文件。

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 的最佳解决方案是什么?

Now as I understand it, C wants to promote char type to int. Why does C want to do this?

因为标准就是这么说的。如果将转换等级小于 int(例如 charboolshort)的整数值传递给采用可变数量参数的函数,它将转换为 int。据推测,其原因在于性能,过去(事实上,现在通常仍然如此)更好地传递与机器字边界对齐的值。

Second, is the best solution to cast the int back to a char?

是的,但您甚至不需要强制转换,隐式转换即可:

char ch = va_arg(ap, int);

可变参数函数被特殊对待。

对于非可变函数,原型(声明)指定所有参数的类型。参数可以是任何(非数组、非函数)类型——包括窄于 int.

的类型

对于可变参数函数,编译器不知道, ...对应的参数类型。由于历史原因,并且为了使编译器的工作更轻松,任何类型小于 int 的相应参数都被提升为 intunsigned int,任何类型为 float 的参数晋升为 double。 (这就是 printffloatdouble 参数使用相同格式说明符的原因。)

因此可变函数 无法接收 类型 char 的参数。您可以使用 char 参数调用这样的函数,但它会被提升为 int.

(在 C 的早期版本中,在引入原型之前,所有 函数都是这样的。甚至 C11 也允许非原型声明,其中窄参数被提升为 intunsigned intdouble。但鉴于原型的存在,实际上没有理由编写依赖于此类提升的代码——可变参数函数的特殊情况除外。)

因此,让 va_arg() 接受 char 作为类型参数是没有意义的。

但是语言并不禁止这样调用va_arg();事实上,描述 <stdarg.h> 的标准部分没有提到参数提升 。该规则在函数调用部分中说明,N1570 6.5.2.2 第 7 段:

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

va_arg() 宏的描述 7.16.1.1 说(强调):

If there is no actual next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined, except for the following cases:
[SNIP]

"default argument promotions" 将窄参数转换为 intunsigned intdouble。 (最大值超过 INT_MAX 的无符号整数类型的参数将被提升为 unsigned int。理论上 char 可能以这种方式运行,但仅在非常不寻常的实现中。)

Second, is the best solution to cast the int back to a 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转换为unsigned char并写入fp