从汇编看待变量初始化

变量如何初始化,才能更有效率?通过查看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]='';
}

void main()
{
	funA();
	funB();
	funC();
	funD();
}


1、VC编译:vc生成汇编代码: cl /FA varInit.c

	TITLE	D:5testVarInitvarInit.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:5testVarInitvarInit.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"
	.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的整数倍长度,哪怕多写一两个,可以减少汇编指令的个数,长度11的时候,用了4*2+2+1这样来存储字符串,如果多写一个,就可以减少一条汇编指令了。没在64位机上验证,不过应该可以一条汇编指令存储8个字节的字符吧。

对比两个平台的汇编代码来看,vc编译的汇编代码注释要详细一些。但是vc平台的对应第二、三种初始化的操作,先把临时变量存在eax里,然后存在字符串变量里;而gcc中,直接从ebp里取,存在对应的内存里,每一次存储少一条指令。

原文地址:https://www.cnblogs.com/dyllove98/p/3190005.html