在 C 中,arr[0] = arr[1] = 值是不好的做法吗?

Is bad practice do arr[0] = arr[1] = value in C?

提问人:AlbertStein 提问时间:5/5/2020 最后编辑:Vlad from MoscowAlbertStein 更新时间:5/5/2020 访问量:156

问:

正如我上面所说,这是不好的做法吗?在ASM中,它会是什么样子?我的意思是,我不知道它是否被翻译成这样的东西:

arr[0] = value;
arr[1] = value;

或者像这样:

arr[1] = value;
arr[0] = arr[1];

第二个显然效率更低(imm vs mem)。

提前致谢。

C 符 - 优先级分配 - 运算符

评论

3赞 zx485 5/5/2020
好吧,我想,任何好的编译器都会优化它。在(x86)汇编器中,它更像是一个“缓存与依赖链”的东西。
1赞 fuz 5/5/2020
编译器非常擅长优化这种事情。您不必太担心这里的性能,因为如果启用优化,编译器可能会从两者生成相同的代码。请注意,大多数 C 编译器都提供了一种检查其程序集输出的方法,以防您对此感兴趣。
2赞 Erik Eidt 5/5/2020
只做前者。可读性强,效率高。后者的意义何在?可读性较差,效率也更高。
0赞 old_timer 5/5/2020
后者符合语言,而前者则不符合,尽管优化会定期绕过中间值。如果变量是全局的,那么就有一个时间假设,我希望它能做后者。

答:

7赞 Vlad from Moscow 5/5/2020 #1

根据 C 语法赋值运算符从右到左计算。

所以这句话

arr[0] = arr[1] = value;

相当于

arr[0] = ( arr[1] = value );

来自 C 标准(6.5.16 赋值运算符)

3 赋值运算符将值存储在由 左操作数。赋值表达式的值为 left 赋值后的操作数,111),但不是左值。的类型 赋值表达式是左操作数后面的类型 左值转换。更新储值的副作用 左操作数在 left 和 右操作数。操作数的评估是未排序的。

因此,根据引文,您可以考虑此声明

arr[0] = arr[1] = value;

喜欢

arr[1] = value;
arr[0] = arr[1];

至于效率,那么编译器可以生成一个高效的目标代码,这与你想象的源代码不同。

0赞 Mickael B. 5/5/2020 #2

如果你想看asm,你可以使用

objdump -d -M intel binary_name

然后,您将搜索该部分,然后搜索函数。.textmain


这是我在计算机上使用的内容和简单易用的变量。int arr[2]printf

优化输出,编译-O3

mov    ecx,0x2a
mov    edx,0x2a

未优化

对于此代码:

arr[0] = 42;
arr[1] = 42;

输出为:

mov    DWORD PTR [rbp-0x10],0x2a
mov    DWORD PTR [rbp-0xc],0x2a

对于此代码:

arr[0] = arr[1] = 42;

输出为:

mov    DWORD PTR [rbp-0xc],0x2a
mov    eax,DWORD PTR [rbp-0xc]
mov    DWORD PTR [rbp-0x10],eax

在第二种情况下,还有一个额外的操作。

因此,对于优化的编译,没有区别,但为了代码的可读性,我不会这样写。

评论

0赞 Russ Schultz 5/5/2020
我很惊讶两者之间存在差异,尤其是在您打开了优化的情况下。
0赞 Mickael B. 5/5/2020
@RussSchultz没有优化时有区别,我已经更新了优化的输出,它是一样的。
0赞 Peter Cordes 5/5/2020
@RussSchultz:从 for locals () 的使用和刚刚存储的值的重新加载可以看出,这是未优化(反优化)的调试模式代码。为什么 clang 在 -O0 时会产生低效的 asm(对于这个简单的浮点求和)?基本上适用于所有编译器。某些编译器甚至可能在调试模式下的那个语句语句中进行了优化,但此编译器没有。优化后的代码完全优化了数组,仅将值具体化为 printf 的寄存器参数。[rbp - ...]-fno-omit-frame-pointera=b=42
0赞 Peter Cordes 5/5/2020
有趣的事实:在这种情况下,保存一些代码大小(2 字节与 5 字节)实际上是一个不错的选择。但是编译器不知道这很慢,并且在一段时间内不会到达它的第 4 个参数 (ECX),而且 1 个周期的依赖性无论如何都是微不足道的。mov ecx,edxmov r32, imm32printf
1赞 AlbertStein 5/5/2020 #3

好吧,我查看了汇编代码,得到了这个:

182             Arr[0] = 0x01;
e357:   A601 LDA #0x01
e359:   C70100 STA 0x0100
184             Arr[0] = Arr[1] = 0x58;
e35d:   A658 LDA #0x58
e35f:   C70101 STA 0x0101
e362:   C70100 STA 0x0100

因此,如果我执行 Arr[0] = 0x58,则预期的结果;并立即 Arr[1] = 0x58;是:

e357:   A601 LDA #0x58
e359:   C70100 STA 0x0100
e359:   C70100 STA 0x0101

(STA: STore 蓄能器 |LDA:LoaD 累加器)

因此,我使用的编译器优化了代码。你们已经说过了,我假设更好的实践(或更好的可读方式)使用:

Arr[0] = 0x58; 
Arr[1] = 0x58;

谢谢大家!

评论

0赞 Peter Cordes 5/5/2020
如果您的常量只是一个数字文本,您可能希望通过编写 来避免重复它。否则,是的,为该值重复一些有意义的符号名称通常没问题。为了获得良好的风格,您真正应该避免的唯一一件事是在单独的行上单独声明。那么你当然应该再次使用。Arr[0] = Arr[1] = ...Arr[1] = Arr[0];value