zoukankan      html  css  js  c++  java
  • 易错、经典问题:return不可返回指向栈内存的指针

    预备知识:内存的分类

    C/C++程序占用的内存分为两大类:静态存储区动态存储区。其示意图如下所示:

    数据保存在静态存储区与动态存储区的区别就是:静态存储区在编译-链接阶段已经确定了,程序运行过程中不会变化,只有当程序退出的时候,静态存储区的内存才会被系统回收。动态存储区是在程序运行过程中动态分配的。

    在其它地方我们还可以看到内存分配还有其他分类,那些都是细分的分类,比如文字常量区、全局数据区等,都归为静态存储区这一个大类。

    关于内存的分类这里只是大致说明一下,关于内存更详细的内容可查看往期笔记:

    【C语言笔记】内存笔记

    例子:return返回指向栈内存指针

    先看一个return返回指向栈内存指针的例子:

    #include <stdio.h>
    
    char *GetStr(void)
    {
    	char p[] = "Hello";	/* 保存在栈中 */
    	return p;
    }
    
    int main(void) 
    {
    	char *str = NULL;
    	str = GetStr();
    	printf("%s
    ", str);
    	return 0;
    }
    

    程序编译、运行的结果如下:

    可以看到,编译出现警告:

    warning: function returns address of local variable

    运行结果并不是我们期望的输出字符串Hello

    那是因为GetStr函数返回指向栈内存的指针,这里的变量p是局部变量,而局部变量是分配在栈上的。即Hello保存在栈内存上,栈内存在函数调用结束时会自动销毁,因此此时的p里的内容是未知的,所以结果无输出。

    下面我们把GetStr函数修改为:

    char *GetStr(void)
    {
    	char *p = "Hello";  /* p在栈上,Hello在静态区(常量区) */
    	return p;
    }
    

    此时编译运行的结果是怎样的呢?结果为:

    可以看到能正常输出。为什么这里又可以正常输出呢?因为这里的p虽然分配在栈上,但是此时的Hello是一个字符串常量,其存储在静态存储区。在调用GetStr函数结束时其也不会被销毁。

    这里可能有些人会有疑惑,同样是Hello,为什么一个在栈上,一个在静态区。

    char *p = "Hello";
    

    此处首先定义了一个指针变量p,编译器就会为指针变量开辟了栈空间。而此时并没有空间来存放Hello,所以Hello只能存储在静态区。

    char p[] = "Hello";
    

    此处首先定义一个数组p,因为未给出数组大小,所以此时数组大小未确定。然后把Hello保存在这个数组里,编译器就会为数组p开闭适当的栈空间来存储Hello

    相关笔记:【C语言笔记】char *str与char str[]的区别

    其它替代方法

    从上面的例子我们知道,若函数返回指向栈内存的指针,所得到的结果并不是我们想要的。除了上面的方法之外,这里还有如下几种解决方法:

    1、把p定义为全局变量,因为全局变量存储在静态存储区,程序结束才会释放。但是这样会导致函数是不可重入的。关于函数的重入与不可重入可查看往期笔记。

    2、在GetStr函数中使用malloc申请动态内存,但使用完一定要记得使用free进行释放,否则会导致内存泄漏。示例代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    char *GetStr(void)
    {
    	char *p = (char*)malloc(64*sizeof(char));
    	strcpy(p, "Hello");
    	return p;
    }
    
    int main(void) 
    {
    	char *str = NULL;
    	str = GetStr();
    	printf("%s
    ", str);
    	free(str);	/* 释放str指向的堆内存 */
    	return 0;
    }
    

    3、可以将变量p声明为static静态变量。但这也会导致函数是不可重入的。示例代码如下:

    char *GetStr(void)
    {
    	static char p[] = "Hello";
    	return p;
    }
    

    以上就是本次笔记分享的内容,如有错误,欢迎指出!


    我的个人博客:https://zhengnianli.github.io/
    我的微信公众号:嵌入式大杂烩

  • 相关阅读:
    Scala刮:使用Intellij IDEA写hello world
    翻译器DIY它———算在英文文本中的单词数,字符和行数
    ZOJ3605-Find the Marble(可能性DP)
    我不需要你喜欢我
    thinkphp5的auth权限认证(转自thinkphp官方文档+自己总结)
    程序员中一些老的程序员去哪了
    信息学竞赛学习资料整理
    m_Orchestrate learning system---二十九、什么情况下用数据库做配置字段,什么情况下用配置文件做配置
    按键精灵使用心得
    thinkphp5中的配置如何使用
  • 原文地址:https://www.cnblogs.com/zhengnian/p/11491110.html
Copyright © 2011-2022 走看看