va_arg中的char类型

Ama*_*man 5 c arguments

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

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);
}
Run Code Online (Sandbox Code Playgroud)

编译后,我从编译器收到以下警告

 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]
Run Code Online (Sandbox Code Playgroud)

现在据我了解,C想要将char类型提升为int.为什么C想要这样做?第二,是将int转换回char的最佳解决方案吗?

The*_*ant 7

现在据我了解,C想要将char类型提升为int.为什么C想要这样做?

因为这就是标准所说的.如果将转换等级小于int(例如char,boolshort)的积分值传递给采用可变数量参数的函数,则将其转换为int.据推测,其原因在于其性能,其中(实际上,现在通常仍然如此)更好地传递与机器字边界对齐的值.

第二,是将int转换回char的最佳解决方案吗?

是的,但你甚至不需要强制转换,隐式转换会:

char ch = va_arg(ap, int);
Run Code Online (Sandbox Code Playgroud)


Kei*_*son 6

变量函数经过特殊处理.

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

对于可变函数,编译器不知道对应的参数类型, ....由于历史原因,并且为了使编译器的工作更容易,类型的任何相应参数int都被提升为int或者被提升为unsigned int,并且任何类型的参数float都被提升为double.(这就是为什么printf对它们floatdouble参数使用相同的格式说明符的原因.)

因此,可变参数函数不能接收类型的参数char.您可以使用char参数调用此类函数,但它将被提升为int.

(在C的早期版本中,引入了原型之前,所有的功能表现这种方式,即使C11允许非原型声明,在狭窄的参数被晋升为int,unsigned intdouble.但考虑到原型的存在,实在没有理由去写取决于此类促销的代码 - 除了可变函数的特殊情况.)

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

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

如果表示被调用函数的表达式具有包含原型的类型,则将参数隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型作为其声明的非限定版本类型.函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数提升是在尾随参数上执行的.

"默认参数提升"转换窄论点va_arg(),intunsigned int.(最大值超出的无符号整数类型的参数double将被提升为INT_MAX.理论上可以采用unsigned int这种方式运行,但仅限于非常不寻常的实现.)

第二,是将int转换回char的最佳解决方案吗?

不,不是在这种情况下.铸造很少是必要的; 在大多数情况下,隐式转换可以完成相同的工作.在这种特殊情况下:

const char c = va_arg(ap, char);
putc(c, fp);
Run Code Online (Sandbox Code Playgroud)

第一个参数char已经是类型putc,所以这更好地写为:

const int c = va_arg(ap, int);
putc(c, fp);
Run Code Online (Sandbox Code Playgroud)

int值由intto 转换putc并写入unsigned char.