29

鉴于有符号和无符号整数使用相同的寄存器等,并且只是以不同的方式解释位模式,而 C 字符基本上只是 8 位整数,那么 C 中的有符号和无符号字符有什么区别?我知道 char 的符号是实现定义的,我根本无法理解它如何产生影响,至少当 char 用于保存字符串而不是进行数学运算时。

4

9 回答 9

34

它不会对字符串产生影响。但是在 C 中,您可以使用 char 来进行数学运算,这会有所作为。

事实上,当在内存受限的环境中工作时,比如嵌入式 8 位应用程序,通常会使用 char 来进行数学运算,然后它会产生很大的不同。byte这是因为C 中默认没有类型。

于 2009-01-16T18:06:33.427 回答
29

就它们所代表的价值而言:

无符号的字符:

  • 跨越值范围0..255 (00000000..11111111)
  • 值在低边缘溢出为:

    0 - 1 = 255 (00000000 - 00000001 = 11111111)

  • 值在高边溢出为:

    255 + 1 = 0 (11111111 + 00000001 = 00000000)

  • 按位右移运算符 ( >>) 进行逻辑移位:

    10000000 >> 1 = 01000000 (128 / 2 = 64)

签名字符:

  • 跨越值范围-128..127 (10000000..01111111)
  • 值在低边缘溢出为:

    -128 - 1 = 127 (10000000 - 00000001 = 01111111)

  • 值在高边溢出为:

    127 + 1 = -128 (01111111 + 00000001 = 10000000)

  • 按位右移运算符 ( >>) 进行算术移位:

    10000000 >> 1 = 11000000 (-128 / 2 = -64)

我包含了二进制表示,以表明值包装行为是纯粹的、一致的二进制算术,并且与有符号/无符号的 char 无关(期望右移)。

更新

评论中提到的一些特定于实现的行为:

于 2009-01-16T19:22:49.670 回答
11
#include <stdio.h>

int main(int argc, char** argv)
{
    char a = 'A';
    char b = 0xFF;
    signed char sa = 'A';
    signed char sb = 0xFF;
    unsigned char ua = 'A';
    unsigned char ub = 0xFF;
    printf("a > b: %s\n", a > b ? "true" : "false");
    printf("sa > sb: %s\n", sa > sb ? "true" : "false");
    printf("ua > ub: %s\n", ua > ub ? "true" : "false");
    return 0;
}


[root]# ./a.out
a > b: true
sa > sb: true
ua > ub: false

排序字符串时很重要。

于 2009-01-16T18:05:55.723 回答
3

有几个区别。最重要的是,如果您通过为其分配一个太大或太小的整数来溢出 char 的有效范围,并且 char 是有符号的,则结果值是实现定义的,甚至某些信号(在 C 中)可能会上升,就像所有有符号类型一样. 与将太大或太小的东西分配给 unsigned char 的情况相比:值环绕,您将获得精确定义的语义。例如,将 -1 分配给 unsigned char,您将获得 UCHAR_MAX。因此,只要您有一个字节,例如从 0 到 2^CHAR_BIT 的数字,您就应该真正使用 unsigned char 来存储它。

传递给可变参数函数时,该符号也会有所不同:

char c = getSomeCharacter(); // returns 0..255
printf("%d\n", c);

假设分配给 c 的值对于 char 来说太大而无法表示,并且机器使用二进制补码。许多实现的行为适用于您为 char 分配的值太大的情况,因为位模式不会改变。如果一个 int 能够表示 char 的所有值(对于大多数实现来说都是如此),那么 char 在传递给 printf 之前将被提升为 int。因此,传递的值将是负数。提升为 int 将保留该标志。所以你会得到一个否定的结果。但是,如果 char 是无符号的,则该值是无符号的,并且提升为 int 将产生正 int。您可以使用 unsigned char,然后您将获得精确定义的行为,既可以分配给变量,也可以传递给 printf,然后打印一些积极的东西。

请注意,char、unsigned 和 signed char 都至少有8 位宽。不要求 char正好是8 位宽。但是,对于大多数系统而言,这是正确的,但对于某些系统,您会发现它们使用 32 位字符。C 和 C++ 中的字节被定义为具有 char 的大小,因此 C 中的字节也不总是正好是 8 位。

另一个区别是,在 C 中,无符号字符必须没有填充位。也就是说,如果您发现 CHAR_BIT 为 8,那么 unsigned char 的值必须在 0 .. 2^CHAR_BIT-1 的范围内。如果它是无符号的,char 也是如此。对于有符号字符,您不能假设任何值的范围,即使您知道编译器如何实现符号内容(二进制补码或其他选项),其中可能有未使用的填充位。在 C++ 中,所有三种字符类型都没有填充位。

于 2009-01-16T18:34:01.063 回答
2

“一个char被签名是什么意思?”

传统上,ASCII 字符集由 7 位字符编码组成。(与 8 位 EBCIDIC 不同。)

在设计和实现 C 语言时,这是一个重大问题。(出于各种原因,例如通过串行调制解调器设备进行数据传输。)额外的位具有奇偶校验等用途。

“签名字符”恰好适合这种表示。

二进制数据 OTOH 只是取每个 8 位数据“块”的值,因此不需要符号。

于 2009-01-16T19:22:51.153 回答
1

字节算术对于计算机图形很重要(其中 8 位值通常用于存储颜色)。除此之外,我可以想到 char 符号很重要的两种主要情况:

  • 转换为更大的 int
  • 比较函数

讨厌的是,如果你所有的字符串数据都是 7 位的,这些就不会咬你。但是,如果您试图使您的 C/C++ 程序 8 位干净,那么它肯定会成为无休止的错误来源。

于 2009-01-16T19:51:32.640 回答
1

charSignedness 在s 中的工作方式与在其他整数类型中的工作方式几乎相同。正如您所指出的,字符实际上只是一字节整数。(但不一定是 8-bit!有区别;在某些平台上,一个字节可能大于 8 位,并且由于andchar的定义, s 与字节相当相关。在or C++'s中定义的宏会告诉你有多少位。)。charsizeof(char)CHAR_BIT<limits.h><climits>char

至于为什么你想要一个带符号的字符:在 C 和 C++ 中,没有称为byte. 对于编译器,chars 是字节,反之亦然,它不区分它们。但是,有时您想要——有时您希望char是一个单字节数字,在这些情况下(特别是一个字节的范围可以有多小),您通常还关心该数字是否有符号。我个人使用有符号(或无符号)来表示某个char是(数字)“字节”而不是字符,并且它将以数字形式使用。如果没有指定的符号,那char确实是一个字符,并且旨在用作文本。

我曾经这样做过,而不是。现在,较新版本的 C 和 C++ 具有(u?)int_least8_t(目前在<stdint.h>or中的 typedef'd <cstdint>),它们更明确地是数字的(尽管它们通常只是有符号和无符号char类型的 typedef)。

于 2013-04-16T17:33:03.953 回答
0

我可以想象这是一个问题的唯一情况是,如果您选择对字符进行数学运算。编写以下代码是完全合法的。

char a = (char)42;
char b = (char)120;
char c = a + b;

根据 char 的符号,c 可能是两个值之一。如果 char 是无符号的,则 c 将为 (char)162。如果它们已签名,那么它将出现溢出情况,因为已签名字符的最大值为 128。我猜大多数实现只会返回 (char)-32。

于 2009-01-16T18:05:52.197 回答
0

关于签名字符的一件事是您可以测试 c >= ' ' (空格) 并确保它是正常的可打印 ascii 字符。当然,它不是便携式的,所以不是很有用。

于 2009-01-16T18:39:26.840 回答