我目前正在学习计算机科学,并希望了解所有内容,所以我在书中注意到人们声明了一个变量int x;
,然后在声明的正下方初始化它,例如。
int x;
x = 0;
我想知道这对编译代码的内存或效率有什么好处吗?
答案 0 :(得分:7)
我目前正在学习计算机科学,并希望了解所有内容
你来对了地方!
如果这对内存或编译代码的效率有积极作用
没有。但你怎么知道呢?经验数据,兄弟!
花时间研究我的编译器中的这两个中间输出。这是一个传说:最左边的列不是很有趣,忽略它。下一列显示源文件名(example2.c
)的名称或源自源代码的可执行机器指令(0000 55
)。显示原始源的行,您会看到四个星号。它与编译时生成的相应程序集输出交错。指令mnemomics和参数显示在可执行指令的右侧。在两个示例之间来回查看,您可以看到两个示例中的说明都相同。
我使用gcc
(gcc -c -g -Wa,-ahl=example.s example.c
)创建了这些示例。
首先,“理智”初始化:
6 with_init:
7 .LFB0:
8 .file 1 "example2.c"
1:example2.c ****
2:example2.c **** int with_init()
3:example2.c **** {
9 .loc 1 3 0
10 .cfi_startproc
11 0000 55 pushq %rbp
12 .LCFI0:
13 .cfi_def_cfa_offset 16
14 .cfi_offset 6, -16
15 0001 4889E5 movq %rsp, %rbp
16 .LCFI1:
17 .cfi_def_cfa_register 6
4:example2.c **** int x = 0;
18 .loc 1 4 0
19 0004 C745FC00 movl $0, -4(%rbp)
19 000000
5:example2.c ****
6:example2.c **** return x;
20 .loc 1 6 0
21 000b 8B45FC movl -4(%rbp), %eax
7:example2.c **** }
22 .loc 1 7 0
23 000e 5D popq %rbp
24 .LCFI2:
25 .cfi_def_cfa 7, 8
26 000f C3 ret
现在你提出了更“有趣”的案例:
6 later_init:
7 .LFB0:
8 .file 1 "example.c"
1:example.c ****
2:example.c **** int later_init()
3:example.c **** {
9 .loc 1 3 0
10 .cfi_startproc
11 0000 55 pushq %rbp
12 .LCFI0:
13 .cfi_def_cfa_offset 16
14 .cfi_offset 6, -16
15 0001 4889E5 movq %rsp, %rbp
16 .LCFI1:
17 .cfi_def_cfa_register 6
4:example.c **** int x;
5:example.c ****
6:example.c **** x = 0;
18 .loc 1 6 0
19 0004 C745FC00 movl $0, -4(%rbp)
19 000000
7:example.c ****
8:example.c **** return x;
20 .loc 1 8 0
21 000b 8B45FC movl -4(%rbp), %eax
9:example.c **** }
22 .loc 1 9 0
23 000e 5D popq %rbp
24 .LCFI2:
25 .cfi_def_cfa 7, 8
26 000f C3 ret
没有区别!
编辑:之前我没有看到java
标签。在这种情况下,可以说更直接:
$ cat example.java
class SOComparison
{
public static int with_init()
{
int x = 0;
return x;
}
public static int later_init()
{
int x;
x = 0;
return x;
}
}
$ javap -c SOComparison
Compiled from "example.java"
class SOComparison {
SOComparison();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static int with_init();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: ireturn
public static int later_init();
Code:
0: iconst_0
1: istore_0
2: iload_0
3: ireturn
}
答案 1 :(得分:2)
除非你有一个非常不寻常的编译器,否则这不应该改变编译代码中的任何内容。
答案 2 :(得分:2)
除了代码可读性之外,没有任何改变:你的编译器应该知道第一个赋值发生在其他地方,并在那里执行赋值。
通常,您应该更喜欢组合初始化和赋值,除了在内部作用域中第一次赋值应该发生到声明变量的情况时的罕见情况,例如在do
/ {{1}内分配的变量循环,并在循环完成后使用:
while
答案 3 :(得分:1)
这是Java故事:
简单类:
public class MyClass {
public static void main(String... args)
{
int x = 0;
}
}
和
public class MyClass {
public static void main(String... args)
{
int x;
x = 0;
}
}
我们在两种情况下都得到相同的字节码:
因此,两者之间没有区别。作为参考,这是通过以下方式生成的:
javap -c MyClass.class
答案 4 :(得分:0)
如果您的变量在声明的范围内分配,则通常不存在性能(甚至代码)差异。现在任何现代编译器都应该能够进行这种简单的优化。 (如果不是扔掉它并得到一个体面的。)
如果将变量的地址传递给函数并在那里初始化它,它可以产生最小的差异,在定义范围内进行任何赋值之前。但是你真的需要良好的性能代码才能注意到这一点,所以你首先应该关注可读性和安全性(从不使用单位化内存),只有当你测量一个明智的时候才会回到这一点关于 初始化的性能问题。
答案 5 :(得分:0)
在汇编语言规模上完全没有。点击http://gcc.godbolt.org/