数组要么在静态区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命周期内保持不变,只有数组的内容可以改变。
指针可以随时指向其他类型的内存块,它的特征是“可变”,所以常用指针来操纵动态内存。指针远比数组灵活,但也更危险。
1.返回指向栈上的内存的指针
char * testStr1(){
char value[4] = "hello";
return value;
}
void main(){
char *str = NULL;
str = testStr1();//str的内容是垃圾
cout<<str<<endl;
}
有问题,value的内存位于栈上,该内存在函数体结束时被自动销毁。所以,return语句不可返回指向“栈内存”的“指针”或“引用”。
但为什么以下两种形式可以呢?
//1.返回int形栈上内存
int fun1()
{
int j = 3;
return j;
}
//类 Complex
//2.返回类(栈内存)
Complex fun2()
{
Complex c;
return c;
}
1)对于返回int型的栈上内存的解释:return返回值的机制为:将返回值存入eax寄存器中,然后系统再将eax中的值赋给变量(i)。
2)对于返回值是类类型的解释:返回值是对象时会调用拷贝构造函数。因为返回的局部变量在函数结束时已被销毁,所以编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数。
3)对于返回指向栈存储的“指针”的解释:
在执行return语句时,首先将return后面的地址值返回存入到比如eax寄存器中,然后系统再将eax中的地址值给接收函数返回地址的指针变量。这看起来都没什么问题,但问题在于两个方面:
[1]接收函数返回地址值的指针变量要访问此地址中的内容。
[2]子函数运行结束后,一切有关于局部变量的内存都已经释放回收。那么在用这个地址来操作就很危险:地址中没有内容
++++++++++++++++++++++++++++++++++++=
总结:
(1)return
不管是返回指针还是返回值,return将return之后的值存到eax寄存器中,回到父函数再将返回的值赋给变量。
(2)局部地址
在函数内返回一个指针会出错的原因:子函数运行完毕时,存局部变量的所有栈地址的内容已经被释放。若在父函数中再访问这些地址中的内容时,因为这些地址的内容已经被释放,所访问到的值可能是乱的、不定的。
参考:
1)http://www.cnblogs.com/wgang171412/p/5019148.htm return机制
2)https://blog.csdn.net/jmh1996/article/details/78384083 c/c++return如何实现的?return的内部机制
2.返回静态内存
char * testStr2(){
char * value = "hello";
return value;
}
void main(){
char *str = NULL;
str = testStr1();
cout<<str<<endl;
}
没问题,不会出错。只不过testStr2的设计概念是错的。因为testStr2内的字符串“hello”是常量字符串,位于静态存储区,它在程序生命周期内恒定不变,不允许改变。无论什么时候调用testStr2,它的返回值始终是同一个“只读”的内存块。
3.不能对空指针进行strcpy操作
char *testStr3(){
char *value1 ="str";
char *value2 = NULL;
strcpy(value2 , value1);
return value2;
}
有问题,value2是空指针,没有分配内存。
4.参数为指针时,形参指向静态内存或者另一地址,实参实际上无任何变化
buf依然是空的,因为参数传递相当于是char *value = buf,value指向buf
而value = "hello",相当于是value指针指向了hello所在的静态内存,但是buf的内容没变
void testStr4(char * value){
value = "hello";
}
void main(){
char buf[16]={0};
testStr4(buf);
}
5.buf最后是hello,参数传递,此时,value 和buf指向的是同一块内存,strcpy是将value1中的内容复制到value所指的内存中
返回时由于value和bufs还是指向同一块内存的,所以bufs的内容为hello.
void testStr5(char *value){
char *value1 = "hello";
strcpy(value, value1);
}
void main(){
char buf[16]={0};
testStr4(buf);
}
4和5要对比着看
6.如果函数的参数是一个指针,不要指望该指针去申请动态内存
void testStr6(char *p, int num){
p = (char* )malloc(sizeof(char) * num);
}
void main(){
char *str = NULL;
testStr6(str, 100); //str仍为NULL
strcpy(str, "hello");//运行错误
}
结合着4和3看。只是把p的内存地址变了,str丝毫未变。且每调用一次就会造成一次内存泄露,因为没有调用free释放内存。
如果非要用指针参数去申请内存,那么久应该该用“指向指针的指针”
void testStr7(char **p, int num){
*p = (char* )malloc(sizeof(char) * num);
}
void main(){
char *str = NULL;
testStr6(&str, 100); //注意参数是&str,而不是str
strcpy(str, "hello");
cout<<str<<endl;
free(str);
}
由于“指向指针的指针“这个概念不容易理解,可以用函数返回值来传递动态内存。
char * testStr8(int num){
char *p = (char* )malloc(sizeof(char) * num);
return p;
}
void main(){
char *str = NULL;
str = testStr8(100)
strcpy(str, "hello");
cout<<str<<endl;
free(str);
}
用函数返回值来传递动态内存的方法需要特别注意一点,不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡。(参考1)