参考文章:
C语言的那些秘密之---函数返回局部变量_局部变量直接返回-优快云博客
先总结:
1:函数返回局部变量:
可以返回局部变量的值,但是不能返回局部变量的地址。因为返回值是复制到CPU寄存器的,局部变量在函数结束,所在的栈就被释放。若返回指针则该指针指向的空间可能无效。若返回局部变量的值,只是个值,给谁都一样,所以该值给CPU寄存器也不会涉及到被释放的栈空间。
===========================================================
2:函数返回指针:
通过返回值:指针应该指向堆上,而不是栈上。
通过形参:传入二级指针,一级指针申请堆上的空间。
需要注意的是,在调用该自定义函数后,该指针不需要用到后,需要使用free函数来释放掉堆上内存。而且申请堆内存时,应该检查malloc是否申请成功。
非要返回局部变量指针:使用静态局部变量,而非自动局部变量。因为静态局部变量的生命周期是从初始化到程序结束。
返回数组:需要返回一个局部变量数组时,需要使用静态局部变量数组,然后返回数组名(数组指针)
===========================================================
3:易混点:
char *p = "hello world"; //p是一个指针,指向一个常量区的字符串首字符。
char p[]="hello world"; //“hello world”是局部变量,存到数组p里面,该字符串可以通过p[i]来改变某个字符。
strcpy(p,"hello world"); //复制"hello world"到p所指向的空间,所以前提是p要指向一个有效的空间。
===========================================================
4:静态变量static
修饰函数:表示该函数只能在本文件中使用。
修饰全局变量:该变量只能在本文中使用。
修饰局部变量:只能在该函数中使用,且只初始化一次,下次使用直接使用上次保存下来的值。
下面的Test()输出什么内容
题目1:
void GetMemory(char *p){
p = (char *)malloc(100);
}
void Test(void){
char *str = NULL;
GetMemory(str);
strcpy(str,"hello world");
printf(str);
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:发生段错误
原因:
1.p是局部变量,GetMemory函数中它本身的操作不能传出该函数。要对它的地址进行操作。
2.str在执行完GetMemory函数后,依旧是NULL,
3.没有free函数
还有一点需要注意:
要确保
str
指向一个有效的内存区域,然后再使用strcpy
。因为strcpy是复制字符串到该指针指向的空间。这通常通过malloc
或类似的内存分配函数来完成,或者如果字符串是静态的或已知长度较小,可以使用字符数组而不是指针。
修改为正确的:
void GetMemory(char **p){
*p = (char *)malloc(100);
}
void Test(void){
char *str = NULL;
GetMemory(&str); //传入二级指针,为一级指针开辟空间
if(str != NULL){ //开辟空间成功
strcpy(str,"hello world");
printf("%s",str);
free(str); //释放空间
}
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:
自定义函数调用malloc申请堆内存 通过使用形参传递时,应该传入二级指针,对一级指针申请堆内存。
题目2:
char *GetMeory(void){
char p[]= "hello world";
return p;
}
void Test(void){
char *str = NULL;
str = GetMeory();
printf("%s",str);
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:段错误
原因:
1:GetMemory函数返回局部变量(或者函数形参)的指针。当函数结束后,该局部变量p所占空间的栈被释放。这个指针指向的内存区域可能已经被覆盖或变得不可访问,因此使用这个指针是未定义行为。
2:“hello world”是该函数的局部遍历,p数组来存它,可以通过p[i]来修改该字符串内容。
修改为正确的:
char *GetMemory(void){
char *p = (char *)malloc(13);
if(p != NULL){
strcpy(p, "hello world");
}
return p;
}
void Test(void){
char *str = NULL;
str = GetMemory();
if(str != NULL){
printf("%s",str);
free(str);
}
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:
或者改成下面也可以:
char *GetMemory(void){
//char p[]= "hello world"; //“hello world”是局部变量,在栈上
//static char p[] = "hello world"; //【这个也可以】使用局部静态变量,生命周期很长
char *p= "hello world"; //p指针指向常量区
return p;
}
void Test(void){
char *str = NULL;
str = GetMemory();
printf("%s",str);
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
题目3:
void GetMemory(char **p, int num){
*p = (char *)malloc(num);
}
void Test(void){
char *str=NULL;
GetMemory(&str, 10);
strcpy(str,"hello");
printf(str);
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:输出“hello world”和"abcd"
问题:
没有free释放堆内存
题目4:
void Test(void){
char *str= (char *)malloc(10);
strcpy(str,"hello");
free(str);
if(str != NULL){
strcpy(str,"world");
printf(str);
}
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果:段错误
原因:
1:free(str)之后,str不是NULL,依旧指向已释放内存的空间,但它的值(即内存地址)并未被自动设置为
NULL
。除非手动指定为NULL。2:strcpy时,因为没有指向一个合法的内存,所以发生段错误。
小结:
题目1:函数中给 局部变量(或者函数形参) 申请内存,这段内存需要在该函数外部使用,则需要传入高一级指针。即自定义函数调用malloc申请堆内存 通过使用形参传递时,应该传入二级指针,对一级指针申请堆内存。
题目2:不要试图返回一个局部变量的地址,应该返回堆上的指针。
题目3:记得malloc后,不使用该空间就要free掉。
题目4:free掉后,指针不会自动变成NULL,NULL需要自己手动赋值。
strcpy(str,"hello world"); 与 char *str="hello world";区别
//1
char *str = malloc(13);
strcpy(str,"hello world");
//2
char *str="hello world";
1:strcpy是把"hello world"复制到str指向的空间里
2:将str指向"hello world"的首字符。
自定义函数返回数组
int *GetMemory(void){
static int p[]= {1,2,3,4,5};
//int p[]= {1,2,3,4,5};
return p;
}
void Test(void){
int *num = NULL;
num = GetMemory();
for(int i=0; i<5; i++)
printf("%d ",*(num+i));
}
int main(int argc, char const *argv[])
{
Test();
printf("\nabcd\n");
return 0;
}
结果: