变量如何初始化,才能更有效率?通过查看vc、gcc编译器的反汇编代码查看不同方法初始化的效率区别。其中cl的版本分别是Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86,gcc的版本是4.5.3。
源代码:
void funA() { char *str1="helloworld"; } void funB() { char str2[]="helloworld"; } void funC() { char str3[11]="helloworld"; } void funD() { char str4[11]; str4[0]='h'; str4[1]='e'; str4[2]='l'; str4[3]='l'; str4[4]='o'; str4[5]='w'; str4[6]='o'; str4[7]='r'; str4[8]='l'; str4[9]='d'; str4[10]='\0'; } void main() { funA(); funB(); funC(); funD(); }
1、VC编译:vc生成汇编代码: cl /FA varInit.c
TITLE D:\5test\VarInit\varInit.c .386P include listing.inc if @Version gt 510 .model FLAT else _TEXT SEGMENT PARA USE32 PUBLIC 'CODE' _TEXT ENDS _DATA SEGMENT DWORD USE32 PUBLIC 'DATA' _DATA ENDS CONST SEGMENT DWORD USE32 PUBLIC 'CONST' CONST ENDS _BSS SEGMENT DWORD USE32 PUBLIC 'BSS' _BSS ENDS _TLS SEGMENT DWORD USE32 PUBLIC 'TLS' _TLS ENDS FLAT GROUP _DATA, CONST, _BSS ASSUME CS: FLAT, DS: FLAT, SS: FLAT endif PUBLIC _funA _DATA SEGMENT $SG32 DB 'helloworld', 00H _DATA ENDS _TEXT SEGMENT _str1$ = -4 _funA PROC NEAR ; File D:\5test\VarInit\varInit.c ; Line 2 push ebp mov ebp, esp push ecx ; Line 3 mov DWORD PTR _str1$[ebp], OFFSET FLAT:$SG32 ; Line 4 mov esp, ebp pop ebp ret 0 _funA ENDP _TEXT ENDS PUBLIC _funB _DATA SEGMENT ORG $+1 $SG36 DB 'helloworld', 00H _DATA ENDS _TEXT SEGMENT _str2$ = -12 _funB PROC NEAR ; Line 7 push ebp mov ebp, esp sub esp, 12 ; 0000000cH ; Line 8 mov eax, DWORD PTR $SG36 mov DWORD PTR _str2$[ebp], eax mov ecx, DWORD PTR $SG36+4 mov DWORD PTR _str2$[ebp+4], ecx mov dx, WORD PTR $SG36+8 mov WORD PTR _str2$[ebp+8], dx mov al, BYTE PTR $SG36+10 mov BYTE PTR _str2$[ebp+10], al ; Line 9 mov esp, ebp pop ebp ret 0 _funB ENDP _TEXT ENDS PUBLIC _funC _DATA SEGMENT ORG $+1 $SG40 DB 'helloworld', 00H _DATA ENDS _TEXT SEGMENT _str3$ = -12 _funC PROC NEAR ; Line 12 push ebp mov ebp, esp sub esp, 12 ; 0000000cH ; Line 13 mov eax, DWORD PTR $SG40 mov DWORD PTR _str3$[ebp], eax mov ecx, DWORD PTR $SG40+4 mov DWORD PTR _str3$[ebp+4], ecx mov dx, WORD PTR $SG40+8 mov WORD PTR _str3$[ebp+8], dx mov al, BYTE PTR $SG40+10 mov BYTE PTR _str3$[ebp+10], al ; Line 14 mov esp, ebp pop ebp ret 0 _funC ENDP _TEXT ENDS PUBLIC _funD _TEXT SEGMENT _str4$ = -12 _funD PROC NEAR ; Line 17 push ebp mov ebp, esp sub esp, 12 ; 0000000cH ; Line 19 mov BYTE PTR _str4$[ebp], 104 ; 00000068H ; Line 20 mov BYTE PTR _str4$[ebp+1], 101 ; 00000065H ; Line 21 mov BYTE PTR _str4$[ebp+2], 108 ; 0000006cH ; Line 22 mov BYTE PTR _str4$[ebp+3], 108 ; 0000006cH ; Line 23 mov BYTE PTR _str4$[ebp+4], 111 ; 0000006fH ; Line 24 mov BYTE PTR _str4$[ebp+5], 119 ; 00000077H ; Line 25 mov BYTE PTR _str4$[ebp+6], 111 ; 0000006fH ; Line 26 mov BYTE PTR _str4$[ebp+7], 114 ; 00000072H ; Line 27 mov BYTE PTR _str4$[ebp+8], 108 ; 0000006cH ; Line 28 mov BYTE PTR _str4$[ebp+9], 100 ; 00000064H ; Line 29 mov BYTE PTR _str4$[ebp+10], 0 ; Line 30 mov esp, ebp pop ebp ret 0 _funD ENDP _TEXT ENDS PUBLIC _main _TEXT SEGMENT _main PROC NEAR ; Line 33 push ebp mov ebp, esp ; Line 34 call _funA ; Line 35 call _funB ; Line 36 call _funC ; Line 37 call _funD ; Line 38 pop ebp ret 0 _main ENDP _TEXT ENDS END
2、cygwin环境内,GCC编译生成汇编代码: gcc -S varInit.c
.file "varInit.c" .section .rdata,"dr" LC0: .ascii "helloworld\0" .text .globl _funA .def _funA; .scl 2; .type 32; .endef _funA: pushl %ebp movl %esp, %ebp subl $16, %esp movl $LC0, -4(%ebp) leave ret .globl _funB .def _funB; .scl 2; .type 32; .endef _funB: pushl %ebp movl %esp, %ebp subl $16, %esp movl $1819043176, -11(%ebp) movl $1919907695, -7(%ebp) movw $25708, -3(%ebp) movb $0, -1(%ebp) leave ret .globl _funC .def _funC; .scl 2; .type 32; .endef _funC: pushl %ebp movl %esp, %ebp subl $16, %esp movl $1819043176, -11(%ebp) movl $1919907695, -7(%ebp) movw $25708, -3(%ebp) movb $0, -1(%ebp) leave ret .globl _funD .def _funD; .scl 2; .type 32; .endef _funD: pushl %ebp movl %esp, %ebp subl $16, %esp movb $104, -11(%ebp) movb $101, -10(%ebp) movb $108, -9(%ebp) movb $108, -8(%ebp) movb $111, -7(%ebp) movb $119, -6(%ebp) movb $111, -5(%ebp) movb $114, -4(%ebp) movb $108, -3(%ebp) movb $100, -2(%ebp) movb $0, -1(%ebp) leave ret .def ___main; .scl 2; .type 32; .endef .globl _main .def _main; .scl 2; .type 32; .endef _main: pushl %ebp movl %esp, %ebp andl $-16, %esp call ___main call _funA call _funB call _funC call _funD movl %ebp, %esp popl %ebp ret
总体来看,第一种方式定义效率是最高的,因为字符串直接定义在代码的数据段里,然后指向寄存器就行了;第二、三种说明不论是否定义数组大小,执行效率是一样的,所以如果字符串直接初始化,还是不定义大小的好,免得数错长度;第四种可以看到,在只定义局部变量的时候,如果不初始化,两种平台根本都不会调用汇编代码来干些什么。所以,从效率角度考虑,能不初始化就不初始化,如果要初始化,尽量在定义的时候初始化。字符串存储的时候,尽量使用4的整数倍长度,哪怕多写一两个\0,可以减少汇编指令的个数,长度11的时候,用了4*2+2+1这样来存储字符串,如果多写一个\0,就可以减少一条汇编指令了。没在64位机上验证,不过应该可以一条汇编指令存储8个字节的字符吧。
对比两个平台的汇编代码来看,vc编译的汇编代码注释要详细一些。但是vc平台的对应第二、三种初始化的操作,先把临时变量存在eax里,然后存在字符串变量里;而gcc中,直接从ebp里取,存在对应的内存里,每一次存储少一条指令。