【问题标题】:Strange output when printing the value 0x89 (-119)打印值 0x89 (-119) 时的奇怪输出
【发布时间】:2013-06-30 14:24:07
【问题描述】:

正如标题所说,运行以下代码时,我得到了一个“奇怪”的结果:

#include <stdio.h>

int main()
{
    char buff[4] = {0x17, 0x89, 0x39, 0x40};
    unsigned int* ptr = (unsigned int*)buff;
    char a = (char)((*ptr << (0*8)) >> (3*8));
    char b = (char)((*ptr << (1*8)) >> (3*8));
    char c = (char)((*ptr << (2*8)) >> (3*8));
    char d = (char)((*ptr << (3*8)) >> (3*8));

    printf("0x%x\n", *ptr);
    printf("0x%x\n", a);
    printf("0x%x\n", b);
    printf("0x%x\n", c);
    printf("0x%x\n", d);

    return 0;
}

输出:

0x40398917
0x40
0x39
0xffffff89
0x17

为什么我没有收到0x89

【问题讨论】:

    标签: c++ c hex output


    【解决方案1】:

    这是因为您的 char 变量已签名,并且在升级时它们正在进行符号扩展(在这种情况下升级为更广泛的类型)。符号扩展是在进行此促销时保留符号的一种方式,以便-119 保持为 -119,无论是 8 位、16 位还是更宽的类型。

    您可以通过显式使用unsigned char 来修复它,因为至少在C 中,char 是有符号还是无符号是特定于实现的。来自C11 6.2.5 Types /15

    实现应将 char 定义为与有符号字符或无符号字符具有相同的范围、表示和行为。

    符号扩展不适用于无符号类型,因为它们是……嗯,无符号 :-)

    【讨论】:

    • 升级到更广泛的类型”你的意思是:提升?
    【解决方案2】:

    char,默认情况下,是有符号的——这意味着数字从 -128 到 127。除此之外的任何数字都不适合。如果您将char 更改为unsigned char,您将获得您期望的数字。

    【讨论】:

    • 是不是 char 0x89 等于 -119 介于 -128 和 127 之间?
    • char 默认不是signedstackoverflow.com/questions/2054939/…
    • 是的。 89 hex 作为有符号整数是 -119。 -119,当以十六进制打印时,变为 ffffff89。 89 hex和ffffff89的区别是char和integer的区别。
    【解决方案3】:

    使用memcpy 而不是演员

    char buff[4] = {0x17, 0x89, 0x39, 0x40};
    unsigned int* ptr = (unsigned int*)buff;
    

    这是不正确的buff 不指向 int 对象或数组,因此未定义转换 (unsigned int*)buff

    buff 重新解释为unsigned int 的安全方法是使用memcpy

    char buff[4] = {0x17, 0x89, 0x39, 0x40};
    unsigned int ui;
    assert (sizeof ui == sizeof buff);
    memcpy (buff, &ui, sizeof ui);
    

    当然,当使用memcpy 时,您无法确保复制的位表示对目标类型有效。

    一种可移植但退化的方法是检查表示是否与现有对象匹配(注意,以下是愚蠢的代码):

    char *null_ptr = 0;
    char null_bytes[sizeof null_ptr] = {0};
    if (memcmp (null_ptr, null_bytes, sizeof null_bytes)==0) {
        char *ptr2;
        memcpy (null_bytes, ptr2, sizeof null_bytes);
        assert (ptr2 == 0);
    }
    

    此代码使用memcpy 并具有完全定义的行为(即使无用)。 OTOH,

    的行为
    int *ptr3 = (int*)null_bytes;
    

    未定义,因为null_bytes 不是intunsigned int 的地址。

    【讨论】:

    • “它违反了打字规则”是什么意思?
    • @j_kubik union 用于类型双关语,在大多数情况下,会破坏类型别名规则。这里可能没问题(但有些人会说不是)。
    • @Jonas 一般来说,union 存在类型别名规则问题,但memcpy 是安全的。
    • memcpy 并不是对指针类型转换的改进:指针类型转换中未定义的行为表示我们对类型内存布局缺乏了解。如果你改变解释内存的方式,或者将它一点一点地复制到另一个变量中没有任何区别。
    • @j_kubik 的改进。 “指针类型转换的未定义行为表示我们缺乏关于类型内存布局的知识” 不。类型转换本身不会重新解释位表示,只有使用结果指针会。这里的演员表本身是不正确的。当然,如果您不知道目标类型的正确位表示是什么,memcpy 对您没有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-26
    • 1970-01-01
    • 1970-01-01
    • 2015-08-18
    • 1970-01-01
    • 2014-04-10
    • 1970-01-01
    相关资源
    最近更新 更多