当十六进制值(超过 0x7f)分配给(有符号)char 时,C 中的奇怪行为

问题描述 投票:0回答:1

当将十六进制值“0x000000a1”分配给(有符号)char 时,char 等于“0xffffffa1”。 谁能解释一下这种奇怪的行为吗?

void main(){
    char testVar = 0x000000a1;

    printf("%x \n",testVar); //prints ffffffa1
    printf("%d \n",testVar); //prints -95

}

当您初始化无符号字符时,它会按预期工作

void main(){
    unsigned char testVar = 0x000000a1;

    printf("%x \n",testVar); //prints a1
    printf("%d \n",testVar); //prints 161

}

当您分配 ASCII 限制 127(或 0x7f)内的值时,也可以按预期工作

void main(){
    unsigned char testVar = 0x7f;

    printf("%x \n",testVar); //prints 7f
    printf("%d \n",testVar); //prints 127

}

问完问题后我现在明白了:

  • 不能将 0x000000a1 分配给字符,因为字符只有 2 个字节长。您只分配 a1 部分。
  • 有符号值的第一位表示是否为负数 (1000 0000 是负数,等于 -128。0111111 不是负数,等于 127)
  • 由于某种原因 printf 需要将 char 变量扩展为 int 才能输出值。
  • 当 printf 将有符号 char 转换为 int 时,它会识别 char 何时为负数,因此通过用 1 而不是 0 扩展来保留负值。 (1000 0000b(-128dez)扩展到 1111 1111 ... 1111 1111 1000 0000b(仍然是 -128dez))
c char hex unsigned signed
1个回答
0
投票

不能将 0x000000a1 分配给字符,因为字符只有 2 个字节长。您只分配 a1 部分。

A

char
根据定义always 1 字节大。 (不过,在外来系统上它可能有超过 8 位。)这里的关键不是
char
具有实现定义的签名:它可以是有符号的或无符号的,具体取决于编译器。 char 默认情况下是签名还是未签名? 显然在你的情况下它是签名的。

值前面的零数量没有丝毫影响,也不影响类型。他们所做的只是愚弄程序员,认为他们有一定的意义。

有符号值的第一位表示是否为负数(1000 0000 为负数,等于 -128。0111111 不是负数,等于 127)

是的,如果是 MSB。

0xa1
赋值给
signed char
时会发生什么也是特定于编译器的。在主流系统上,这将导致负的二进制补码值
-95
十进制。

或者某些原因 printf 需要将 char 变量扩展为 int 才能输出值。

printf
是一个可变参数函数(可变数量的参数),对于这些函数,有一个特殊的隐式类型提升规则,称为“默认参数提升”。当将小整数类型(例如
char
short
)传递给可变参数函数时,它总是会隐式提升为
int
。 (当通过
float
时,它将晋升为
double
。)

当 printf 将有符号 char 转换为 int 时,它会识别 char 何时为负数,因此通过用 1 而不是 0 扩展来保留负值

正确。在此促销期间,如果您的原始类型已签名且具有负值,则会尊重这一点并保留符号,即所谓的“符号扩展”。因此,在事物的二进制表示中,您得到的是

char
0xffffffa1 = 仍然 -95,而不是(有符号)
int
0xa1 = -95。

然后你用

%x
对 printf 撒谎,告诉它需要一个
unsigned int
参数。严格来说,这是未定义的行为,但任何明智的编译器都会将
int
转换为
unsigned int
,这是一个明确定义的转换,留下 0xffffffa1 但现在它是无符号表示,因此它等于十进制值 4294967201。

© www.soinside.com 2019 - 2024. All rights reserved.