Linux c 函数返回局部变量的指针和返回局部指针变量

Linux c 函数返回局部变量的指针和返回局部指针变量

最近再看c语言实用之道这本书,对于其中的一段代码不太清楚,关于c函数返回局部指针变量与返回局部变量的指针之间的区别,对于后者还是比较清楚的,前者一直有点懵,这次查阅一些资料,算是弄清楚了,在此作个记录。

c语言函数之间的调用是通过栈帧(Stack frame)实现的,栈帧其实都是进程虚拟地址空间中栈区域分配的一块内存空间,X86架构下,栈是从进程虚拟地址空间中的高地址往低地址增长,栈的边界是通过两个寄存器EBP(栈基址)和ESP(栈地址)保存的,栈帧的销毁是通过移动ESP指针来实现的,没有真正的清理,所以当再次使用这块栈帧区域时,会遗留之前使用的垃圾值。

下面的内容引用自C函数调用栈

#include <stdio.h>

int a();
int b();
int c();

int a()
{
  b();
  c();
  return 0;
}

int b()
{ return 0; }
 
int c()
{ return 0; }

int main()
{
  a();
  return 0;
}

下图是函数调用的栈帧:

可以看到,栈帧是可以复用的,对于c()函数来说,其分配的栈帧含有之前b()函数使用的值,但对于c()函数来说就是垃圾值。

对于返回局部变量的指针或地址的c函数来说,该函数再返回后,其栈帧已被销毁,通过返回的指针去引用该销毁区域的变量是非常危险的,所以编译器会报警告。

#include <stdio.h>

int *ptr1(int n){
   return &n;
}

int *ptr2(int n){
    int val = n;
    return &val;
}

int main(void){
    int *nn = ptr1(7);
    printf("%d\n", *nn);
}

函数ptr1()和ptr2()返回了局部变量的地址,这两种情况都是不允许的。

书中对ptr1()函数进行了修改返回一个局部的指针变量,

int *ptr(int n) {
  int *p = &n; 
  return p;
}

编译不报警告,输出了数值7。 

当说一个指针的时候,其实是指其所指向的变量,也就是该指针所保存的值,同时指针也是有地址的。但这个地址和其保存的地址值是不相关的。

这里是返回了一个变量,其实是相当于返回了该变量的值,也就是该指针变量存储的地址值。函数返回一个局部变量的值是没有问题的。相当于main函数里的指针变量nn保存的是ptr函数栈帧空间里的变量n的地址,也就是说nn现在是指向一个销毁的栈帧空间里的地址,虽然该栈帧已销毁,但是并没有清理,所以输出了数值7。

紧接着,作者有添加了一个nothing()函数,

int *ptr(int n) {
    printf("ptr() &n = %p\n", &n);
    int *p = &n; 
    printf("ptr() &p = %p\n", &p);
    printf("ptr() p = %p\n", p); 
    return p;
}

void nothing(int n1) {
    printf("nothing() &n1 = %p\n", &n1);
    int n2 = n1; 
    printf("nothing() &n2 = %p\n", &n2);
}

int main(void){
    int *nn = ptr(7);
    printf("main() nn = %p\n", nn);
    printf("main() &nn = %p\n", &nn);
    nothing(10);
    printf("%d\n", *nn);
}

并在main()函数里进行了调用,输出数值10。

ptr() &n = 0x7ffcecf14d6c
ptr() &p = 0x7ffcecf14d70
ptr() p = 0x7ffcecf14d6c
main() nn = 0x7ffcecf14d6c
main() &nn = 0x7ffcecf14d90
nothing() &n1 = 0x7ffcecf14d6c
nothing() &n2 = 0x7ffcecf14d74
10

这里把变量的地址打印出来进行观察,发现nothing()函数复用了之前销毁的ptr()函数的栈帧,nothing()函数中的n1变量的地址和ptr()函数中变量n的地址相同,都是int类型,所以该地址的值被nothing()函数进行覆写, 导致输出了10。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值