代码之家  ›  专栏  ›  技术社区  ›  Dan

经历比特移位/掩蔽的奇怪行为

  •  1
  • Dan  · 技术社区  · 12 年前

    Backstory:我正在开发一个玩具编译器,它采用一些简化的汇编类文本,并将其转换为32位指令。

    转换为32位指令的工作正常,但当我试图输出结果时,我遇到了一些问题。具体来说,我编写的用于拉出各个片段的宏似乎以我无法解释的方式破坏了值。

    以下循环是我遇到问题的地方(位操作存储在宏中,但为了更简单的调试,我扩展了它们):

    while(pc < num_insts)
    {
       printf("full: %x | op: %x | rs: %x | rt: %x | rd: %x | imm: %x\n",
          inst_mem[pc],
          ((inst_mem[pc] >> 26) & 0x0000003F),
          ((inst_mem[pc] >> 21) & 0x0000003F),
          ((inst_mem[pc] >> 16) & 0x0000003F),
          ((inst_mem[pc] >> 11) & 0x0000F800),
          (inst_mem[pc++] & 0x0000FFFF));
    }
    

    它打印出以下内容:

    full: 20450008 | op: 8 | rs: 2 | rt: 5 | rd: 0 | imm: 10
    full: 0 | op: 0 | rs: 0 | rt: 0 | rd: 0 | imm: 8
    

    这些行的正确值为:

    full: 20450008 | op: 8 | rs: 1 | rt: 4 | rd: 0 | imm: 16
    full: 20240010 | op: 8 | rs: 2 | rt: 5 | rd: 0 | imm: 8
    

    如果我把它换成更简单的

    while(pc < num_insts)
    {
       printf("full: %x", inst_mem[pc++]);
    }
    

    那么每个完整地址都会如我所期望的那样被输出。这对我来说意味着解析都在正常工作,而且我的宏在解析后从地址中提取值,并用一些不应该的东西进行劫持。我只是不确定这是什么 某物 会的。

    自从 inst_mem[] 包含 int32_t 值和所有移位都小于32,这不应该是未记录的移位行为的问题。

    如果有人能朝着正确的方向推动我,我将不胜感激。我的想法快用完了。

    2 回复  |  直到 12 年前
        1
  •  3
  •   us2012    12 年前

    函数参数的求值顺序不必是“第一个参数优先”。因此,有这样的增量是非常危险的 pc++ 当其他函数参数使用时 pc 改变 个人电脑++ 个人电脑 和增量 个人电脑 之后:

    while(pc < num_insts)
    {
       printf("full: %x | op: %x | rs: %x | rt: %x | rd: %x | imm: %x\n",
          inst_mem[pc],
          ((inst_mem[pc] >> 26) & 0x0000003F),
          ((inst_mem[pc] >> 21) & 0x0000003F),
          ((inst_mem[pc] >> 16) & 0x0000003F),
          ((inst_mem[pc] >> 11) & 0x0000F800),
          (inst_mem[pc] & 0x0000FFFF));
       pc++;
    }
    
        2
  •  1
  •   Shafik Yaghmour    12 年前

    程序的行为是 unspecified undefined 。如果我们查看 C99 draft standard 部分 6.5.2.2 函数调用 段落 10 is说( 重点矿井 ):

    这个 函数指示符、实际参数的求值顺序,以及 实际参数中的子表达式未指定 ,但有一个序列点 在实际呼叫之前。

    所以我们无法确定子表达式何时 inst_mem[pc++] & 0x0000FFFF 将相对于其他参数执行,因此我们不知道何时执行 pc 将递增。

    它是 未定义的行为 因为如果我们看看截面 6.5 表达 段落 2. 说( 重点矿井 ):

    在上一个和下一个序列点之间,对象应具有其存储值 通过表达式的评估最多修改一次。72) 此外,先验值 应仅读取以确定要存储的值 .73)

    在脚注中 73 提供了以下示例 未定义的行为 :

    i = ++i + 1;
    a[i++] = i;
    

    所以在 sequence point 如果对象被修改,则只能读取先前的值来确定要存储的值。在您的代码中 个人电脑 正在使用进行修改 pc++ 表达式,并被读取其他几次以确定数组索引,从而调用未定义的行为。

    解决方法是直接向前移动 个人电脑++ 中的 printf 呼叫