1

背景故事:我正在开发一个玩具编译器,它采用一些简化的类似汇编的文本并将其转换为 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,因此它不应该是未记录的班次行为的问题。

如果有人能在正确的方向上提供帮助,我将不胜感激。我的想法不多了。

4

2 回答 2

3

函数参数的求值顺序不必是“第一个参数优先”。因此,像pc++其他函数参数使用pc. 之后更改pc++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++;
}
于 2013-09-22T00:32:40.600 回答
1

程序的行为是unspecifiedundefined。如果我们查看C99 草案标准部分6.5.2.2 函数调用10段,函数参数的评估顺序是未指定的(强调我的):

函数指示符、实际参数和实际参数中的子表达式的求值顺序是未指定的,但在实际调用之前有一个序列点。

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

这是未定义的行为,因为如果我们查看6.5 表达式2段说(强调我的):

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

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

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

因此,在一个序列点内,如果一个对象被修改,则只能读取前一个值以确定要存储的值。在您的代码pc中,正在使用表达式进行修改,pc++并且正在多次读取以确定数组索引,从而调用未定义的行为。

解决方法很简单,那就是把电话pc++移出printf电话。

于 2013-09-22T01:33:00.663 回答