有没有一种正统的方法可以避免编译器警告C4309 - 使用二进制文件输出“截断常量值”?

时间:2013-03-18 00:02:28

标签: c++ file-io compiler-warnings binary-data truncation

我的程序执行将二进制数据写入文件的常见任务,符合某种非文本文件格式。由于我正在编写的数据不是已经存在于现有块中,而是在运行时逐字节放在一起,因此我使用std::ostream::put()而不是write()。我认为这是正常的程序。

程序运行正常。它使用std::stringstream::put()std::ofstream::put()两个十六进制整数作为参数。但是每当put()的参数大于0x7f时,我都会收到编译器警告C4309:“截断常量值”(在VC ++ 2010中)。显然编译器期望signed char,并且常量超出范围。但我认为任何截断都不会发生;字节的写入就像它应该的那样。

编译器警告让我觉得我没有以正常,可接受的方式做事。我描述的情况必须是一个普遍的情况。 是否有通用的方法来避免这种编译器警告?或者这是一个无意义的编译器警告的例子,应该被忽略?

我想到了避免它的两种不太优雅的方法。我可以在每次调用时使用mystream.put( char(0xa4) )之类的语法。或者不是使用std::stringstream我可以使用std::basic_stringstream< unsigned char >,但我不认为这个技巧适用于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

2 个答案:

答案 0 :(得分:18)

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

ss.put( 0xa4 ); // C4309

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

ss.put( 0xa4u );

答案 1 :(得分:7)

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

正如您所猜测的那样,问题是ostream.put()需要char,但0x7Fchar的最大值,而更高的内容会被提升为int {1}}。您应该转换为unsigned char,这与char一样宽,因此它会安全地存储char所做的任何事情,但也会使截断警告合法化:

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