arr[0] = arr[1] = C 中的值是不好的做法吗?
Posted
技术标签:
【中文标题】arr[0] = arr[1] = C 中的值是不好的做法吗?【英文标题】:Is bad practice do arr[0] = arr[1] = value in C? 【发布时间】:2020-08-19 07:19:37 【问题描述】:正如我上面所说,这是不好的做法吗?在 ASM 中它会是什么样子?我的意思是,我不知道它是否被翻译成这样:
arr[0] = value;
arr[1] = value;
或者是这样的:
arr[1] = value;
arr[0] = arr[1];
第二个显然效率更低(imm vs mem)。
提前致谢。
【问题讨论】:
好吧,我想,任何好的编译器都会优化它。而在 (x86) 汇编器中,它更像是“缓存与依赖链”。 编译器非常擅长优化这类事情。您不必太担心这里的性能,因为如果启用优化,编译器可能会从两者生成相同的代码。请注意,如果您对此感兴趣,大多数 C 编译器都提供了一种检查其汇编输出的方法。 只做前者。可读且高效。后者的意义何在?可读性差,效率也不高。 后者符合语言而前者不符合,尽管优化会定期绕过中间值。如果变量是全局的,那么有一个时间假设,我希望是后者。 【参考方案1】:如果您想查看 asm,可以使用
objdump -d -M intel binary_name
然后您将搜索.text
部分,然后搜索main
函数。
这是我在计算机上使用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
在第二种情况下,还有一个额外的操作。
所以经过优化的编译,没有区别,但是为了代码的可读性我不会这样写。
【讨论】:
我很惊讶这两者之间存在差异,尤其是在您开启优化的情况下。 @RussSchultz 未优化时会有所不同,我已经用优化的输出进行了更新,是一样的。 @RussSchultz:您可以从[rbp - ...]
用于本地人 (-fno-omit-frame-pointer
) 以及刚刚存储的值的重新加载中看到,这是未优化(反优化)调试模式代码。 Why does clang produce inefficient asm with -O0 (for this simple floating point sum)? 基本上适用于所有编译器。一些编译器甚至可能在调试模式下的一条语句 a=b=42
语句中进行了优化,但这个编译器没有。优化后的代码完全优化了数组,只将值具体化为 printf 的寄存器参数。
有趣的事实:mov ecx,edx
在这种情况下实际上是一个不错的选择,可以节省一些代码大小(2 字节与 5 字节 mov r32, imm32
)。但是编译器不知道printf
很慢并且在一段时间内不会到达它的第 4 个参数(ECX),而且 1 个周期的依赖无论如何都是微不足道的。【参考方案2】:
好吧,我查看了汇编代码,得到了这个:
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:存储累加器 | LDA:负载累加器)
所以,我使用的编译器优化了代码。你们已经说过,我假设更好的做法(或更好的可读方式)使用:
Arr[0] = 0x58;
Arr[1] = 0x58;
谢谢大家!
【讨论】:
如果您的常量只是一个数字文字,您可能希望通过编写Arr[0] = Arr[1] = ...
来避免重复它。否则,是的,为该值重复一些有意义的符号名称通常很好。为了良好的风格,您应该真正避免的唯一事情是将Arr[1] = Arr[0];
作为单独的语句放在单独的行上。 那么你当然应该再次使用value
。【参考方案3】:
根据 C 语法赋值运算符从右到左求值。
所以这个说法
arr[0] = arr[1] = value;
等价于
arr[0] = ( arr[1] = value );
来自 C 标准(6.5.16 赋值运算符)
3 赋值运算符将值存储在由指定的对象中 左操作数。 赋值表达式具有左边的值 赋值后的操作数,111) 但不是左值。一个类型 赋值表达式是左操作数之后的类型 左值转换。更新存储值的副作用 左操作数在左的值计算之后排序,并且 右操作数。操作数的计算是无序的。
所以根据报价你可以考虑这个说法
arr[0] = arr[1] = value;
喜欢
arr[1] = value;
arr[0] = arr[1];
至于效率,编译器可以生成与您想象的源代码不同的高效目标代码。
【讨论】:
以上是关于arr[0] = arr[1] = C 中的值是不好的做法吗?的主要内容,如果未能解决你的问题,请参考以下文章
`if (idx < arr.length)` 是不是等同于 `if (arr[idx])`?
2022-03-18:arr数组长度为n, magic数组长度为m 比如 arr = { 3, 1, 4, 5, 7 },如果完全不改变arr中的值, 那么收益就是累加和 = 3 + 1 + 4 +