C 转换和字符符号

C cast and char signedness

所以最近,我读到一个关于 C 中三种不同类型的问题,char/unsigned char/signed char。我现在遇到的问题不是我到目前为止遇到的问题(我的程序在所有经过测试的计算机上都能正常工作,并且只针对小端(基本上所有现代桌面和服务器都使用 Windows/Linux 对吗?)。我经常重复使用我定义的一个 char 数组,用于保存 "string"(当然不是真正的字符串)作为临时变量。例如,我没有将另一个 char 添加到堆栈,而是重用其中一个成员,如 array[0]。但是,我这种策略基于一个 char 总是被签名的事实,直到我今天读到它实际上取决于实现。如果我现在有一个 char 并且我为它分配一个负值会发生什么?

char unknownsignedness = -1;

如果我写

unsigned char A = -1;

我认为 C 风格的转换将简单地重新解释位,并且 A 表示为无符号类型的值变得不同。这些 C 风格的转换只是对位的重新解释,我说得对吗?我现在指的是有符号 <-> 无符号转换。

因此,如果一个实现将 char 作为无符号字符,我的程序是否会按预期停止工作?取最后一个变量,如果我现在做

if (A == -1)

我现在正在比较一个 unsigned char 和一个 signed char 值,所以这只是比较不关心符号的位,还是这个 return false 因为显然 A 不能是 -1?我很困惑在这种情况下会发生什么。这也是我最担心的,因为我经常使用这样的字符。

我认为这个问题最好用一个简单的例子来回答(警告:C++,但请参阅我的推理解释):

char c = -1;
unsigned char u = -1;
signed char s = -1;
if (c == u)
        printf("c == u\n");
if (s == u)
        printf("s == u\n");
if (s == c)
        printf("s == c\n");
if (static_cast<unsigned char>(s) == u)
        printf("(unsigned char)s == u\n");
if (c == static_cast<char>(u))
        printf("c == (char)u\n");

输出:

s == c
(unsigned char)s == u
c == (char)u

C 在按原样使用时会以不同方式处理这些值,但你是正确的,转换只会重新解释这些位。我在这里使用了 C++ static_cast 来表明编译器可以进行这种转换。在 C 中,您只需在括号中加上类型前缀即可进行强制转换。没有编译器检查来确保转换在 C 中是安全的。

以下代码打印 No:

#include <stdio.h>

int
main()
{
    unsigned char a;

    a = -1;

    if(a == -1)
        printf("Yes\n");
    else
        printf("No\n");

    return 0;
}

代码 a = -1 将实现定义的值分配给 a;在大多数机器上,a 将为 255。测试 a == -1unsigned charint 进行比较,因此适用通常的提升规则;因此,它被解释为

`(int)a == -1`

因为a是255,所以(int)a还是255,测试结果是false。

unsigned char A = -1;

结果为 255。分配或初始化时没有重新解释。 -1 只是一堆 1 二进制补码符号中的位,其中 8 位是逐字复制的。

比较有点不同,因为文字 -1int 类型。

if (A == -1)

将在比较之前进行提升(隐式转换)(int)A,因此您最终会将 255 与 -1 进行比较。不相等。

是的,你必须小心普通的 char

unsigned char a = -1;

ISO/IEC 9899:1999 在 6.3.1.3/2 中说:

if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type

我们把(UCHAR_MAX+1)加到-1一次,结果是UCHAR_MAX,显然在unsigned char的范围内。

if (a == -1)

6.3.1.8/1中有一段很长:

If both operands have the same type, then no further conversion is needed.

Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

unsigned char的排名低于int的排名。

如果int可以表示unsigned char可以表示的所有值(通常是这种情况),那么两个操作数都转换为int,比较returns false

如果 int 不能表示 unsigned char 中的所有值,这可能发生在具有 sizeof(int)==sizeof(char) 的少数机器上,那么两者都被转换为 unsigned int-1 转换为 UINT_MAX 恰好与 UCHAR_MAX 相同,比较 returns true.