-2

这可能是一个非常基本的问题,但我无法做到。这是我正在使用的。

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int s;
    c1 = 128;
    c2 = -128;

    s = sizeof(char);

    printf("size of char: %d\n", s);
    printf("c1: %x, c2: %x\n", c1, c2);
    printf("true or false: %d\n", c1 == c2);
}

结果是这样的。

size of char: 1
c1: ffffff80, c2: ffffff80
true or false: 1

我将值 128 分配给有符号(普通)字符类型,但它没有溢出。

此外,c1 和 c2 似乎都包含 4 个字节,并且 -128 和 128 是相同的值。

我怎样才能理解这些事实?我需要你的帮助。非常感谢。

4

4 回答 4

5

在, 128 不适合您的 C 实现使用c1 = 128;的有符号八位。char128 转换为charper C 2018 6.5.16.1 2:“右操作数的值转换为赋值表达式的类型……”</p>

转换是实现定义的,根据 6.3.1.3 3:“否则,新类型是有符号的,值不能在其中表示;结果要么是实现定义的,要么是产生实现定义的信号。” 您的 C 实现将 128(即 10000000 2作为无符号二进制数字)转换为 -128,当对有符号二进制使用二进制补码时,它用相同的位表示。因此,结果是c1包含值 -128。

printf("c1: %x, c2: %x\n", c1, c2);,c1被转换为int. 这是因为使用...参数调用函数的规则是将默认参数提升应用于相应的参数,根据 6.5.2.2 7:“默认参数提升是在尾随参数上执行的。”</p>

默认参数提升包括整数提升,根据 6.5.2.2 6。当 的范围char小于int时,就像在大多数 C 实现中一样,整数提升将 a 转换charint,根据 6.3.1.1 2:“如果 anint可以表示所有原始类型的值……,该值被转换为int……”</p>

因此,在 中printf("c1: %x, c2: %x\n", c1, c2);int-128 的值作为第二个参数传递。您的 C 实现使用 32 位二进制补码int,其中 -128 用位 11111111111111111111111110000000 表示,我们可以用十六进制表示为 ffffff80。

格式字符串指定使用%x. 正确的参数类型%xunsigned int. 但是,您的 C 实现已接受int并将其重新解释为unsigned int. 因此,11111111111111111111111110000000 位被转换为字符串“ffffff80”。

这就解释了为什么要打印“ffffff80”。不是因为c1有四个字节,而是因为它在传递给printf. 此外,将负值转换为该四字节类型会导致设置了许多位的四个字节。

关于c1 == c2评估为真(1),这仅仅是因为c1如上所述被赋予值 -128,并且c2 = -128;还将值 -128 分配给c2,因此c1c2具有相同的值。

于 2020-06-15T17:17:34.097 回答
2

根据编译器选项或编译器的默认设置,类型char可以表现为类型signed char或类型。unsigned char

在您的情况下,类型char表现为 type signed char。在这种情况下CHAR_MIN等于-128CHAR_MAX等于127

因此,该类型的对象char不能保存正数 128。在内部,该值具有以下十六进制表示0x80。因此存储在类型的对象中,char它被解释为负值,因为设置了符号位。这个负值是-128

所以在这些陈述之后

c1 = 128;
c2 = -128;

这两个对象具有相同的值,等于-128

和输出

c1: ffffff80, c2: ffffff80

这个电话的

printf("c1: %x, c2: %x\n", c1, c2);

表明这两个对象c1c2提升到类型int 具有相同的负值表示。

请注意,为有符号类型的对象分配一个无法在对象中表示的正值是实现定义的行为。

于 2020-06-15T16:58:07.120 回答
1

在声明中

printf("c1: %x, c2: %x\n", c1, c2);

%x需要一个 type 的参数,因此和unsigned int的值从to提升,并扩展了前导位。要将 an 的数值打印为十六进制,您需要在转换中使用长度修饰符:c1c2charunsigned intunsigned charhh

printf("c1: %hhx, c2: %hhx\n", c1, c2 );

至于可以在 a 中表示的值,则char比这要复杂一些。

基本字符集1的成员的编码保证为非负数。附加字符的编码可能是负数或非负数。

因此,根据实现,一个plainchar可以表示至少范围内的值[-128..127](假设二进制补码表示) [0..255]. 我说“至少”,因为CHAR_BIT可能超过 8 个(历史系统使用 9 位字节和 36 位字)。Asigned char将至少表示范围内的值[-128..127](再次假设二进制补码)。

假设char是有符号的 8 位,那么分配 128 会c1导致有符号整数溢出并且其行为是undefined,这意味着编译器和执行环境不需要以任何特定方式处理它。 就需要语言定义而言, 任何结果都是“正确的”,无论它是否是您预期的结果。


  1. 大写和小写拉丁字母、十进制数字、29 个图形字符、空格和控制字符(换行、换页、制表符等)。

于 2020-06-15T17:24:07.610 回答
0

这里解释:https ://en.wikipedia.org/wiki/Signed_number_representations

如果 -128 和 128 以及介于两者之间的所有数字都用一个字节表示,那么该集合中将有 257 个数字。但是我们没有,它只有 256。

其映射如下十进制:[0..127,-128..-1] => [0b00000000..0b11111111]。请注意,第一位在 -128 处变为 1,这是一个快乐的意外;)。

此外,您的字符串格式不正确,您的编译器应该警告您,%x 需要 4 个字节!如果你考虑到我之前所说的,你会发现 0x80 确实是 0b10000000。

于 2020-06-15T16:58:34.943 回答