有没有一种正统的方法可以避免编译器警告 C4309 - 带有二进制文件输出的"truncation of constant value"?

Is there an orthodox way to avoid compiler warning C4309 - "truncation of constant value" with binary file output?

本文关键字:二进制文件 输出 constant of truncation C4309 value 警告 一种 编译器 可以避免      更新时间:2023-10-16

我的程序完成了将二进制数据写入文件的常见任务,该文件符合特定的非文本文件格式。由于我正在编写的数据不在现有的块中,而是在运行时逐字节地放在一起,所以我使用std::ostream::put()而不是write()。我想这是正常的程序。

这个程序运行得很好。它使用带有两位十六进制整数的std::stringstream::put()std::ofstream::put()作为自变量。但每当put()的参数大于0x7f时,我就会收到编译器警告C4309:"截断常数值"(在VC++2010中(。显然,编译器期望signed char,而常量超出了范围。但我认为实际上并没有发生任何截断;字节按照预期写入。

编译器的警告让我觉得我没有以正常的、可接受的方式做事。我所描述的情况一定很常见有没有常见的方法可以避免这样的编译器警告?或者这是一个应该被忽略的毫无意义的编译器警告的例子?

我想了两种不雅的方法来避免它。我可以在每次调用中使用类似mystream.put( char(0xa4) )的语法。或者,我可以使用std::basic_stringstream< unsigned char >来代替std::stringstream,但我认为这个技巧不适用于std::ofstream,它不是模板类型。我觉得这里应该有一个更好的解决方案,特别是因为ofstream是用于编写二进制文件的。

你的想法?

--编辑--

啊,我弄错了std::ofstream不是模板类型。它实际上是std::basic_ofstream<char>,但我尝试了那个方法,并意识到它无论如何都不起作用,因为缺乏定义的方法以及与std::ostream的多态性不兼容。

这里有一个代码示例:

stringstream ss;
int a, b;
/* Do stuff */
ss.put( 0 );
ss.put( 0x90 | a ); // oddly, no warning here...
ss.put( b );        // ...or here
ss.put( 0xa4 );     // C4309

我找到了我满意的解决方案。它比显式地将每个常量强制转换为unsigned char更优雅。这就是我所拥有的:

ss.put( 0xa4 ); // C4309

我认为unsigned char隐式转换为char时会发生"截断",但丛旭指出,整数常数被假定为有符号的,任何大于0x7f的常数都会从char提升为int。然后,如果传递给put(),它实际上必须被截断(减少到一个字节(。通过使用后缀"u",我可以指定一个无符号整数常量,如果它不大于0xff,它将是一个unsigned char。这就是我现在所拥有的,没有编译器警告:

ss.put( 0xa4u );
std::stringstream ss;
ss.put(0x7f);
ss.put(0x80); //C4309

正如您所猜测的,问题是ostream.put()期望char,但0x7Fchar的最大值,任何更大的值都会升级为int。您应该强制转换为与char一样宽的unsigned char,这样它可以安全地存储char所做的任何事情,但也可以使截断警告合法:

ss.put(static_cast<unsigned char>(0x80)); // OK
ss.put(static_cast<unsigned char>(0xFFFF)); //C4309