一段简单的C代码引起的思考-局部字符串变量传参

本文由 @lonelyrains 出品,转载请注明出处。 
文章链接: http://blog.csdn.net/lonelyrains/article/details/12491761


#include <stdio.h>
#include <string.h>

const char * GetHello()
{
	char *rtn = "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn";
	return rtn;
}

const char * GetHello1()
{
	char rtn[0x500];
	strcpy(rtn,"hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn");
	return rtn;
}

int main()
{
	printf("%s\n%s\n",GetHello(),GetHello1())<span style="font-family:Arial,Helvetica,sans-serif">;</span>
	return 0;
}

分别用cygwin-gcc和windows-cl编译,生成汇编代码。

cygwin-gcc: 

版本截图:

一段简单的C代码引起的思考-局部字符串变量传参_第1张图片

生成汇编代码:

生成对应的汇编代码:

	.file	"funcParamTest.c"
	.section .rdata,"dr"
	.align 4
LC0:
	.ascii "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn\0"
	.text
.globl _GetHello
	.def	_GetHello;	.scl	2;	.type	32;	.endef
_GetHello:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$16, %esp
	movl	$LC0, -4(%ebp)
	movl	-4(%ebp), %eax
	leave
	ret
.globl _GetHello1
	.def	_GetHello1;	.scl	2;	.type	32;	.endef
_GetHello1:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$1304, %esp
	movl	$187, 8(%esp)
	movl	$LC0, 4(%esp)
	leal	-1288(%ebp), %eax
	movl	%eax, (%esp)
	call	_memcpy
	leal	-1288(%ebp), %eax
	leave
	ret
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC1:
	.ascii "%s\12%s\12\0"
	.text
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	pushl	%ebx
	subl	$28, %esp
	call	___main
	call	_GetHello1
	movl	%eax, %ebx
	call	_GetHello
	movl	%ebx, 8(%esp)
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	movl	$0, %eax
	addl	$28, %esp
	popl	%ebx
	movl	%ebp, %esp
	popl	%ebp
	ret
	.def	_memcpy;	.scl	2;	.type	32;	.endef
	.def	_printf;	.scl	2;	.type	32;	.endef

生成执行文件:


运行执行文件:


windows-cl:版本截图:生成汇编代码同时生成链接文件和执行文件:  (几个操作使用一条命令即完成)

一段简单的C代码引起的思考-局部字符串变量传参_第2张图片

生成对应的汇编代码:

	TITLE	funcParamTest.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	_GetHello
_DATA	SEGMENT
$SG601	DB	'hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjf'
	DB	'laksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkds'
	DB	'ajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoi'
	DB	'fnosdifn', 00H
_DATA	ENDS
_TEXT	SEGMENT
_rtn$ = -4
_GetHello PROC NEAR

; 5    : {

	push	ebp
	mov	ebp, esp
	push	ecx

; 6    : 	char *rtn = "hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn";

	mov	DWORD PTR _rtn$[ebp], OFFSET FLAT:$SG601

; 7    : 	return rtn;

	mov	eax, DWORD PTR _rtn$[ebp]

; 8    : }

	mov	esp, ebp
	pop	ebp
	ret	0
_GetHello ENDP
_TEXT	ENDS
PUBLIC	_GetHello1
EXTRN	_strcpy:NEAR
_DATA	SEGMENT
	ORG $+1
$SG605	DB	'hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjf'
	DB	'laksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkds'
	DB	'ajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoi'
	DB	'fnosdifn', 00H
_DATA	ENDS
_TEXT	SEGMENT
_rtn$ = -1280
_GetHello1 PROC NEAR

; 11   : {

	push	ebp
	mov	ebp, esp
	sub	esp, 1280				; 00000500H

; 12   : 	char rtn[0x500];
; 13   : 	strcpy(rtn,"hellodjfldsjfsadlfkja;sldjflaskdjfalksdjfladjflkjsaldkjflaksdfljalsdkjfl;asjdfsadfasdlkjfaldskjf;lajdsflkja;sldjflkdsajflaksjdf;lkjdsafjiowekmfoisdjfwokneofosdijfowenfosdifjnweoifnosdifn");

	push	OFFSET FLAT:$SG605
	lea	eax, DWORD PTR _rtn$[ebp]
	push	eax
	call	_strcpy
	add	esp, 8

; 14   : 	return rtn;

	lea	eax, DWORD PTR _rtn$[ebp]

; 15   : }

	mov	esp, ebp
	pop	ebp
	ret	0
_GetHello1 ENDP
_TEXT	ENDS
PUBLIC	_main
EXTRN	_printf:NEAR
_DATA	SEGMENT
	ORG $+1
$SG608	DB	'%s', 0aH, '%s', 0aH, 00H
_DATA	ENDS
_TEXT	SEGMENT
_main	PROC NEAR

; 20   : {

	push	ebp
	mov	ebp, esp

; 21   : 	printf("%s\n%s\n",GetHello(),GetHello1());

	call	_GetHello1
	push	eax
	call	_GetHello
	push	eax
	push	OFFSET FLAT:$SG608
	call	_printf
	add	esp, 12					; 0000000cH

; 22   : 	return 0;

	xor	eax, eax

; 23   : }

	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
END


运行执行文件:

一段简单的C代码引起的思考-局部字符串变量传参_第3张图片



分析:

1、之所以定义这么长的字符串,是担心编译器会允许几个字符的越界操作,而意外正常执行。

2、gcc在编译时会对GetHello1产生警告,执行时只打印第一个函数的结果。对比发现,直接定义字符串比先定义再拷贝字符串的操作多了lea操作和_memcpy调用。可能是lea操作导致函数传参时被检测出来是局部变量。而不包含lea操作时,直接引用ascii的地址传出。

3、cl默认不会产生警告,汇编内嵌_strcpy调用,而不是_memcpy。同样调用了lea,但是没有检测出局部变量的传参,两个函数的打印居然都成功了。分别试验了反复100次调用打印、在main和打印之间增加新一层调用函数test、在test内定义const char *test = GetHello1();free(test);在windows-cl仍能‘正常’打印所有的字符串。跟不同的编译器内存调度有关吧。可能cl的运行时在这些条件下都不清空其它函数的局部变量临时内存。而gcc会马上清空。

你可能感兴趣的:(一段简单的C代码引起的思考-局部字符串变量传参)