林锐的《高质量C/C++程序设计指南》有这样一个例子:
void GetMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char)*num);
}
int main()
{
char *str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");
}
书中也说这样用是完全错误的,但是错误的原因书中解释的并不是很容易让人理解
为了便于大家理解我们可以用gcc这个工具来查看汇编代码
以下是GetMemory函数:
.file "neicun.c"
.text
.align 2
.globl GetMemory
.type GetMemory,@function
GetMemory:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
subl $12, %esp
pushl 12(%ebp)
call malloc
addl $16, %esp
movl %eax, 8(%ebp) //虽然将申请的内存空间的地址存放在了(%ebp+8)
//的位置但函数返到main函数后,刚才的函数工作栈的空间是
//无法访问的
//换句话说也就是GetMemory()没有和main函数中的str也就是
//(%ebp-4)这个局部的指针变量建立任何联系
以下是main函数:
leave
ret
.Lfe1:
.size GetMemory,.Lfe1-GetMemory
.section .rodata
.LC0:
.string "hello"
.text
.align 2
.globl main
.type main,@function
main:
pushl %ebp //将%ebp指针压入堆栈,保存原始%ebp
movl %esp, %ebp //保存原%esp的值
subl $8, %esp //为局部变量分配空间
andl $-16, %esp //16字节对齐
movl $0, %eax //%eax清0
subl %eax, %esp
movl $0, -4(%ebp) //main中的栈 GetMemory中的栈
subl $8, %esp
pushl $100 //采用堆栈来传递参数,将100压入堆栈
pushl -4(%ebp) //将局部变量(%ebp-4)的值也就是str的值(str初始化为NULL)压入堆栈
call GetMemory //调用GetMemory
addl $16, %esp //-------------------->注意这里很重要 通过堆栈指针上指的方式来清空堆栈
//堆栈指针以下的内存空间是无法访问的
subl $8, %esp //由于将两个参数压入堆栈所以堆栈指针共向上指了8个字节
pushl $.LC0
pushl -4(%ebp) //将str也就是(%ebp-4)它的值是0传递给strcpy()函数
call strcpy //strcpy()中试图将字符串"hello"写入0x0然而这是非法的
//所以运行是会报错非法写入内存
addl $16, %esp
leave
ret
.Lfe2:
.size main,.Lfe2-main
.ident "GCC: (GNU) 3.2 20020903 (Red Hat Linux 8.0 3.2-7)"
以下为内存中的堆栈模型:
请继续关注:
指针传递内存深入探讨(二)
指针传递内存深入探讨(三)