使用 %u 读取签名的字符

Reading signed char using %u

#include <stdio.h>

int main() {
    int i,n;
    int a = 123456789;

    void *v = &a;

    unsigned char *c = (unsigned char*)v;

    for(i=0;i< sizeof a;i++) {
        printf("%u  ",*(c+i));
    }

    char *cc = (char*)v;
    printf("\n %d", *(cc+1));

    char *ccc = (char*)v;
    printf("\n %u \n", *(ccc+1));

}

此程序在我的 32 位 Ubuntu 机器上生成以下输出。

21  205  91  7  
-51
4294967245

前两行输出我能看懂=>

请解释最后一行输出。为什么要添加三个字节的 1 因为 (11111111111111111111111111001101) = 4294967245

-51 以 8 位十六进制存储为 0xCD。 (假设2s补码二进制)

当您将它传递给 printf 之类的 variadic function 时,会进行默认参数提升,并且 char 会提升为 int,表示形式为 0xFFFFFFCD(对于 4 字节 int).

0xFFFFFFCD解释为int-51,解释为unsigned int4294967245.

进一步阅读:Default argument promotions in C function calls


please explain the last line of output. WHY three bytes of 1's are added

这个叫做sign extension. When a smaller signed number is assigned (converted) to larger number, its signed bit get's replicated to ensure it represents same number (for example in 1s and 2s compliment).


错误 printf 格式说明符
您正在尝试使用指定 unsigned [int] 的说明符 "%u" 打印 char。与 printf 中的转换说明符不匹配的参数是 7.19.6.1 第 9 段中的未定义行为。

If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

使用char存储有符号值
还要确保 char 包含 signed 值,显式使用 signed char 因为 char 可能表现为 signed charunsigned char。 (在后一种情况下,您的代码段的输出可能是 205 205)。在 gcc 中,您可以使用 -funsigned-char 选项强制 char 表现得像 unsigned char

显然你的编译器使用带符号的字符,它是小端,二进制补码系统。

123456789d = 075BCD15h
Little endian: 15 CD 5B 07

因此 v+1 给出值 0xCD。当它存储在带符号的字符中时,您会得到带符号的十进制格式的 -51

当传递给 printf 时,包含值 -51 的字符 *(ccc+1) 首先被隐式类型提升为 int,因为像 printf 这样的可变参数函数有一条规则说明所有小整数参数将被提升为 int 默认参数提升 )。在此促销期间,标志被保留。您仍然有值 -51,但是对于 32 位有符号整数,这给出了值 0xFFFFFFCD.

最后 %u 说明符告诉 printf 将其视为无符号整数,因此您最终得到 42.9 亿。

这里要理解的重要部分是%u与实际的类型提升无关,它只是告诉printf如何在提升之后解释数据.