一般我们都知道不能返回局部变量的指针,但很多人知道其然,不知道所以然,那么接下来我们分析一下,先看两段代码
/*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 类型 (初值)