我的程序完成了将二进制数据写入文件的常见任务,该文件符合特定的非文本文件格式。由于我正在编写的数据不在现有的块中,而是在运行时逐字节地放在一起,所以我使用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
,但0x7F
是char
的最大值,任何更大的值都会升级为int
。您应该强制转换为与char
一样宽的unsigned char
,这样它可以安全地存储char
所做的任何事情,但也可以使截断警告合法:
ss.put(static_cast<unsigned char>(0x80)); // OK
ss.put(static_cast<unsigned char>(0xFFFF)); //C4309