返回指针变量的函数

本文详细探讨了在C++中函数返回局部变量的各种情况,包括返回局部变量的值、返回局部变量的指针以及返回局部变量的引用等,并分析了各种情况下的可行性和潜在问题。
原文出自http://blog.chinaunix.net/uid-27411029-id-3497902.html

  1. 原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html  
原文自http://blog.chinaunix.net/uid-15014334-id-3533931.html
  1. 一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。  
  2. 1. 返回局部变量的值  
  3.   
  4. 可以有两种情况:返回局部自动变量和局部静态变量,比如,  
  5.   
  6. int func()  
  7. {  
  8.     int temp = 0;   // 返回局部自动变量的值  
  9.     return temp;  
  10. }  
  11.   
  12. 局部变量temp存储在栈中,函数返回时会自动复制一份temp的copy给调用者,没有问题。  
  13.   
  14. int func()  
  15. {  
  16.     static int a = 1;   // 返回局部静态变量的值  
  17.     return a;  
  18. }  
  19. 局部变量a存储在静态(全局)存储区中,从初始化后一直有效直到程序结束,仅分配一次内存,并且函数返回后,变量不会销毁,没有问题。  
  20.   
  21. vector<int> func()  
  22. {  
  23.     vector<int> v;  
  24.     v.push_back(0);  
  25.     return v;  
  26. }  
  27.   
  28. 返回的是v的值拷贝,没有问题。  
  29.   
  30. Person func()  
  31. {  
  32.     Person p1;  
  33.     p1.name = "test";  
  34.     return p1;  
  35. }  
  36.   
  37. ?  
  38. 返回的也是值拷贝,会调用Person类的拷贝构造函数,没有问题。  
  39.   
  40. 2. 返回局部变量的指针  
  41.   
  42. int* func()  
  43. {  
  44.     int temp = 0;   // 返回局部变量的地址  
  45.     return &temp;  
  46. }  
  47.   
  48. ?  
  49. 前面讨论过,局部变量temp存储在栈中,函数返回时将已销毁变量的地址返回给调用者,结果将是不可预知的。  
  50.   
  51. int* func()  
  52. {  
  53.     static int temp = 1;  
  54.     return &temp;  
  55. }  
  56.   
  57. ?  
  58. 局部变量temp存储在静态存储区,返回指向静态存储区变量的指针是可行的。  
  59.   
  60. char* func()  
  61. {  
  62.     char *p = "test";  
  63.     return p;   // 返回指向常量字符串的指针  
  64. }  
  65.   
  66. ?  
  67. 对于字符串的特殊情况,由于字符串test存储在常量存储区(不是静态存储区),因此函数返回一个指向常量的字符串指针是可行的。  
  68.   
  69. char* func()  
  70. {  
  71.     char str[] = "test";  
  72.     return str; // 返回局部字符串的指针  
  73. }  
  74.   
  75. ?  
  76. 这种情况下,str被初始化为字符串局部变量,因此函数返回一个已销毁的局部变量是不可行的。解决办法就是将字符串str声明为static。  
  77.   
  78. char* func()  
  79. {  
  80.     char *str = (char *)malloc(sizeof(char) * BUFFER_SIZE);  
  81.     strcpy(str, "test");  
  82.     return str;  
  83. }  
  84.   
  85. ?  
  86. 这种情况下,函数返回一个指向堆内存的指针,由于堆存储区由程序员手动管理,因此这种做法是可行的,但是要防止出现内存泄露,函数调用完后需要手动释放内存。这里的sizeof作用于指针返回的是指针类型的长度1byte,而如果作用于数组返回的则是数组的长度。  
  87.   
  88. char *temp = NULL;  
  89. temp = func();  
  90. // some operation...  
  91. free(temp);  
  92.   
  93. ?  
  94. 3. 返回局部变量的引用  
  95.   
  96. int& func()  
  97. {  
  98.     int temp = 0;   // 返回局部变量的引用  
  99.     return temp;  
  100. }  
  101.   
  102. 由引用的概念可知,函数返回的是temp本身,而temp在函数返回后已销毁,结果将是不可预知的。  
  103.   
  104. 补充:静态全局变量和全局变量的区别  
  105.   
  106. 静态全局变量只在当前文件中可用,全局变量在其他文件中也可用,需要用extern声明。  
  107.   
  108. 全局变量和静态变量如果没有手动初始化,则默认由编译器初始化为0。  
  109.   
  110. 1:  
  111. [cpp] view plaincopy  
  112. #include <stdio.h>     
  113. char *returnStr()     
  114. {     
  115.     char *p="hello world!";     
  116.     return p;     
  117. }     
  118. int main()     
  119. {     
  120.     char *str;     
  121.     str=returnStr();     
  122.     printf("%s\n", str);     
  123.     return 0;     
  124. }    
  125. 这个没有任何问题,因为"hello world!"是一个字符串常量,存放在只读数据段,把该字符串常量存放的只读数据段的首地址赋值给了指针,所以returnStr函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。  
  126. 2:  
  127. [html] view plaincopy  
  128. #include <stdio.h>     
  129. char *returnStr()     
  130. {     
  131.     char p[]="hello world!";     
  132.     return p;     
  133. }     
  134. int main()     
  135. {     
  136.     char *str;     
  137.     str=returnStr();     
  138.     printf("%s\n", str);     
  139.     return 0;     
  140. }     
  141. "hello world!"是局部变量存放在栈中。当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以有可能打印出来的是乱码。   
  142. 3:  
  143. [html] view plaincopy  
  144. int func()    
  145. {    
  146.       int a;    
  147.       ....    
  148.       return a;    //允许    
  149. }                       
  150.     
  151. int * func()    
  152. {    
  153.       int a;    
  154.       ....    
  155.       return &a;    //无意义,不应该这样做    
  156. }     
  157. 局部变量也分局部自动变量和局部静态变量,由于a返回的是值,因此返回一个局部变量是可以的,无论自动还是静态,  
  158. 因为这时候返回的是这个局部变量的值,但不应该返回指向局部自动变量的指针,因为函数调用结束后该局部自动变量  
  159. 被抛弃,这个指针指向一个不再存在的对象,是无意义的。但可以返回指向局部静态变量的指针,因为静态变量的生存  
  160. 期从定义起到程序结束。  
  161.   
  162. 4:如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。如下:  
  163. [html] view plaincopy  
  164. #include <stdio.h>     
  165. char *returnStr()     
  166. {     
  167.     static char p[]="hello world!";     
  168.     return p;     
  169. }     
  170. int main()     
  171. {     
  172.     char *str;     
  173.      str=returnStr();     
  174.     printf("%s\n", str);     
  175.     
  176.     return 0;     
  177. }     
  178. 5: 数组是不能作为函数的返回值的,原因是编译器把数组名认为是局部变量(数组)的地址。返回一个数组一般用返回指向这个数组的指针代替,而且这个指针不能指向一个自动数组,因为函数结束后自动数组被抛弃,但可以返回一个指向静态局部数组的指针,因为静态存储期是从对象定义到程序结束的。如下:  
  179. [html] view plaincopy  
  180. int* func( void )    
  181. {    
  182.     static int a[10];    
  183.     ........    
  184.     return a;    
  185. }     
  186. 6:返回指向堆内存的指针是可以的  
  187. [html] view plaincop  
  188. char *GetMemory3(int num)    
  189. {    
  190. char *p = (char *)malloc(sizeof(char) * num);    
  191. return p;    
  192. }    
  193. void Test3(void)    
  194. {    
  195. char *str = NULL;    
  196. str = GetMemory3(100);    
  197. strcpy(str, "hello");    
  198. cout<< str << endl;    
  199. free(str);    
  200. }    
  201. 程序在运行的时候用 malloc 申请任意多少的内存,程序员自己负责在何时用 free释放内存。动态内存的生存期一直到程序员自己释放。  
一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。
1. 返回局部变量的值

可以有两种情况:返回局部自动变量和局部静态变量,比如,

int func()
{
    int temp = 0;   // 返回局部自动变量的值
    return temp;
}

局部变量temp存储在栈中,函数返回时会自动复制一份temp的copy给调用者,没有问题。

int func()
{
    static int a = 1;   // 返回局部静态变量的值
    return a;
}
局部变量a存储在静态(全局)存储区中,从初始化后一直有效直到程序结束,仅分配一次内存,并且函数返回后,变量不会销毁,没有问题。

vector<int> func()
{
    vector<int> v;
    v.push_back(0);
    return v;
}

返回的是v的值拷贝,没有问题。

Person func()
{
    Person p1;
    p1.name = "test";
    return p1;
}

?
返回的也是值拷贝,会调用Person类的拷贝构造函数,没有问题。

2. 返回局部变量的指针

int* func()
{
    int temp = 0;   // 返回局部变量的地址
    return &temp;
}

?
前面讨论过,局部变量temp存储在栈中,函数返回时将已销毁变量的地址返回给调用者,结果将是不可预知的。

int* func()
{
    static int temp = 1;
    return &temp;
}

?
局部变量temp存储在静态存储区,返回指向静态存储区变量的指针是可行的。

char* func()
{
    char *p = "test";
    return p;   // 返回指向常量字符串的指针
}

?
对于字符串的特殊情况,由于字符串test存储在常量存储区(不是静态存储区),因此函数返回一个指向常量的字符串指针是可行的。

char* func()
{
    char str[] = "test";
    return str; // 返回局部字符串的指针
}

?
这种情况下,str被初始化为字符串局部变量,因此函数返回一个已销毁的局部变量是不可行的。解决办法就是将字符串str声明为static。

char* func()
{
    char *str = (char *)malloc(sizeof(char) * BUFFER_SIZE);
    strcpy(str, "test");
    return str;
}

?
这种情况下,函数返回一个指向堆内存的指针,由于堆存储区由程序员手动管理,因此这种做法是可行的,但是要防止出现内存泄露,函数调用完后需要手动释放内存。这里的sizeof作用于指针返回的是指针类型的长度1byte,而如果作用于数组返回的则是数组的长度。

char *temp = NULL;
temp = func();
// some operation...
free(temp);

?
3. 返回局部变量的引用

int& func()
{
    int temp = 0;   // 返回局部变量的引用
    return temp;
}

由引用的概念可知,函数返回的是temp本身,而temp在函数返回后已销毁,结果将是不可预知的。

补充:静态全局变量和全局变量的区别

静态全局变量只在当前文件中可用,全局变量在其他文件中也可用,需要用extern声明。

全局变量和静态变量如果没有手动初始化,则默认由编译器初始化为0。

1:
[cpp] view plaincopy
#include <stdio.h>   
char *returnStr()   
{   
    char *p="hello world!";   
    return p;   
}   
int main()   
{   
    char *str;   
    str=returnStr();   
    printf("%s\n", str);   
    return 0;   
}  
这个没有任何问题,因为"hello world!"是一个字符串常量,存放在只读数据段,把该字符串常量存放的只读数据段的首地址赋值给了指针,所以returnStr函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。
2:
[html] view plaincopy
#include <stdio.h>   
char *returnStr()   
{   
    char p[]="hello world!";   
    return p;   
}   
int main()   
{   
    char *str;   
    str=returnStr();   
    printf("%s\n", str);   
    return 0;   
}   
"hello world!"是局部变量存放在栈中。当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以有可能打印出来的是乱码。 
3:
[html] view plaincopy
int func()  
{  
      int a;  
      ....  
      return a;    //允许  
}                     
  
int * func()  
{  
      int a;  
      ....  
      return &a;    //无意义,不应该这样做  
}   
局部变量也分局部自动变量和局部静态变量,由于a返回的是值,因此返回一个局部变量是可以的,无论自动还是静态,
因为这时候返回的是这个局部变量的值,但不应该返回指向局部自动变量的指针,因为函数调用结束后该局部自动变量
被抛弃,这个指针指向一个不再存在的对象,是无意义的。但可以返回指向局部静态变量的指针,因为静态变量的生存
期从定义起到程序结束。

4:如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。如下:
[html] view plaincopy
#include <stdio.h>   
char *returnStr()   
{   
    static char p[]="hello world!";   
    return p;   
}   
int main()   
{   
    char *str;   
     str=returnStr();   
    printf("%s\n", str);   
  
    return 0;   
}   
5: 数组是不能作为函数的返回值的,原因是编译器把数组名认为是局部变量(数组)的地址。返回一个数组一般用返回指向这个数组的指针代替,而且这个指针不能指向一个自动数组,因为函数结束后自动数组被抛弃,但可以返回一个指向静态局部数组的指针,因为静态存储期是从对象定义到程序结束的。如下:
[html] view plaincopy
int* func( void )  
{  
    static int a[10];  
    ........  
    return a;  
}   
6:返回指向堆内存的指针是可以的
[html] view plaincop
char *GetMemory3(int num)  
{  
char *p = (char *)malloc(sizeof(char) * num);  
return p;  
}  
void Test3(void)  
{  
char *str = NULL;  
str = GetMemory3(100);  
strcpy(str, "hello");  
cout<< str << endl;  
free(str);  
}  
程序在运行的时候用 malloc 申请任意多少的内存,程序员自己负责在何时用 free释放内存。动态内存的生存期一直到程序员自己释放。

  1. 可以函数的参数表中传入一个指针变量,然后局部指针指向这个参数指针,则当函数返回时,由于参数指针所指的内存空间的寿命是主函数的寿命,函数返回时不会被释放。  
可以函数的参数表中传入一个指针变量,然后局部指针指向这个参数指针,则当函数返回时,由于参数指针所指的内存空间的寿命是主函数的寿命,函数返回时不会被释放。
  1. int *fun(int *p)  
  2. {  
  3.     int *a;  
  4.     *p=4;  
  5.     a=p;  
  6.     return a;  
  7. }  
  8. int main()  
  9. {  
  10.     int a=2;  
  11.     int *p=&a;  
  12.     int *r=fun(p);  
  13.     cout<<*r;  
  14.     return 0;  
  15. }  
int *fun(int *p)
{
	int *a;
	*p=4;
	a=p;
	return a;
}
int main()
{
	int a=2;
	int *p=&a;
	int *r=fun(p);
	cout<<*r;
	return 0;
}











<think>嗯,用户想了解C语言中函数返回指针变量的用法和示例。首先,我需要回忆一下相关知识。函数返回指针变量,也就是函数返回类型是一个指针,比如int*、char*这样的类型。不过要注意,不能返回局部变量指针,因为局部变量函数结束后会被销毁,导致悬空指针。所以应该返回静态变量、动态分配的内存或者全局变量指针。接下来,我需要考虑如何组织回答结构。用户可能需要先了解基本概念,然后看到具体的示例,再了解注意事项。所以应该分步骤讲解,先说明基本语法,再给出不同类型的示例,比如返回静态变量指针、动态分配内存的指针,以及结构体指针的示例。每个示例都需要代码块,并解释关键点。然后,注意事项部分要强调不能返回局部变量的地址,动态分配后的释放问题,以及生命周期管理。这些是常见错误点,用户可能容易忽视,需要特别提醒。用户提供的引用中有函数指针的内容,但当前问题不是关于函数指针,而是函数返回指针变量。不过可能需要确认是否用户混淆了这两个概念,但根据问题描述,用户明确询问的是返回指针变量,所以应专注于这一点。 需要确保示例代码正确,比如静态变量、malloc的使用,以及结构体的例子。同时,要检查语法是否正确,比如malloc之后是否类型转换,在C语言中是否需要。实际上,在C语言中,malloc返回void*,可以隐式转换为其他指针类型,所以不需要显式转换,但最好保持正确性。 可能还需要考虑用户的实际应用场景,比如返回字符串、动态创建的结构体实例等,这些在示例中应该覆盖到。此外,错误处理,比如malloc失败的情况,但可能在示例中简化处理,专注于主要用法。最后,生成相关问题,帮助用户深入理解。比如返回指针的常见错误、动态内存管理、与函数指针的区别等。这些问题应该与主题相关,并且引导用户进一步学习。</think>### C语言函数返回指针变量的用法详解 #### 一、基本概念 当函数需要返回指针变量时,其返回值类型需声明为指针类型(如`int*`、`char*`等)。但需注意:**不能返回局部变量的地址**,因为函数执行结束后局部变量的内存会被释放,导致悬空指针[^1]。 --- #### 二、实现方式与示例 ##### 1. 返回静态变量指针 静态变量的生命周期贯穿程序运行全程,可安全返回其地址: ```c #include <stdio.h> int* create_static_array() { static int arr[3] = {1, 2, 3}; // 静态数组 return arr; } int main() { int* ptr = create_static_array(); printf("%d %d %d\n", ptr[0], ptr[1], ptr[2]); // 输出: 1 2 3 return 0; } ``` ##### 2. 返回动态分配内存指针 通过`malloc`分配堆内存,需调用者手动释放: ```c #include <stdio.h> #include <stdlib.h> char* create_string() { char* str = malloc(6 * sizeof(char)); // 动态分配内存 strcpy(str, "hello"); return str; } int main() { char* s = create_string(); printf("%s\n", s); // 输出: hello free(s); // 必须手动释放 return 0; } ``` ##### 3. 返回结构体指针 适用于创建复杂数据对象: ```c #include <stdio.h> #include <stdlib.h> typedef struct { int x; int y; } Point; Point* create_point(int a, int b) { Point* p = malloc(sizeof(Point)); p->x = a; p->y = b; return p; } int main() { Point* pt = create_point(3, 4); printf("坐标: (%d, %d)\n", pt->x, pt->y); // 输出: (3,4) free(pt); return 0; } ``` --- #### 三、注意事项 1. **悬空指针风险** 返回局部变量地址会导致未定义行为: ```c int* invalid_example() { int num = 5; // 局部变量 return &num; // 错误!函数结束后num被销毁 } ``` 2. **内存管理责任** 动态分配的内存需通过`free()`释放,否则会导致内存泄漏[^2]。 3. **生命周期控制** 全局/静态变量指针虽可安全返回,但可能引发多线程安全问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值