【问题标题】:Accessing a struct field in adjacent to a conditional expression访问与条件表达式相邻的结构字段
【发布时间】:2016-04-26 21:36:50
【问题描述】:

为什么接下来的 2 个代码段之间存在差异:

struct g {
    int m[100];
};


struct a {
    struct g ttt[40];
    struct g hhh[40];
}man;


extern int bar(int z);


//this code generate a call to memcopy.
void foo1(int idx){
    bar(((idx == 5) ? man.hhh[idx+7] : man.ttt[idx+7]).m[idx+3]);
}

//this code doesn't generate a call to memcopy.    
void foo2(int idx){
    bar(((idx == 5) ? man.hhh[idx+7].m[idx+3] : man.ttt[idx+7].m[idx+3]));
}

在两个代码段中,我想将相同的字段(取决于条件表达式)发送到 bar 函数。但是,第一个代码会生成对 memcopy 的调用(当使用 clang 编译到 powerpc arch 时,可以清楚地看到)。我写了一点 main 并运行了 2 个函数,它们给了我相同的输出(使用 gcc 4.4.7 编译)。

【问题讨论】:

  • 将 struct 转换为 int?
  • 请选择您使用的语言对应的语言标签
  • 在第一个sn-p中,条件表达式的结果类型为struct g。编译器使用memcpy 复制该结果。在第二个 sn-p 中,条件表达式的结果类型为 int。编译器可以使用mov 指令复制该结果。所以避免使用第一个 sn-p,反正第二个 sn-p 更容易阅读。

标签: c


【解决方案1】:

这个答案仅适用于 C - 这个问题是双重标记的,但我假设 OP 使用 C 的原因稍后会变得清楚。


这里还是第一个表达式:

((idx == 5) ? man.hhh[idx+7] : man.ttt[idx+7]).m[idx+3]

条件表达式的类型是struct g。但是,C 中条件运算符的结果是 not 左值。那是什么?

  • 在 C11 6.2.4p8 中,它被明确定义为临时生命周期的值。
  • 在 C90 中,m[idx+3] 格式不正确:m 不是左值,因为 . 运算符仅在左操作数是左值时才产生左值;并且数组指针衰减仅适用于左值。
  • 在 C99 中,数组指针衰减发生在所有值上,但没有明确说明衰减 m 点的位置。

我个人认为在 C99 中已经很清楚了,类似于 C11 的行为是有意的,所以我认为代码在 C99 中定义良好。 Further discussion here。这可能是一个有争议的问题,因为在我尝试过的所有编译器上,它们对 -std=c99 的结果与对 -std=c11 的结果相同。

然后向前推进:在 C11(可能还有 C99)中,代码段 1 应该给出正确的结果。您的编译器会这样做,但似乎它对代码的优化很差。它在索引到它之前天真地复制了条件运算符产生的整个值。

godbolt测试,发现所有版本的“x86 clang”和“PowerPC gcc 4.8”都使用memcpy;但是“x86 gcc”能够优化代码。

在 C++ 中,如果第二个和第三个操作数是相同类型的左值,则条件运算符的结果是一个左值,因此在该语言中不应该出现这个问题。


为避免此问题,请使用条件运算符的结果不是结构或联合值的替代方案。例如,您可以只使用 Snippet 2;或:

bar( ((idx == 5) ? &man.hhh[idx+7] : &man.ttt[idx+7]))->m[idx+3] );

bar( ((idx == 5) ? man.hhh : man.ttt)[idx+7].m[idx+3] );

【讨论】:

    猜你喜欢
    • 2012-10-22
    • 2015-01-05
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多