在C语言中打印整数的实际位表示

4
我想在C语言中打印整数的实际比特表示。以下是我找到的两种方法。
第一种方法:
union int_char {
    int val;
    unsigned char c[sizeof(int)];
} data;

data.val = n1;
// printf("Integer: %p\nFirst char: %p\nLast char: %p\n", &data.f, &data.c[0], &data.c[sizeof(int)-1]);

for(int i = 0; i < sizeof(int); i++)
    printf("%.2x", data.c[i]);
    printf("\n");

第二点:
for(int i = 0; i < 8*sizeof(int); i++) {
    int j = 8 * sizeof(int) - 1 - i;
    printf("%d", (val >> j) & 1);
}
printf("\n");

第二种方法的输出结果是0000000202000000。我还尝试了其他数字,似乎这两个字节被交换了。哪一个是正确的?


1
它们都是正确的,这取决于您想以什么顺序显示位。 - M.M
3
你的意思是第二段代码输出02000000printf("%d", (val >> j) & 1); 只应该输出0或者1,不应该输出2 - Gerhardh
第二个示例不会输出“2”。这两个示例明确按相反的顺序打印字节。第二个示例不太可取,因为在int上使用>>可能会产生未定义的行为,并且char类型不能保证为8位。 - Peter
val 是什么类型?它是如何赋值的?除非 valunsigned,否则 printf("%d", (val >> j) & 1); 会出现问题。一个 [mcve] 可以改善这篇文章。 - chux - Reinstate Monica
((int)a)>>n 的未定义行为对于小的 n,即 n < (sizeof(int) * 8),不会影响 (((int)a)>>n) & 1。 - William J Bagshaw
显示剩余2条评论
4个回答

3
欢迎来到字节序的神秘世界。
我们通常是按最高位数字优先的顺序书写数字,你可能会想象最高位字节存储在较低地址处。
但建造计算机的电气工程师更有想象力。
有时他们会将最高位字节存储在首位,但在你的平台上,它是最不重要的。
甚至存在一些平台,其中所有内容都有点混乱 - 但在实践中很少遇到这些问题。
因此,我们大部分时间都在谈论大端和小端。这是关于《格列佛游记》中的一个笑话,那里有一场无意义的争斗,争论从水煮蛋的哪一端开始。这本身就是对基督教教堂的一些争议的讽刺。但我扯远了。
因为您的第一个片段将值视为一系列字节,因此它按字节顺序遇到它们。
但是,因为 >> 被定义为操作位,所以它被实现为在不考虑实现的情况下 '逻辑' 运行。
C 没有定义字节顺序是正确的,因为不支持 C 选择的模型的硬件将负担着无休止且毫无意义的字节重排的开销。
不幸的是,并没有内置标识符告诉您模型是什么 - 虽然可以找到执行此项操作的代码。
如果(a)正如上述,您想将整数类型分解为字节并操纵它们,或者(b)您收到了包含多字节结构的其他平台的文件,则它将变得相关。
Unicode 提供了称为 BOM(字节顺序标记)的东西,用于 UTF-16 和 UTF-32。
事实上,使用 UTF-8 的一个很好的理由(其中有许多)就是问题消失了。因为每个组件都只是一个字节。
脚注:在评论中非常公正地指出,我没有讲完整个故事。
C 语言规范承认多种表示形式,特别是有符号整数。具体来说是:有符号幅值、二进制补码和反码。
它还允许 '填充位' 不表示值的一部分。
因此,原则上,我们需要考虑表示法以及处理字节顺序。
所有现代计算机使用二进制补码,而使用任何其他东西的现有机器非常罕见,除非您真的需要支持这些平台,否则我建议假设您正在使用二进制补码系统。

2
我们按照最高位数字先写。"十六"怎么样?看起来是按照最低位数字先写的?;-) - chux - Reinstate Monica
字节序并不是这个问题的全部,表示方式通常是实现定义的(可能有填充位、符号位的位置等),因此真正的问题在于确定哪个代码打印了实际的表示。 - user2371524
1
“广泛使用”,是吗?我敢打赌100美元,这位OP今天正在使用传统的2进制补码小端机器,并且在他整个编程生涯中都不会遇到其他任何东西。 - Steve Summit
除非他在他的爷爷的地下室里找到了什么东西。加拿大有PDP-11运行核反应堆。但它们是2补码!哦,如果某物正在核反应堆上工作并在Stack Overflow上发布问题,那会吓死我... - Persixty
所有这些推理都是无效的。只要标准没有强制规定任何事情,就永远不能依赖它。 - user2371524
显示剩余10条评论

0

这取决于您对“正确”的定义。

第一个将按内存中的布局精确打印数据,所以我打赌这就是您得到意外的 02000000 的原因。*) 依我看,这是正确的。它可以通过直接使用 unsigned char * 进行别名处理来更简单地完成(实际上,char 指针始终允许别名任何其他指针,事实上,访问表示是标准中提到的 char 指针的用例):

int x = 2;
unsigned char *rep = (unsigned char *)&x;
for (int i = 0; i < sizeof x; ++i) printf("0x%hhx ", rep[i]);

第二个代码段仅打印值位(value bits)**),并按照从最高有效字节到最低有效字节的顺序将它们取出。我不会称其为正确,因为它还假设字节有8位,并且负数移位的使用是实现定义的。此外,如果您真的想看到表示,仅忽略填充位似乎也不正确。

编辑:正如Gerhardh所评论的,这第二个代码段不是逐字节打印,而是逐位打印。所以,你声称看到的输出是不可能的。但它仍然遵循相同的原则,仅打印值位并从最高位开始。


*) 你正在使用“小端”机器。在这些机器上,最不重要的字节首先存储在内存中。了解更多关于维基百科上的字节序

**) C语言中类型的表示也可能有填充位。一些类型不允许包含填充(如char),但int允许有它们。这第二个选项不与char别名,因此填充位保持不可见。

***) 这段代码的正确版本(用于打印所有值位)必须a)正确确定值位数(8 * sizeof int是错误的,因为字节(char)可以有超过8位,即使CHAR_BIT * sizeof int也是错误的,因为如果存在填充位,这也会计算在内)和b)通过首先转换为unsigned来避免实现定义的移位行为。它可能看起来像这样:

#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
                  + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))

int main(void)
{
    int x = 2;

    for (unsigned mask = 1U << (IMAX_BITS((unsigned)-1) - 1); mask; mask >>= 1)
    {
        putchar((unsigned) x & mask ? '1' : '0');
    }
    puts("");
}

请参考这个答案,了解这个奇怪的宏的解释。


0

如果您使用十六进制声明整数,则正确的十六进制表示字符串为00000002。

int n = 0x00000002; //n=2

或者当你像这样将整数打印为十六进制时:

printf("%08x", n);

但是,当按顺序打印整数字节时,您还必须考虑endianess,即多字节整数的字节顺序:

在大端系统中(一些UNIX系统使用它),4个字节将按以下方式在内存中排序:

 00 00 00 02 

在小端系统中(大多数操作系统),字节将按照以下顺序在内存中排序:

 02 00 00 00

“正确的是00000002”,真的吗?我认为“0x00 0x00 0x06”也同样可以作为数字“2”的正确表示。 - user2371524
问题是关于十六进制表示的。 - SHR
1
什么是“十六进制表示”?这个问题涉及到表示,即在内存中的实际位模式。 - user2371524
就像我之前回答的那样,这可能会有所不同,小端序和大端序是相反的。 - SHR
看看我在第一条评论中的例子。我创建了一个具有22个值位和2个填充位的“int”。现在呢? - user2371524

0

第一个会按照内存中出现的顺序打印代表整数的字节。具有不同字节序的平台将打印不同的结果,因为它们以不同的方式存储整数。

第二个打印构成整数值的位,最高有效位先打印。这个结果与字节序无关。结果也不依赖于如何实现带符号 int 的 >> 运算符,因为它不查看可能受实现影响的位。

第二个更适合问题“在 C 中打印整数的实际位表示”。虽然存在许多歧义。


1
“结果也不受有符号整数的>>运算符如何实现的影响”<-你确定吗?在实践中可能是正确的,但所有标准(在6.5.7 p5中)都要说:“如果E1具有带符号类型和负值,则结果值是实现定义的。” - user2371524

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接