【问题标题】:Testing the endianness of a machine测试机器的字节顺序
【发布时间】:2011-09-04 17:23:24
【问题描述】:

这是我使用的程序:

int hex = 0x23456789;
char * val = &hex;
printf("%p\n",hex);
printf("%p %p %p %p\n",*val,*(val+1),*(val+2),*(val+3));

这是我的输出:

0x23456789
0xffffff89 0x67 0x45 0x23

我正在使用带有 64 位操作系统的 64 位 CPU。这表明我的机器是小端的。为什么第一个字节是 0xffffff89?为什么是ff?

【问题讨论】:

    标签: c endianness


    【解决方案1】:

    首先,您应该使用 %x,因为它们不是指针。

    %x 说明符需要一个整数。因为您传递的是一个“char”类型的值,它是一个有符号类型,所以该值被转换为整数并被符号扩展。 http://en.wikipedia.org/wiki/Sign_extension

    这实质上意味着它需要最高有效位并将其用于所有较高位。所以 0x89 => 0b10001001 ,最高位为 '1' 变为 0xFFFFFF89。

    正确的解决方案是指定一个'length'参数选项。您可以在这里获得更多信息:Printf Placeholders 本质上,在 '%' 和 'x' 之间,您可以放置​​额外的参数。 'hh' 表示您正在传递一个 char 值。

    int hex = 0x23456789;
    char *val = (char*)&hex;
    
    printf("%x\n",hex);
    printf("%hhx %hhx %hhx %hhx\n", val[0], val[1], val[2], val[3]);
    

    【讨论】:

    • "%p" 需要 void *,而不是整数。任何指针都可以在大多数机器上工作,但要在技术上正确(因为可变参数),您应该在使用它时显式转换。
    • 另外,您需要使用"%hhx" 来打印unsigned char 值,而不是完整大小的unsigned 值。
    • 不错!我知道'h',但我不知道'hh'存在,所以这就是为什么我有未签名的演员表。我已经更新了我的答案以包含这种更好的方法。
    【解决方案2】:

    char 是有符号类型,当作为参数传递时,它会被提升为 int。此促销导致符号扩展。 0x89 是 char 的负值,因此它被符号扩展为 0xffffff89。其他值不会发生这种情况,在大多数机器上它们不会超过 CHAR_MAX、127 或 0x7f。您对此行为感到困惑,因为您使用了错误的格式说明符。

    【讨论】:

      【解决方案3】:

      %p 在问printf to format it as an address,你实际上是在传递一个值 (*val)

      在 64 位机器上,指针地址是 64 位,因此 printf 正在添加 ffff 以填充字段

      【讨论】:

      • 为什么不对其他值这样做?像0xffffff67。我使用 %x 而不是 %p 并且得到相同的输出。
      • 这是未定义的行为,您要求它将不是地址的值打印为地址。所以它可以使用堆栈中的下一个值,或者它可以打印粉红色的独角兽
      • @Bruce:因为它正在符号扩展。我敢打赌,如果您将 val 声明为 unsigned char*,它会改变。
      【解决方案4】:

      正如@Martin Beckett 所说,%p 要求printf 打印一个指针,相当于%#x%#lx(具体格式取决于您的操作系统)。

      这意味着printf 期望intlong(同样取决于操作系统),但您只提供char,因此该值向上转换为适当的类型。

      当您将较小的有符号数转换为较大的有符号数时,您必须执行名为 sign extension 的操作以保留该值。在0x89 的情况下,这是因为设置了符号位,所以高字节是0xff 并且因为它们很重要而被打印出来。

      0x670x450x23的情况下,由于没有设置符号位,所以不会发生符号扩展,因此高字节为0,因此不打印。

      【讨论】:

        【解决方案5】:

        我使用条件((char)((int)511) == (char)255) 测试字节序。真为小,假为大。

        我已经在几个单独的系统上测试了这个,无论大小,使用 gcc 并关闭优化。在我所做的每一项测试中,我都得到了正确的结果。

        您可以在应用程序需要执行字节序关键操作之前将该条件放在应用程序的 if 中。如果您只想保证您在整个应用程序中使用正确的字节序,您可以改用静态断言方法,如下所示:

        extern char ASSERTION__LITTLE_ENDIAN[((char)((int)511) == (char)255)?1:-1];
        

        如果系统不是 little endian,全局范围内的那一行将产生编译错误,并拒绝编译。如果没有错误,它将完美编译,就好像该行不存在一样。我发现错误消息非常具有描述性:

        error: size of array 'ASSERTION__LITTLE_ENDIAN' is negative
        

        现在,如果您像我一样对编译器优化实际检查感到偏执,您可以执行以下操作:

        int endian;
        {
            int i = 255;
            char * c = &i;
            endian = (c[0] == (char)255);
        }
        if(endian) // if endian is little
        

        很好地压缩到这个宏中:

        #define isLittleEndian(e) int e; { int i = 255; char * c = &i; e = (c[0] == (char)255); }
        isLittleEndian(endian);
        if(endian) // if endian is little
        

        或者如果你使用 GCC,你可以逃脱:

        #define isLittleEndian ({int i = 255; char * c = &i; (c[0] == (char)255);})
        if(isLittleEndian) // if endian is little
        

        【讨论】:

        • 你能解释一下你答案的第一行吗?
        猜你喜欢
        • 1970-01-01
        • 2012-02-17
        • 2012-05-06
        • 1970-01-01
        • 2015-01-07
        • 1970-01-01
        • 2015-12-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多