【问题标题】:C - Bitwise concatenation causing missing informationC - 按位连接导致信息丢失
【发布时间】:2018-10-01 11:52:16
【问题描述】:

我有五个短类型变量,我想将它们连接成一个 32 位 unsigned int 类型。我需要连接五个短变量。这些变量的名称称为 opcode(5 位)、reg1(4 位)、reg2(4 位)、reg3(4 位)、extension(3 位)和 addr_constant(12 位)。现在我的代码不适用于我不知道为什么的一种情况。我在下面列出了我的代码。

这段代码的目的是将某些值转换为 32 位机器指令,这意味着即使我得到了等价的值,我仍然需要一个 32 位指令。

...
unsigned int *const word;
unsigned short opcode = 1;
unsigned short reg1 = 3; 
unsigned short reg2 = 4;
unsigned short reg3 = 5;
unsigned short extension = 0;
unsigned int addr_constant = 0;

unsigned int machine_word = 0;
machine_word = machine_word | (opcode << 27);
machine_word = machine_word | (reg1 << 23);
machine_word = machine_word | (reg2 << 19);
machine_word = machine_word | (reg3 << 15);

machine_word = machine_word | (extension << 12);
machine_word = machine_word | addr_constant;

*word = machine_word
return 0;
...

二进制形式的输出应该是:

0000 1001 1010 0010 1000 0000 0000 0000.

但现在是:

1001 1010 0010 1000 0000 0000 0000. 

如您所见,它遗漏了前 4 个零。

在下面的测试中,“word”是:unsigned int *const word。在上面代码的最后,我写了“*word = machine_word”。在测试中,它比较:“word == 0x09a28000” 我没有通过以下测试。

assert(word == 0x09a28000);

【问题讨论】:

  • printf("%d\n", machine_word); 不可能打印1001 1010 0010 1000 0000 0000 0000。请显示执行打印的代码。
  • 0000 1001 1010 0010 1000 0000 0000 00001001 1010 0010 1000 0000 0000 0000. 相同,就像022 相同。
  • 测试是怎么写的?那么printf("%08x\n", machine_word) 的输出是什么?
  • “我在assert(word == 0x09a28000); 上失败了” - 可能是因为wordmachine_word 不一样????
  • 您的代码有效,请参阅here 以获取实时版本。

标签: c bit-manipulation bitwise-operators bit-shift bitwise-or


【解决方案1】:

也许问题只是结果解释。我运行了以下代码,该代码基于您提供的算法,并对逻辑操作和结果打印进行了一些修改。该操作的核心逻辑与您发布的内容保持不变,因此结果可能被误读。

代码如下:

#include <stdio.h>

int main() {
    int i, j, mask;

    unsigned short opcode = 1;
    unsigned short reg1 = 3; 
    unsigned short reg2 = 4;
    unsigned short reg3 = 5;
    unsigned short extension = 0;
    unsigned int addr_constant = 0;

    unsigned int machine_word = 0;
    machine_word |= opcode << 27;
    machine_word |= reg1 << 23;
    machine_word |= reg2 << 19;
    machine_word |= reg3 << 15;

    machine_word |= extension << 12;
    machine_word |= addr_constant;

    for (i = 7; i >= 0; i--) {
        for (j = 3; j >= 0; j--){
            printf("%d", (machine_word & 0x00000001 << (4 * i + j)) >> (4 * i + j));
        }
        printf(" ");
    }
    printf("\n");
    return 0;
}

代码给出以下输出:

0000 1001 1010 0010 1000 0000 0000 0000

这应该是您正在搜索的结果,它对应于161644544 (0x9A28000) 的无符号整数值。

【讨论】:

  • 也许有一种更好的方法可以在控制台上打印二进制值,而无需使用屏蔽和循环。不幸的是,我不知道该怎么做
【解决方案2】:

只需使用位域——它们就是为此而设计的。

struct all_the_things {
  unsigned opcode : 5;
  unsigned reg1 : 4;
  unsigned reg2 : 4;
  unsigned reg3 : 4;
  unsigned extension : 3;
  unsigned addr_constant : 12;
};

一旦你填充了这样一个结构,你可以像这样将它转换成一个 32 位整数:

uint32_t num;
memcpy(&num, &things, 4);

(别担心,优化编译器不会真正调用函数来复制 4 个字节。)

【讨论】:

  • 很遗憾,我的老师不允许我们在讲座之外使用任何东西。所以,我只能使用位移而不是位域
  • (0) 此答案不提供单个数字、数字或输出字符串中的连接位,因此不满足“二进制形式的输出应为 0000 1001 1010 0010”的规范1000 0000 0000 0000。” (1) 这个答案引入了与实现相关的位域排序问题以及用于问题中不存在的位域的存储单元。
  • @EricPostpischil:我在答案中添加了一些内容,以展示如何将其转换为uint32_t。依赖于实现的部分在技术上是正确的,但众所周知,这样的代码可以在我们都知道和喜爱的编译器上运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 2018-07-15
  • 2014-06-10
  • 2014-08-23
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多