临时开通了这个blog,希望把最近学习的内容记录下来。
一道出现段错误的指针题:
#include <stdio.h>
int a;
void func(int *p)
{
p = &a;
}
int main(void)
{
int *p;
func(p);
*p = 100;
return 0;
}
gcc编译后发现段错误,我很难发现错误的原因,不过对传参第过程的确有疑问。
于是gcc -S 反汇编一下,得到:
.file "ptice.c"
.comm a,4,4
.text
.globl func
.type func, @function
func:
pushl %ebp
movl %esp, %ebp
movl $a, 8(%ebp)
popl %ebp
ret
.size func, .-func
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $20, %esp
movl -4(%ebp), %eax
movl %eax, (%esp)
call func
movl -4(%ebp), %eax
movl $100, (%eax)
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.1-4ubuntu9) 4.4.1"
.section .note.GNU-stack,"",@progbits
发现main函数在call func之前:
movl -4(%ebp), %eax
movl %eax, (%esp)
明显的错误,把ebp的下4字节的内容移动到当前esp所指向的内容,实际上做了一个*p的副本,为func函数使用;
调用func函数后:
movl $a, 8(%ebp)
于是,func函数操作的只是*p的副本,把副本里存放了a的首地址。
ret回main函数后,变量a赋值为100,main函数里的*p,是不知道的,因为,只有它的副本知道。
于是可对.c文件做如下修改:
#include <stdio.h>
int a;
void func(int **p)
{
*p = &a;
}
int main(void)
{
int *p;
func(&p);
*p = 100;
return 0;
}
即:我们传入的参数是指针p的首地址,而接收参数的是指针p的指针(*p的指针)
反观汇编:
.file "ptice.c"
.comm a,4,4
.text
.globl func
.type func, @function
func:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl $a, (%eax)
popl %ebp
ret
.size func, .-func
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $20, %esp
leal -4(%ebp), %eax
movl %eax, (%esp)
call func
movl -4(%ebp), %eax
movl $100, (%eax)
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.1-4ubuntu9) 4.4.1"
.section .note.GNU-stack,"",@progbits
我们发现只有一个地方发生了改变:
main函数在call func之前:
leal -4(%ebp), %eax
movl %eax, (%esp)
mov指令变成了lea指令,也就是说,作为副本供func函数使用的不是ebp下4个字节的内容,而是内容里所指向的地址(即mian函数*p的地址)。
我们gcc正确的程序,发现没有段错误。加一句:
printf("a = %d/n",a);
结果是预期的100.
指针的作用有了凸显。