【问题标题】:cout of negative value of signed char in C++C++ 中带符号字符的负值计数
【发布时间】:2018-06-24 20:26:38
【问题描述】:

C++ 如何处理有符号字符的负值计数?行为是否在 C++11 标准中定义?我正在使用 MinGW C++ 11 编译器。它看起来通过添加 256 将有符号值转换为无符号类型,然后打印扩展的 ASCII 字符。

signed char a=-35;
std::cout<<a;

【问题讨论】:

标签: c++ c++11 gcc


【解决方案1】:

根据this,选择如下重载:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
                                        signed char ch );

由于signed char 不是chara 首先使用widen 转换为char

char_type widen( char c ) const;

所以你的代码相当于:

std::cout << std::cout.widen(c);
// or:
std::cout << std::use_facet< std::ctype<char> >(getloc()).widen(c)

如您所见,widen 采用char,因此您将在实际“扩大”之前从signed char 转换为char

即使您从char 扩大到char,行为也是由实现定义的——标准对此不做任何保证。

【讨论】:

  • 此编译器中的 char 似乎已签名,因为 cout ::is_signed;返回我 1. 那么它如何打印扩展的 ASCII 字符(128 到 255)?对于扩展的 ASCII 字符,应该转换为无符号值吧?
  • @Rajesh 您假设字符集是 ASCII + 扩展 ASCII,但标准对此没有任何假设。在我的电脑上,我只收到了一个?。终端上显示的内容取决于您的终端编码。程序可能会写-35,而你的系统会将其解释为221
  • 在理解语句“程序可能写-35而您的系统将其解释为221”时有些困难。对不起,我有一些问题,希望你能详细说明。我在下面运行代码并且都产生相同的输出。所以 -35 根据我的编译器变成 221。如果它变成有符号字符,一个值永远不会超过 127,因此扩展字符永远不会打印对吗?签名字符 a; for(int cnt=-1;cnt>=-128;cnt--) { a=cnt; std::cout=128;cnt--) { printf("%c\t",cnt); }
  • @Rajesh 抱歉,这写得不好。您的代码可能会将-35 的二进制表示写入与std::cout 关联的流。现在,如何在另一端解释这个二进制值取决于您的系统。您的系统可能会读取二进制表示,将其解释为 221,并在终端中定义的字符集中显示相应的字符。
【解决方案2】:

使用类型转换为int...

std::cout << (int)a;

...或者,遵循更好的 C++ 编程风格(正如 Christian Hackl 建议的那样):

std::cout << static_cast<int>(a);

这实际上并没有回答你的问题(霍尔特已经回答了),但显示了问题的解决方案。

【讨论】:

  • 为什么不static_cast
  • @ChristianHackl 因为它的字符更多,我看不出这种情况下的功能有什么不同。这只是风格和个人喜好的问题。
  • 这些正是您应该在此处使用 C++ 样式转换的原因。演员表应该难以打字且易于阅读。
  • 更新答案。
  • 如果你真的想保存一些字符,最短的版本是std::cout &lt;&lt; +a,它还有一个额外的好处,就是为任何整数或浮点类型做“正确的事情”。
【解决方案3】:

其中大部分是必需的行为(而且大部分还不是必需的)。

具体来说,C++ 标准说 iostream 与 C 风格的输入和输出流相关联,因此 coutstdout 相关联(§[narrow.stream.objects]/3):

对象cout 控制输出到与stdout 中声明的对象stdout 关联的流缓冲区。

反过来,C 标准将窄字符输出定义为好像是通过 fputc 编写的(第 7.19.3/12 节):

字节输出函数将字符写入流,就像通过连续调用 fputc 函数一样。

fputc 需要 (§7.19.7.3/2):

fputc 函数将c 指定的字符(转换为unsigned char)写入stream 指向的输出流,[...]

所以,是的,转换为unsigned char 正是标准所要求的。 C 标准要求从有符号到无符号(任何整数类型,包括char)的转换以下列方式进行(第 6.3.1.3/2 节):

否则,如果新类型是无符号的,则在新类型可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。

所以是的,它通过添加 256 转换为无符号(假设 unsigned char 可以表示从 0 到 255 的值,这是典型的)。

所以这给我们留下了标准类型尝试需要的一部分,而不是一路走来——widen 必须做的转换(§[locale.ctype.virtuals]/10):

应用从一个 char 值或 char 值序列到相应的一个或多个 charT 值的最简单合理的转换。

由于要确切地确定什么是“合理的”有点困难,所以这可以对你的角色进行或多或少的任意映射。事实上,它显然是在不加修改的情况下将输入映射到输出(至少对于您正在编写的特定角色而言),但确实其他转换可能属于“合理”范围,并且最终很难划定一条强硬线说任何特定的转变都不“合理”。

C++(或任何其他)标准并不真正要求的另一部分是其他东西将如何解释该输出。所有语言标准都可以强制要求写入流的内容。其他东西打开该流并将其内容解释为“扩展 ASCII”(可能是 ISO 8859 变体之一)的部分显然超出了语言的控制范围(当然,或者您程序中的大部分其他内容)。

【讨论】:

  • 您引用了 C 标准,但是 C++ 标准在哪里说 std::cout 的输出是通过调用 fputc 来管理的?因为std::coutstdout 相关联并不意味着std::cout 的所有输出都由fputc 管理,就像C 中的stdout 一样。我在C++ 标准中唯一能找到的东西与@ 有关987654321@ 接受 char 而不是 unsigned char
  • @Holt:C++ 标准只是说数据被写入标准输出。 C 标准说所有字节输出(对应于 C++ 标准所称的窄字符输出)都是 fputc 的。我已经编辑添加了 C 标准中针对该特定点的引用。由于 C++ 流写入(好像)到stdout 并且所有到stdout 的输出都是(好像)通过fputc 写入的,所有来自 C++ 的输出必须(好像)通过fputc
  • 引用以“字节输出函数”开头,所以据我了解,这意味着所有C函数都像fputc一样写入流,但这并不意味着 C++ 函数可以。当我阅读您的答案时(也许我遗漏了一些东西),我了解 "std::coutstdout 使用相同的输出缓冲区,stdout 上的 C 操作就像通过 fputc 一样,所以在std::cout 也必须是。”,但这似乎不对。
  • @Holt:如果它的意思是:“cout 和 stdout 使用相同的输出缓冲区”,那就是它的意思。这不是它所说或要求的。它所说和要求的是它与stdout 相关联,而不仅仅是stdout 控制的缓冲区。因为coutchar 的实例化,所以coutcharTchar。因此,cout 将字节写入stdout,这(在此重复)必须像fputc 那样完成。我知道你不想承认你的答案大部分是错误的,但你开始扭曲语言以保护你的错误。
  • 好吧,我误解了这部分,但仍然“字节输出函数”是CI/O库函数(fprintf、fputc、fputs、fwrite、我的版本中的 printf、putc、putchar、puts、vfprintf、vprintf)和我 C++ 标准不要求(除非我错过它)与stdout 关联并由std::cout 使用的实现定义的streambuf 具有要使用这些函数写入stdout,它可能会使用低级例程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-06
  • 1970-01-01
  • 1970-01-01
  • 2019-03-24
  • 2015-10-31
相关资源
最近更新 更多