【问题标题】:Why this way to detect machine storage methods is incorrect? (Using C language)为什么这种检测机器存储方式的方式是不正确的? (使用C语言)
【发布时间】:2018-03-25 15:28:58
【问题描述】:

我在做作业时错过了一个问题。

计算机内存中存储数据的地方有big-endian存储和small-endian存储两种方式,为了检测一种机器存储方式,有同学写了如下程序:

union NUM {
    int a;
    char b;
} num;

int main(){
    num.b = 0xff;
    if (num.a! = 0xff)
        printf ("bigend");
    else 
        printf ("smallend");
    return 0;
}

但是他发现在x86机器上运行的程序,打印出来的其实是'bigend',这显然是错误的。你知道问题出在哪里吗? 这个程序应该如何修改?

我问过我的老师,题目是对的。我在一些网站上找到了一些信息,但这让我更加困惑。为什么这个问题不正确?问题究竟出在哪里?

【问题讨论】:

  • 您应该尝试在 x86 计算机上使用显示“bigend”的调试器进行调试。
  • 如果将0xff替换为0x1,试试看是否有效。
  • 如果 num 是静态/全局变量,它应该可以工作。否则它可能会失败,因为并非a 的所有字节都已初始化。 0xFF 是有问题的,因为它取决于 char 的签名,尽管在这种特殊情况下它可能不是问题。
  • ... 或者只是 printf num.a 并查看它的实际内容。

标签: c cpu-architecture


【解决方案1】:

假设int(因此作为一个整体的联合)是 4 个字节,写入num.b 只会写入这 4 个字节中的一个,其余部分未初始化。随后读取num.a 读取那些未初始化的字节,调用undefined behavior

union 中的字节必须设置为全 0,以便明确定义内容。

#include <stdio.h>
#include <string.h>

union NUM {
    int a;
    char b;
} num;

int main(){
    // set all bytes of num to 0 first
    memset(&num, 0, sizeof(num));
    num.b = 0xff;
    if (num.a! = 0xff)
        printf ("bigend");
    else 
        printf ("smallend");
    return 0;
}

【讨论】:

  • memset() 可能使其适用于您当前的编译器版本,但读取 num.a 仍然未定义,因为这仍然违反严格的别名规则。
【解决方案2】:

类型双关是 C 中未定义的行为。当您阅读 num.a 时,您违反了严格的别名规则,因此允许编译器生成可能返回任何内容的代码。它的作用。

为避免这种情况,您需要使用memcpy():

int a = 0x00010203;
char bytes[sizeof(a)];
memcpy(bytes, &a, sizeof(a));
if(bytes[0] == 03) {
    printf("small endian\n");
} else ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 2021-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多