一般来说,函数在返回返回值的时候汇编代码一般都会将待返回的值放入eax寄存器暂存,接着再调用mov指令将eax中返回值写入对应的变量中,如下代码:做简单的sum运算,
1 #include<stdio.h>
2 #include<stdlib.h>
3
4 int sum(int ivar1,int ivar2)
5 {
6 return ivar1+ivar2;
7 }
8
9 int main()
10 {
11 int res=sum(1,2);
12 }
接着我们来看对应的汇编代码
5 sum:
6 pushl %ebp
7 movl %esp, %ebp
8 movl 12(%ebp), %eax
9 movl 8(%ebp), %edx
10 leal (%edx,%eax), %eax //将函数返回值暂存在eax寄存器中
11 popl %ebp //main函数栈基地址出栈
12 ret //返回到上调用函数,并调整PC寄存器为main程序下一条指令的地址
13 .size sum, .-sum
14 .globl main
15 .type main, @function
16 main:
17 pushl %ebp
18 movl %esp, %ebp
19 subl $24, %esp
20 movl $2, 4(%esp)
21 movl $1, (%esp)
22 call sum
23 movl %eax, -4(%ebp) //sum函数返回的返回值通过eax现在赋值给变量res。
24 leave
25 ret
函数在调用结束后不会会自动清理栈上的内存,如果我们再次访问原来栈空间上的数据,那么我们读取到的数据还是可以读取到的,例如我上面sum程序的基础上加个sum_1
9 int* sum_1(int ivar1,int ivar2)
10 {
11 int tmp= ivar1+ivar2;
12 return &tmp;
13
}

那么在编译的时候,gcc报了一个这样的警告:函数返回了一个局部变量的地址,但是这种情况再运行会程序没什么问题。
我们运行程序
结果依然是对的。但是我们不推荐这么做,因为栈是一个进程调用函数的动态区域,现在是这样,以后又是怎么样的我们无从知晓。最好就是返回值,函数内部的局部变量的地址尽量少返回。不过有下面三中情况可以返回指针。
(1)函数中存储了静态局部变量,可以通过返回静态局部变量的地址,来修改静态局部变量的值。如下面函数
14 char *fun_1()
15 {
16 static char name[]="jack";
17 return name;
18 }
那么局部静态变量是存储在.data段的,不是在堆栈上的,所以函数在返回后,name数组里面的数据jack依旧存在,并且是伴随程序结束消亡的。在这里"jack"这个字符串常量是从.rodata区域复制过来到name这一块内存里面的(言外之意就是说"jack"是存储在.rodata区域)。由于Name数组是在可读可写的.data段里,所以,我们可以对这一段内存的数据进行修改。
所以在主程序里面写入
char *ptr=fun_1();
*(ptr+1)='i';
是合法的。
(2)函数返回一个const修饰的常量,也可以通过返回它地址在主函数中访问。原来在于:const修饰的常量存储在.rodata段,跟函数栈上面的数据关系,所以在也可以返回指针。
看下面
19 char *fun_2()
20 {
21 char *p="alice";
22 return p;
23 }
主函数还是
char *ptr=fun_1();
*(ptr+1)='i';

编译并没有报错。但是我们允许起来,发现。。
崩溃了!原因在于:对于.rodata区域里的”alice“进行修改。看汇编代码。
23 .section .rodata
24 .LC0:
25 .string "alice"
26 .text
49 call fun_2
50 movl %eax, 28(%esp)
51 movl 28(%esp), %eax
52 addl $1, %eax
53 movb $105, (%eax) //试图将'i'写入“alice“
使用gdb调试

报出SIGSEGV段访问错误。说明无权限访问。
再回到主题看
(3)函数返回一个指向动态内存分配块的指针。这个是最常见的了。因为我们都知道动态分配内存是malloc函数在堆上分配size个字节的内存,并返回一个(void *)类型的指针,因此我们在返回时需要强转为目标类型。由于动态内存时在堆上进行分配的因此需要程序员手动释放内存,否则会发生内存泄漏!
先下面程序题(你会对此印象深刻!)
void getMemory(char *p)
{
p=(char *)malloc(100);
}
main()
{
char *str=NULL;
getMemory(str);
strcpy(str,NULL);
printf("%s",str);
}
会打印什么?猜一猜!

哈哈,内存崩溃了!
原因在于:getMemory里面的p是保存在栈上的局部变量,是str的一个副本。函数展开后p对应的栈空间存储着已分配内存的地址,现在函数结束了,操作系统收回函数的栈空间,原来的str还是原来的NULL,现在我们将一个“hello”字符串写到0地址里面当然就会发生错误了,因为0地址这个区域是用户不可访问的。我们原意是将hello字符串写入动态分配的内存里,现在写到了0地址内存单元里面,原因在于getMemory没有返回值啊,因此正确的程序应该写成一下
5 char * getMemory(char *p)
6 {
7 p=(char*)malloc(100);
8 assert(p!=NULL);
9 return p;
10 }
11
12 int main()
13 {
14 char *ptr=NULL;
15 ptr=getMemory(ptr);
16 strcat(ptr,"hello");
17 printf("%s\n",ptr);
18 free(ptr);
19 }
印象是不是很深刻啦!因此,在函数中申请的动态内存空间对应的指针一定要返回!并在主程序中释放。