为什么我可以将一个大于 127 的 int 传递给一个 char 数组,而不是直接传递?

Why can I pass an int with a value higher than 127 into a char array, but not directly?

我知道 char 值不能表示为 176,但一些字节系统是无符号的 (0-255),而其他字节系统是有符号的(-128 到 127)。在这种情况下,我使用的是无符号的,所以我只想创建一个简单的字节消息数组,但是当我尝试放置一个高于 127 的值时出现此错误,但是如果我先将其声明为 int,它会避免该错误.有人可以详细解释为什么这有效吗?

方法一:不行。我收到此错误:缩小从“int”到“char”的“176”转换

char m1[3]{ 176, 118, 1 };

方法 2:这个有效

int b1 = 176;
char m1[3]{ b1, 118, 1 };

当使用花括号进行初始化(又名 "uniform initialization")时,不允许缩小转换。否则它们是并且值只是被静默截断。

大多数编译器都有警告选项,您可以启用这些选项,以捕获许多(但不是全部)发生截断的实例。他们通常还有可以将此类警告变成错误的选项。 您应该使用这些选项。

如果你想使用字节,那么 std::byte is arguably the correct type to use. Or (if you cannot use that) std::uint8_t

大多数系统上的典型字符范围:

  • 类型:char,范围:-127 到 1270 到 255
  • 类型:unsigned char,范围:0 到 255
  • 类型:signed char,范围:-127 到 127

charsigned 还是 unsigned 是实现定义的。您可以使用 std::is_signed<char>() 检查。在您的情况下,它是隐式的 signed,并且编译器检测到您正在从正整数 (10110000 = 176) 隐式转换为带符号的字符值(其中 10110000 为 -90)。如果值小于或等于 127(试试吧!),则不会生成警告。 如果你想避免隐式转换(缩小转换),你可以明确指定它是 unsigned:

unsigned char m1[3]{ 176, 118, 1 };

如果你想使用符号字符,你最好使用signed修饰符,而不是依赖实现:

signed char m1[3]{ b, 118, 1 }; // where b is less than or equal to 127

对于 {} 初始化,编译器应该 - 至少 - 将其诊断为警告,这会使程序格式错误。但同样,这取决于所使用的编译器和选项。如果您转到 https://gcc.godbolt.org/ 并使用不同的 compilers/options 编译您的代码,您会看到差异。

一些例子:

对于以下代码:

char m1[3] = {176, 118, 1 };
  • 使用 x86-64 gcc 6.1 你会得到一个错误:error: narrowing conversion of '176' from 'int' to 'char' inside { } [-Wnarrowing]。然而,当你使用标志 -Wno-narrowing 时你并没有得到它,但是你的程序仍然是错误的,你不希望这样。
  • 使用 x86-64 gcc 4.8.1 你不会收到错误,也不会收到警告。但是,例如,当使用选项 -Wno-narrowing-Werror=narrowing 时,您会看到它被拒绝并分别显示警告或错误。

使用代码:

int b1 = 176;
char m1[3] = {b1, 118, 1 };

不同的编译器版本会有不同的行为,但程​​序仍然是错误的。

一般来说,使用 -Wnarrowing-Wall-Werror=narrowing 之类的选项以及任何版本的编译器(我想,我没有检查所有这些选项),应该指出收缩转换,这意味着您的程序格式错误,您不希望这样。

希望对您有所帮助!

这两种情况都是错误格式的,但正如我在 my answer here 中解释的那样,格式错误只需要进行诊断。该诊断是警告还是错误取决于实现。所以这是一个非常符合要求的结果。

例如,对于这种情况,gcc 对第一个产生错误,但对第二个仅产生警告 (see it live on godbolt):

error: narrowing conversion of '176' from 'int' to 'char'  [-Wnarrowing]
    2 |     char m1[3]{ 176, 118, 1 };
      |                             ^
warning: narrowing conversion of 'b1' from 'int' to 'char' [-Wnarrowing]

    5 |    char m2[3]{ b1, 118, 1 };
      |                           ^ 

这是标准允许的,我将引用this gcc bug report的相关部分:

The standard only requires that "a conforming implementation shall issue at least one diagnostic message" so compiling the program with a warning is allowed. As Andrew said, -Werror=narrowing allows you to make it an error if you want.

G++ 4.6 gave an error but it was changed to a warning intentionally for 4.7 because many people (myself included) found that narrowing conversions where one of the most commonly encountered problems when trying to compile large C++03 codebases as C++11. Previously well-formed code such as char c[] = { i, 0 }; (where i will only ever be within the range of char) caused errors and had to be changed to char c[] = { (char)i, 0 }

对于 gcc 和 clang,您可以使用 -Werror.

将所有警告转换为错误