返回局部变量指针

一般我们都知道不能返回局部变量的指针,但很多人知道其然,不知道所以然,那么接下来我们分析一下,先看两段代码

/*test1.c*/

#include <stdio.h>

char* get_str()
{
    char str[] = {"hello"};
    return str;
}


int main()
{
    char* p = get_str();
    printf("%s/n", p);
    return 0;
}

 

====================

/*test2.c*/

#include <stdio.h>

char* get_str()
{
    char *str = {"hello"};
    return str;
}


int main()
{
    char* p = get_str();
    printf("%s/n", p);
    return 0;
}

 

test2.c运行是没有问题的,也可以打印出正确的值,而test1.c却是错误的,打印出来的值和预期的完全不一样。他们都是返回了局部变量的指针,为什么会有差异呢,我们仔细看代码,发现他们只有一个地方不一样,那就是test1.c 里面str是一个数组,test2.c里面str是一个指针。原因就在这,str指针,其值是一个常量,而常量是放在数据段里面的,即便函数返回了,数据段里面的常量数据也还会在,直到程序结束才会消失,所以我们可以打印出来。

而对于数组来说,它是一个局部变量,是放在栈里面的,函数返回之后,str指向的空间还是在的,如果我们打印出它的地址还是没有变。那么为什么内容会变掉呢,难道是系统会把栈中的数据清除,答案是否定的,因为这样做会消耗系统的资源,而且没有任何好处,下次用到这块内存还是会进行初始化的。打印出来的内容变掉是因为printf本身也是一个函数,也会进行参数的压栈,在压栈的过程中会把原来str指向的空间覆盖掉,也就改变了其中的值。如果我们在get_str之后,不调用任何函数并不创建新的局部变量(严格的说是不使栈继续往下增长),这个时候p指向的内容还是没变的。

所谓的不可以返回局部变量的引用或指针,指的是不能返回局部变量的引用或地址给引用或指针。事实上还是看该地址的位置是否在该函数的栈区,若是在栈区,函数调用结束,该地址就被释放了。尽管会出现栈地址上的值没被销毁的问题,可能是该栈区还没被其他的函数堆栈掉。

1、int  & fun()

{

int a = 10;

return a;

}      

不可以,尝试返回 a 的地址给引用变量,a是存在栈里的,函数结束调用栈被销毁。

2、int  * fun()

{

int a = 10;

return &a;

}    

不可以,尝试返回 a 的地址给指针,a是存在栈里的,函数结束调用栈被销毁。

3、int   fun()

{

int a = 10;

return a;

}    

可以,函数在返回的时候,返回处产生一个临时对象,用于存放局部变量a的值的一份拷贝(变量a的右值的拷贝)

4、int *fun()

{

int  a = 10;

int *p = &a

return p;//返回的是变量a的地址而不是指针p的地址,尽管p的地址&p也在栈上。

或:return &a;(同例2)

}

5、char *fun()

{

char *s = "1sfsdg";

return s;//返回的是1sfsdg的地址,1sfsdg存储在字符常量区,也就在静态存储区。而指针变量s的地址&s是在栈上,会随着函数的调用完被销毁。

}

函数返回处产生一个对返回对象的“左值”s的拷贝,也就是在“左值”拷贝中存储了指向字符串常量1sfsdg的地址

返回可以,因为返回的不是该函数的栈地址,而是静态存储区的地址。

6、int *fun()

{

int a[10]  = {.......};//静态数组

return a;//静态数组,在预处理阶段就在该函数的栈上分配了空间,尝试返回一个栈地址,错误

}

返回不可以。

7、int  *fun()

{

int *a = new int[10]//动态数组数组的空间在堆上,而指针变量a的地址&a在栈上,占4个字节,存储着堆    上动态数组的首地址。

return a;//变量a的左值是一个局部变量指针,存储于函数栈上,右值 是“堆”的地址(“堆”的值是数组里面存的 值),函数返回处产生一个对返回变量的“左值”a的拷贝,也就是在“左值”的拷贝中存储了指向“堆”的地 址。

}

返回可以。


       通常,变量的意义在于,它给一块内存存储区提供名字,方便程序对这块内存进行读写。变量包含两个值:左值和右值。左值是内存存储区的名字,右值是存放存储区中的值。


    使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。

     new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。

 一般使用格式:
格式1:指针变量名=new 类型标识符;
        格式2:指针变量名=new 类型标识符(初始值);
        格式3:指针变量名=new 类型标识符 [内存单元个数];

说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;但是格式2在内存分配成功后,同时将一初值存放到该内存单元中;而格式3可同时分配若干个内存单元,相当于形成一个动态数组。

请注意“内存单元个数”不必是常量表达式,即它的值不必在编译时确定,可以在运行时确定。

     一维: int *a = new int[100];    //开辟一个大小为100的整型数组空间

     二维: int **a = new int[5][6]

     三维及其以上:依此类推.

     一般用法: new 类型 (初值)
 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值