典型案例
Ex1
#include <iostream>
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
int main()
{
char *str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");
return 0;
}
分析:
main()
函数的str
与GetMemory()
函数的p
的生存周期不同,通过传值的方式(传的指针的值)并没有改变实参指针str
所指向地址空间的值,所以调用GetMemory()
,str
依旧是NULL
,这时使用strcpy
会造成程序崩溃.
如何修改:
(分清 "传值,传址,传引用" 的作用,这类问题迎刃而解:值传递的是一个值的副本,函数对形参的操作不影响实参的值;)采用 指向指针的指针 来解决!!! (一般实际编码又不会这样做,指针很危险,指针的指针更危险) void GetMemory(char ** pp, int num) // 指针的指针 { *pp = (char *)malloc(sizeof(char) * num); } ... int main() { char *str = NULL; GetMemory( &str, 100); // 取址!!! strcpy(str, "hello"); return 0; }
EX2
Q:下面的函数有什么问题?该如何修改
char * StrA()
{
char str[] = "hello world";
return str;
}
分析:
str
里存放的地址是函数StrA()
函数所有栈帧中"hello world"
的首地址,函数调用完后,栈帧恢复到调用strA()
之前的状态,临时空间被重置,栈帧"回缩",strA()
栈帧不再属于应该访问的范围,存在于栈帧里的hello world
当然也不应该访问。
这段程序可以正确输出结果,但是这种访问方法违背了函数的栈帧机制,这种方式有不合理性及危险性
总结:
上述的strA()
函数返回的是局部变量的地址,当调用这个函数后,这个局部变量str
就释放了,所以他的返回结果是不确定且不安全的,随时有被回收的可能
修改:
const char *strA()
{
char * str = "hello world";
return str;
}
首先区分:char str[]
与char *str
-
char str[] = "hello world"
是分配一个局部数组,是局部变量,对应分配至栈区,通过赋值操作将字符串常量赋值给局部数组,str
保存的是栈区的地址(即局部数组) -
char *str = "hello world"
字符串常量,分配在全局数据区,str
保存的是全局数据区的地址,指向全局数据区的字符串常量区域
虽然上面两个str
都是局部变量,生存周期只是在所处的函数区间,但是通过return
返回,第一个返回的地址是栈区的地址,第二个返回的地址是全局数据区的地址。需要用到的数据所处的位置不同,效果也就不一样。
此外:
字符串常量保存在只读数据段,而不是普通数据区,
如:
char * c = "hello world";
*c = 't'; // false
而
char c[] = "hello world";
c[0] = 't'; // true,局部区的数据可以修改
这里c
不占存储空间
补充另外一种修改方法:
const char * strA()
{
static char str[] = "hello world";
return str;
}
通过static
开辟一段静态存储空间。