小区别,大容量
大多数编程语言都存在称为“语法糖”的东西,实际上是为了代码编写方便而提供的另一种表达形式,但是这些东西一定是甜蜜的吗?最近发现了一个东西,来给大家分享一下。
为了突出重点,将不必要的部分省去,列出下面两段代码:
/* * try_1.c */ int a[50000] = {1}; int main() { return 0; }
/* * try_2.c */ int a[50000]; int main() { a[0] = 1; return 0; }
对于数组的初始化操作就是我们今天要提及的语法糖,看起来两段代码是相同的,但是实际上他们有着微妙的差别,让我们来编译一下,然后比较看看:
[daybreakcx@Fedora ~]$ gcc -o a1 try_1.c [daybreakcx@Fedora ~]$ gcc -o a2 try_2.c [daybreakcx@Fedora ~]$ ls -l a* -rwxrwxr-x 1 daybreakcx daybreakcx 207417 7月 23 19:48 a1 -rwxrwxr-x 1 daybreakcx daybreakcx 7377 7月 23 19:48 a2
注意看大小一列,看出来了吗,虽然同为全局变量,但是两个大小差了200000个字节以上,再看看我们的数组大小是50000,根据每个int是4字节来算,大小就差不多了,在此进行猜测:也许第二个程序并没有将a数组分配在二进制文件中,而第一个程序却这么干了,于是我们生成一下汇编代码来看看:
[daybreakcx@Fedora ~]$ gcc -S try_1.c [daybreakcx@Fedora ~]$ gcc -S try_2.c
先是第一个程序的:
[daybreakcx@Fedora ~]$ cat try_1.s .file "try_1.c" .globl a .data .align 32 .type a, @object .size a, 200000 a: .long 1 .zero 199996 .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp movl $0, %eax popl %ebp ret .size main, .-main .ident "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)" .section .note.GNU-stack,"",@progbits
接着是第二个:
[daybreakcx@Fedora ~]$ cat try_2.s .file "try_2.c" .comm a,200000,32 .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp movl $1, a movl $0, %eax popl %ebp ret .size main, .-main .ident "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)" .section .note.GNU-stack,"",@progbits
看到了吗,第一个程序将整个数组分配在了.data段之中,这也就是出现上述现象的原因,这是个编译器行为,本机没有windows系统,所以cl无法测试,但是对于在gcc下的童鞋发出提示:当数值个数不是很多的时候,不选用第一个程序那种初始化方式也许会更好一些,比如在嵌入式环境中(因为都是全局数组,所以其他部分都是会初始化为0的,两者并没有区别)。
那么是不是但凡这样的初始化方式都会导致这样的情况发生呢?我们是否该杜绝使用它呢?看下面这个例子:
/* * try_3.c */ int a[50000] = {0}; int main() { return 0; }
然后是编译看信息:
[daybreakcx@Fedora ~]$ gcc -o a3 try_3.c [daybreakcx@Fedora ~]$ ls -l a3 -rwxrwxr-x 1 daybreakcx daybreakcx 7377 7月 23 20:03 a3
很显然和第二个程序的大小相同,那是否与第二个程序进行了同样的处理呢?我们来看看它的汇编代码再下结论:
[daybreakcx@Fedora ~]$ gcc -S try_3.c [daybreakcx@Fedora ~]$ cat try_3.s .file "try_3.c" .globl a .bss .align 32 .type a, @object .size a, 200000 a: .zero 200000 .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp movl $0, %eax popl %ebp ret .size main, .-main .ident "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)" .section .note.GNU-stack,"",@progbits
很显然,它的处理和第一个程序相同,不同的是它分配在了.bss段,在其中是不事先分配在二进制文件之中的,也就是说根据第一个程序的定义语法,会有两种不同的结果,根据是否有非0值,可能是.data或者.bss两者之一,而分配在.data段中会产生的结果就是我们先前见到的,而第二个程序中单独定义全局数组的方式则不在二进制文件中进行分配。
结论:语法糖给我们带来了方便,很多人也很喜欢使用,但是使用的效果因人而异,而这些差异在于大家的熟练程度和认知程度,平时多反反程序,对于理解很有帮助,至于随意使用的后果嘛,没啥好说的,大家看到了什么就是什么,我就不予总结了,发完文章了,闪人…………
PS:多谢网友提醒,造成不同行为的语法的确不能称为语法糖,原文内容就不做修改了,数组初始化的语法影响了编译器行为,说它是语法糖是错误的,特在此编辑说明。
2010年7月26日 00:58
你主要讲了全局变量初始化为不为0的值的话会放在data段,不初始化或初始化为0值会放在bss段,从而程序大小不一样。
这个是程序编译链接时的问题,这个不叫语法糖吧?就像你的定义,语法糖说的是语法层面的东西,如果一个语法去掉了对整个系统没有什么影响,那么它可以称为是一个语法糖,为了方便书写而来,比如for语句就可以说是一个语法糖。
2010年7月29日 02:48
多谢指正,的确造成副作用的是不能叫做语法糖,我将文章修改了:)
2010年8月04日 07:10
哦,这事我以前遇见过,我在想所有的数组是不是都应该动态分配,好处是可以减小文件大小,坏处是容易内存泄漏,降低程序运行速度,弊大于利,还是不该的,如果非常大的话还可以