ChatGPT解决这个技术问题 Extra ChatGPT

什么是无符号字符?

在 C/C++ 中,unsigned char 用于什么?它与普通的 char 有何不同?


m
mediocrevegetable1

在 C++ 中,存在三种不同的字符类型:

字符

签名字符

无符号的字符

如果您对 text 使用字符类型,请使用不合格的 char

它是字符文字的类型,例如“a”或“0”(仅在 C++ 中,在 C 中它们的类型是 int)

它是构成诸如“abcde”之类的 C 字符串的类型

它也可以作为一个数字值,但未指定该值是被视为有符号还是无符号。当心通过不等式进行字符比较 - 尽管如果您将自己限制在 ASCII (0-127) 范围内,那么您几乎是安全的。

如果您将字符类型用作数字,请使用:

带符号的字符,它至少为您提供 -127 到 127 的范围。 (-128 到 127 很常见)

unsigned char,它至少为您提供 0 到 255 的范围。

“至少”,因为 C++ 标准只给出了每种数字类型需要覆盖的最小值范围。 sizeof (char) 必须为 1(即一个字节),但理论上一个字节可以是例如 32 位。 sizeof 仍会将其大小报告为 1 - 这意味着您可以拥有 sizeof (char) == sizeof (long) == 1


需要明确的是,你可以有 32 位字符和 32 位整数,并且有 sizeof(int) != sizeof(char)?我知道标准说 sizeof(char) == 1,但是相对 sizeof(int) 是基于实际大小差异还是范围差异?
+1。但是 C++ 中有四种不同的字符类型,wchar_t 就是其中之一。
从 c++11 开始,您有 6 种不同的类型:char、signed char、unsigned char、wchar_t、char16_t、char32_t。
@unheilig 在 sizeof 之后放置一个空格很常见,因为它不是函数而是运算符。在获取变量的大小时,省略括号是一种更好的风格。 sizeof *psizeof (int)。如果它适用于类型或变量,这将很快清楚。同样,在 return 后面加上括号也是多余的。这不是一个函数。
在这种情况下,“字节”指的是最小的可寻址内存单元。 C 和 C++ 标准要求一个字节至少为 8 位,但它们没有指定最大值。在当今几乎所有通用计算机(包括任何与最新版本的 posix 兼容的计算机)上,一个字节正好是 8 位,但专门的 DSP 平台和复古系统可能有更大的字节。
S
Stefan van den Akker

这取决于实现,因为 C 标准没有定义 char 的符号性。根据平台,char 可能是 signedunsigned,因此如果您的实现依赖于它,您需要明确要求 signed charunsigned char。如果您打算表示字符串中的字符,只需使用 char,因为这将匹配您的平台在字符串中放置的内容。

signed charunsigned char 之间的区别正如您所料。在大多数平台上,signed char 将是一个 8 位二进制补码,范围从 -128127,而 unsigned char 将是一个 8 位无符号整数(0255)。请注意,该标准不要求 char 类型具有 8 位,仅要求 sizeof(char) 返回 1。您可以使用 limits.h 中的 CHAR_BIT 获取 char 中的位数。但是,今天很少有平台会出现 8 之外的内容。

这个问题有一个很好的总结here

正如其他人在我发布此内容后所提到的那样,如果您真的想表示小整数,最好使用 int8_tuint8_t


带符号的字符只有 -127 到 127 的最小范围,而不是 -128 到 127
@12431234123412341234123:技术上是正确的,因为 C 标准将 -127 到 127 定义为最小范围。不过,我挑战你找到一个不使用二进制补码算法的平台。在几乎每一个现代平台上,签名字符的实际范围都是 -128 到 127。
标准要求 CHAR_BIT 至少为 8 位。
Y
Yun

因为我觉得真的很需要,所以我只想说一下C和C++的一些规则(在这方面是一样的)。首先,如果有任何 unsigned char 对象,unsigned char所有位 都参与确定值。其次,unsigned char 明确声明为无符号。

现在,我与某人讨论了将 int 类型的值 -1 转换为 unsigned char 时会发生什么。他拒绝将结果 unsigned char 的所有位都设置为 1 的想法,因为他担心符号表示。但他不必如此。转换会立即遵循此规则:

如果新类型是无符号的,则在新类型可以表示的最大值的基础上反复加减一,直到该值在新类型的范围内。 (C99 草案中的 6.3.1.3p2)

这是一个数学描述。 C++ 用模演算来描述它,这产生了相同的规则。无论如何,保证的是整数 -1 中的所有位在转换之前都是 1。那么,我们有什么可以声称得到的 unsigned char 的所有 CHAR_BIT 位都变为 1?

所有位都参与确定其值 - 也就是说,对象中不会出现填充位。只加一次 UCHAR_MAX+1 到 -1 将产生一个范围内的值,即 UCHAR_MAX

够了,真的!因此,无论何时您想拥有一个所有位都为一的 unsigned char,您可以

unsigned char c = (unsigned char)-1;

还可以看出,转换不仅仅是截断高阶位。二进制补码的幸运之处在于它只是一个截断,但对于其他符号表示不一定如此。


为什么不直接使用 UCHAR_MAX
因为 (unsigned type)-1 是某种成语。 ~0 不是。
如果我有类似 int x = 1234char *y = &x 的东西。 1234 的二进制表示是 00000000 00000000 00000100 11010010 。我的机器是小端的,所以它将它反转并存储在内存中 11010010 00000100 00000000 00000000 LSB 排在第一位。现在主要部分。如果我使用 printf("%d" , *p)printf 将读取第一个字节 11010010只有输出是 -4611010010210 那么为什么要打印 -46 。我真的很困惑,我猜一些字符到整数的提升正在做某事,但我不知道。
N
NAND

例如 unsigned char 的用法:

unsigned char 经常用在计算机图形中,它经常(但不总是)为每个颜色分量分配一个字节。通常看到 RGB(或 RGBA)颜色表示为 24(或 32)位,每个位是 unsigned char。由于 unsigned char 值在 [0,255] 范围内,因此这些值通常被解释为:

0 表示完全没有给定的颜色分量。

255 表示给定颜色颜料的 100%。

所以你最终会得到 RGB 红色为 (255,0,0) -> (100% red, 0% green, 0% blue)。

为什么不使用 signed char?算术和位移成为问题。如前所述,signed char 的范围基本上移动了 -128。将 RGB 转换为灰度的一种非常简单且幼稚(大部分未使用)的方法是对所有三个颜色分量进行平均,但是当颜色分量的值为负时,这会遇到问题。使用 unsigned char 算术时,红色 (255, 0, 0) 的平均值为 (85, 85, 85)。但是,如果值是 signed chars (127,-128,-128),我们最终会得到 (-99, -99, -99),在我们的 {3 } 空间,这是不正确的。


我可能会遗漏一些东西,但我不知道固定班次如何打破算术平均值。 127、-128 和 -128 的平均值是 -43,而不是 -99。如果您将 128 添加到其中,您将得到 85,这与您的未签名示例相同。
J
James Hopkin

signed char 的范围为 -128 到 127; unsigned char 的范围为 0 到 255。

char 将等效于有符号字符或无符号字符,具体取决于编译器,但它是一种不同的类型。

如果您使用 C 风格的字符串,只需使用 char。如果您需要将字符用于算术(非常罕见),请显式指定有符号或无符号以实现可移植性。


w
whoan

unsigned char 只取正值....例如 0255

然而

signed char 接受正值和负值....例如 -128+127


P
Pang

charunsigned char 不保证在所有平台上都是 8 位类型 - 它们保证是 8 位或更大。一些平台有 9-bit, 32-bit, or 64-bit bytes。但是,当今最常见的平台(Windows、Mac、Linux x86 等)具有 8 位字节。


P
PaSTE

unsigned char 是无符号字节值(0 到 255)。您可能认为 char 是一个“字符”,但它实际上是一个数值。常规 char 是有符号的,因此您有 128 个值,这些值使用 ASCII 编码映射到字符。但无论哪种情况,您在内存中存储的是一个字节值。


“常规字符已签名”:不,它取决于实现。并且不能保证 unsigned char 的值范围是从 0 到 255:至少是这样,但它可能更宽。
char 不保证是一个字节。
Y
Yu Hao

就直接值而言,当已知值介于 CHAR_MINCHAR_MAX 之间时,使用常规 char,而 unsigned char 在正端提供两倍的范围。例如,如果 CHAR_BIT 为 8,则正则 char 的范围仅保证为 [0, 127](因为它可以有符号或无符号),而 unsigned char 将是 [0, 255] 和 {6 } 将是 [-127, 127]。

就其用途而言,标准允许将 POD 对象(普通旧数据)直接转换为无符号字符数组。这允许您检查对象的表示和位模式。 char 或signed char 不存在相同的安全类型双关语保证。


实际上,它通常是 [-128, 128]。
标准仅正式将对象表示定义为 unsigned char序列,而不是具体的 数组,&任何“转换”只能通过从对象复制unsigned char & 的真实声明的array 来正式定义。然后检查后者。尚不清楚 OR 是否可以直接重新解释为这样的数组,允许指针算术它需要,即在这种用法中是否“序列”==“数组”。有一个核心问题#1701 打开,希望得到澄清。谢天谢地,因为这种模棱两可最近真的困扰着我。
@RastaJedi 不,不会。它不能。 -128...+128 的范围在物理上不可能用 8 位来表示。该宽度仅支持 2^8 == 256 个离散值,但 -128...+128 = 2 * 128 + 1 表示 0 = 257。符号幅度表示允许 -127...+127 但有 2 个(双极)零。二进制补码表示保持一个零,但通过在负侧多一个值来构成范围;它允许 -128...+127。 (以此类推,对于更大的位宽。)
关于我的第二条评论,假定我们可以将指针指向 OR 的第一个 unsigned char,然后从那里继续使用 ++ptr 来读取它的每个字节......但是 AFAICT ,它没有被明确定义为允许,因此我们只能从标准,类似于拼图游戏。这并不理想。好吧,也许措辞最终会有所改善。这是我提到的 CWG 问题,但缺少链接空间 - open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1701
@underscore_d 抱歉,这是一个错字。 [-128, 127] 是我输入的意思:p。是的,我知道带有符号/大小的双零(“正”和“负”零)。我一定很累:p。
N
NAND

如果您喜欢使用各种类型的特定长度和符号,那么使用 uint8_tint8_tuint16_t 等可能会更好,因为它们完全按照他们所说的做。


Y
Yun

unsigned char 是所有诡计的核心。在所有平台的几乎所有编译器中,unsigned char只是一个字节和一个(通常)8位的无符号整数,可以被处理作为一个小整数或一组位。

此外,正如其他人所说,该标准没有定义字符的符号。因此,您有 3 种不同的 char 类型:charsigned charunsigned char


n
neuroguy123

一些谷歌搜索发现了 this,人们对此进行了讨论。

无符号字符基本上是一个字节。因此,如果您需要一个字节的数据(例如,您可能想使用它来设置标志打开和关闭以传递给函数,就像在 Windows API 中经常做的那样),您会使用它。


佚名

unsigned char 使用为常规 char 的符号保留的位作为另一个数字。这会将范围更改为 [0 - 255],而不是 [-128 - 127]。

当您不想要符号时,通常使用无符号字符。在将 char 作为字节处理而不是将其用作数字时,这将在执行诸如移位(移位扩展符号)之类的事情和其他事情时产生影响。


N
NAND

unsigned char 只取正值:0 到 255,而 signed char 取正值和负值:-128 到 +127。


Z
ZhaoGang

引用自“c 编程语言”一书:

限定符 signedunsigned 可以应用于 char 或任何整数。无符号数始终为正数或零,并遵守算术模 2^n 的定律,其中 n 是类型中的位数。因此,例如,如果 char 是 8 位,则 unsigned char 变量的值介于 0 和 255 之间,而有符号 char 的值介于 -128 和 127 之间(在二进制补码机器中)。普通字符是有符号还是无符号是机器- 依赖,但可打印的字符始终为正数。


K
Kalana

signed charunsigned char 都代表 1 个字节,但它们的范围不同。

   Type        |      range
-------------------------------
signed char    |  -128 to +127
unsigned char  |     0 to 255

signed char中如果我们考虑char letter = 'A','A'表示ASCII/Unicode中65的二进制,如果可以存储65,也可以存储-65。 ASCII/Unicode 中没有负二进制值,无需担心负值。

例子

#include <stdio.h>

int main()
{
    signed char char1 = 255;
    signed char char2 = -128;
    unsigned char char3 = 255;
    unsigned char char4 = -128;

    printf("Signed char(255) : %d\n",char1);
    printf("Unsigned char(255) : %d\n",char3);

    printf("\nSigned char(-128) : %d\n",char2);
    printf("Unsigned char(-128) : %d\n",char4);

    return 0;
}

输出 -:

Signed char(255) : -1
Unsigned char(255) : 255

Signed char(-128) : -128
Unsigned char(-128) : 128

char 不保证是一个字节,并且 signed char 只保证保持范围 [-127,127](尽管几乎所有系统都使用二进制补码并至少保持 [-128,127])