【问题标题】:Why signed char can hold bigger values than 127?为什么有符号字符可以保存比 127 更大的值?
【发布时间】:2020-02-12 12:59:49
【问题描述】:
int main()
{
    char MCU = 0b00000000;
    char al_av = 0b10100000;

    // Before bit operation
    cout << "MCU = " << int(MCU) << endl;

    MCU = MCU | al_av;

    // After the bit operation
    cout << "MCU = " << int(MCU) << endl;   // Expected 160, got -96

    char temp = 160;

    cout << temp;   // got the a with apostrophe

    return 0;
}

我预计char temp 的输出是负数(或警告/错误),因为 160 超出了 [-127,127] 区间,但结果却是 ASCII 表中的那个(a带撇号)

cpp reference:

char - 可以在目标系统上最有效地处理的字符表示类型(与有符号字符或无符号字符具有相同的表示和对齐方式,但始终是不同的类型

我不明白用斜体写的内容(我也不确定它对这个问题有多大帮助)。有没有隐式转换?

【问题讨论】:

  • 请注意,char 不能保证被签名。从您发布的链接中:“char 的签名取决于编译器和目标平台:ARM 和 PowerPC 的默认值通常是无符号的,x86 和 x64 的默认值通常是有符号的。”跨度>
  • 斜体部分表示char 有符号或无符号,但无论如何charunsigend charsigned char 是3(!)不同的类型。我认为与您的问题无关
  • 请提供使用的编译器、您指定的任何编译器选项以及您运行它的平台。这些与正在发生的事情有关。
  • 231 在您的代码中是integer literal (en.cppreference.com/w/cpp/language/integer_literal),它隐式转换 (en.cppreference.com/w/cpp/language/implicit_conversion) 到您的“char”类型
  • @mvidelgauz 答案应该转到答案

标签: c++ char


【解决方案1】:

为什么 signed char 可以保存比 127 更大的值?

不能。

char x = 231;

这里有一个(隐式)整数转换:231 是一个int 类型的prvalue,在转换为char 之前取值-25(在您的系统)。您可以使用-Wconstant-conversion 要求您的编译器警告您。


char - 可以在目标系统上最有效地处理的字符表示类型(与有符号字符或无符号字符具有相同的表示和对齐方式,但始终是不同的类型

我看不懂斜体写的是什么

这与类型可以容纳什么无关,它只确保charsigned charunsigned char这三种类型具有共同的属性。

【讨论】:

  • 好的,这是否意味着从这个 asciitable.com ,在 signed char 变量上,负值是这样的:-1 = 128, -2 = 129, etc.?
  • @CătălinaSîrbu 否。这取决于匹配整数值和字符的任何 lib 如何解释您的值。例如,如果您使用std::cout 进行打印,这将取决于您的运行时在您的环境中的具体实现。
  • @CătălinaSîrbu • 在我的系统上,-1 = 255、-2 = 254 等
【解决方案2】:

从 C++14 char,如果 signed,必须是 2 的补码类型。这意味着它的范围是至少 -128 到+127。重要的是要知道范围可能大于此,因此假设大于 127 的数字不能存储在char 中是不正确的,如果signed。使用

std::numeric_limits<char>::max()

在您的平台上获得真正的上限。

如果您为char 分配了一个大于此值的值,而charsigned,那么您的代码行为是由实现定义的。通常,这意味着 环绕 为负数,这实际上是签名 char 类型的普遍行为。

还要注意 ASCII 是 7 位编码,所以说 0 - 127 范围之外的任何字符都是 ASCII 是错误的。另请注意,ASCII 不是 C++ 支持的唯一编码。还有其他的。

最后,不同的类型:即使charsigned,它也是与signed char 不同的类型。这意味着代码

int main() {
    char c;
    signed char d;
    std::swap(c, d);
}

总是会导致编译错误。

【讨论】:

  • C++14 标准的哪些变化要求签名的char 是 2 的补码?
  • 我认为 C++20 需要 2s 补码。在此之前,它可能是 1s 或 2s。 (其他编码不适用。)
  • @Eljay:C++20 对所有有符号整数类型都需要 2 的补码。 C++14 需要它来处理有符号字符和有符号字符。
  • CppReference 说在 C++20 之前,有符号字符类型可能是 1,并且还指出所有已知的 C++ 编译器都使用 2s 补码(在 C++20 之前,但是(可能) C++11 及之后)。并不是说 CppReference 是权威的;他们有时会犯错误(尽管很少)。
【解决方案3】:
char temp = 160;

实际上是负数。关键是 cout 支持非 ASCII 字符,因此它将其解释为非负数。 cout 可能在使用它之前将其强制转换为无符号字符(或任何无符号整数类型)。

如果你使用 printf 并告诉它把它解释为一个整数,你会看到它是一个负值。

printf("%d\n", temp); // prints -96

【讨论】:

  • 它之所以有效,只是因为printf 是一个可变参数函数,它的参数转换为int。见en.cppreference.com/w/cpp/language/…
  • @YSC 它可以工作,因为 char 是有符号类型并且它具有负值。这就是我想要展示的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-12
  • 2023-03-31
  • 2010-09-17
  • 1970-01-01
相关资源
最近更新 更多