变量如何初始化,才能更有效率?通过查看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里取,存在对应的内存里,每一次存储少一条指令。
本文通过对比Microsoft C/C++编译器(VC)与GCC编译器初始化字符串的不同方法,分析了它们在生成汇编代码时的效率差异。实验结果显示,直接在代码数据段定义字符串并指向寄存器的方式效率最高。此外,文章还讨论了初始化字符串长度、存储格式以及平台间汇编代码生成的区别。
5422

被折叠的 条评论
为什么被折叠?



