首先声明变量然后分配值或直接声明并在编译函数中指定值之间是否存在差异?编译的函数是否执行相同的工作?例如,它是否仍然读取参数,声明变量然后分配值,或者编译版本中的两个示例之间是否存在差异?
示例:
void foo(u32 value) {
u32 extvalue = NULL;
extvalue = value;
}
与
比较 void foo(u32 value) {
u32 extvalue = value;
}
我的印象是,如果你查看编译后的代码,这两个函数之间没有区别,例如它们看起来一样,我将无法分辨哪个是哪个。
答案 0 :(得分:4)
这取决于编译器和&当然是优化水平。
一个愚蠢的编译器/低优化级别,当它看到:
u32 extvalue = NULL;
extvalue = value;
可以在下一行设置为NULL
然后设置为value
。
由于extvalue
未在中间使用,NULL
初始化无用,大多数编译器直接设置为value
作为简单优化
请注意,声明变量实际上并不是指令。编译器只是分配自动内存来存储这个变量。
我测试了一个简单的代码,有或没有赋值,结果是差异
使用gcc
编译器6.2.1与-O0
(不要优化任何东西)标志时的标志:
#include <stdio.h>
void foo(int value) {
int extvalue = 0;
extvalue = value;
printf("%d",extvalue);
}
拆卸:
Disassembly of section .text:
00000000 <_foo>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 28 sub $0x28,%esp
6: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) <=== here we see the init
d: 8b 45 08 mov 0x8(%ebp),%eax
10: 89 45 f4 mov %eax,-0xc(%ebp)
13: 8b 45 f4 mov -0xc(%ebp),%eax
16: 89 44 24 04 mov %eax,0x4(%esp)
1a: c7 04 24 00 00 00 00 movl $0x0,(%esp)
21: e8 00 00 00 00 call 26 <_foo+0x26>
26: c9 leave
27: c3 ret
现在:
void foo(int value) {
int extvalue;
extvalue = value;
printf("%d",extvalue);
}
拆卸:
Disassembly of section .text:
00000000 <_foo>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 28 sub $0x28,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: 89 45 f4 mov %eax,-0xc(%ebp)
c: 8b 45 f4 mov -0xc(%ebp),%eax
f: 89 44 24 04 mov %eax,0x4(%esp)
13: c7 04 24 00 00 00 00 movl $0x0,(%esp)
1a: e8 00 00 00 00 call 1f <_foo+0x1f>
1f: c9 leave
20: c3 ret
21: 90 nop
22: 90 nop
23: 90 nop
0 init已经消失。在这种情况下,编译器没有优化初始化。
如果我切换到-O2
(良好的优化级别),那么在这两种情况下代码都是相同的,编译器发现初始化是不必要的(仍然是静默的,没有警告):
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 18 sub $0x18,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: c7 04 24 00 00 00 00 movl $0x0,(%esp)
10: 89 44 24 04 mov %eax,0x4(%esp)
14: e8 00 00 00 00 call 19 <_foo+0x19>
19: c9 leave
1a: c3 ret
答案 1 :(得分:1)
我在Godbolt尝试了这些功能:
void foo(uint32_t value)
{
uint32_t extvalue = NULL;
extvalue = value;
}
void bar(uint32_t value)
{
uint32_t extvalue = value;
}
我移植到实际类型uint32_t
而不是u32
这不是标准的。由x86-64 GCC 6.3生成的非优化组件是:
foo(unsigned int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi
mov DWORD PTR [rbp-4], 0
mov eax, DWORD PTR [rbp-20]
mov DWORD PTR [rbp-4], eax
nop
pop rbp
ret
bar(unsigned int):
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-20], edi
mov eax, DWORD PTR [rbp-20]
mov DWORD PTR [rbp-4], eax
nop
pop rbp
ret
很明显,非优化代码保留了(奇怪的,正如其他人指出的那样,因为它没有写入指针)NULL
赋值,这当然是毫无意义的。
我投票支持第二个,因为它更短(在阅读代码时更少留在一个人的头脑中),并且从不允许/推荐在用适当的值覆盖之前,将NULL
设置为无意义。我会认为这是一个错误,因为你正在说/做一些你并不意味着的事情。