函数返回局部变量/局部指针
内存四区模型
讨论这个问题之前,一定要理解堆区和栈区的工作原理,数据的存储区域(参考内存四区模型),另外一定不要返回局部对象或变量的引用和指针。
局部变量
局部变量分局部自动变量和局部静态变量,由于c返回的是值,因此返回一个局部变量是可以的,无论自动还是静态,因为这时候返回的是这个局部变量的值。另外,函数返回局部变量时实际上是返回变量值的拷贝。a为局部变量,在栈区存储,虽然在函数调用结束后所在内存会被释放回收掉,但返回值不是访问地址,而是a的拷贝副本。
1 int INTtest(){ 2 int a = 10; 3 return a; 4 }
局部指针
局部指针跟上面所述的局部变量一样。可以返回一个局部指针的值,也可以返回一个局部静态指针的地址,但不应该返回一个局部自动指针的地址,除非自动指针的地址指向数据区或堆区。
返回值为局部指针,可以分为:(1)声明局部变量,返回其地址;(2)声明局部数组,返回数组名;(3)声明局部指针,返回该指针。
1.返回局部变量地址,局部变量分为自动和静态局部变量,不应该返回指向局部自动变量的指针,因为函数调用结束后栈上声明的局部自动变量被抛弃,这个指针指向一个不再存在的对象,是无意义的。但可以返回指向局部静态变量的指针,因为静态变量存在数据区,它的生存期从定义起到程序结束。
2.返回数组名,与局部变量相同(自动和静态),调用的结果指向栈上声明的数组的首地址,函数结束后自动数组内存释放掉,将无法对其进行访问(某些编译器Release下可以访问,但理论上是没有意义的),但静态数组可以。
3.返回指针,调用结果指向该指针指向的内存,在函数结束后在栈上声明的指针也会被释放掉,但应该注意原指针指向的内存地址,若改地址同样是栈上声明的,则无法访问,如果是数据区或堆区的内容,则可以访问。
1和2比较容易理解,这里不再进行举例,仅对第3种情况的数据区和堆区进行说明。
数据区:返回指针,但指针指向的内存地址存储在数据区(常量区)
1 char * test(){ 2 char *arr = "hello world"; //字符常量 3 //static char arr[] = "hello world!"; 4 return arr; 5 } 6 7 void test_(){ 8 char *s = test(); 9 printf("s = %s\n", s); 10 } 11 12 int main(){ 13 test_(); 14 return 0; 15 }
堆区:返回指针,指针指向的内存地址存储在堆区,没有手动释放前都可以访问到。
1 char *getstring(){ 2 char *p; 3 p = malloc(100); 4 memset(p, 0, 100); 5 strcpy(p, "hello"); 6 7 return p; 8 } 9 10 void test(){ 11 char *ret = getstring(); 12 printf("%s\n", ret); 13 free(ret); 14 ret = NULL; 15 } 16 17 int main(){ 18 19 test(); 20 21 system("pause"); 22 return 0; 23 }
如果指针指向的内存是在函数内部申请的,基本不干返回指针这种事,做这种事情很有可能是在给自己找麻烦,多数做的都是在可以释放的地方申请好内存,通过指针传递进函数,申请和释放都在同一函数中。上面的函数,可以修改如下:
1 void getstring01(char *p){ 2 strcpy(p, "hello"); 3 } 4 5 void test01(){ 6 char *p = NULL; 7 p = malloc(100); 8 memset(p, 0, 100); 9 //char *ret = NULL; 10 getstring01(p); 11 printf("%s\n", p); 12 free(p); 13 p = NULL; 14 } 15 16 17 int main(){ 18 test01(); 19 return 0; 20 }