预备知识:内存的分类

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\n", 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\n", str);
free(str); /* 释放str指向的堆内存 */
return 0;
}

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

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

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


我的个人博客:https://zhengnianli.github.io/

我的微信公众号:嵌入式大杂烩

易错、经典问题:return不可返回指向栈内存的指针的更多相关文章

  1. C++ —— 返回数组指针的函数 和 返回指向函数的指针的函数

    返回数组指针的函数 基础知识:数组不能被拷贝,函数不能返回数组,只能返回数组的指针或者引用. 定义一个 返回数组指针的函数 的方法,以 一个接收参数为 含有10个整型元素的数组的引用  和 返回一个含 ...

  2. C语言指针的易错点

    1.内存泄漏:申请的堆内存没有释放. 2.内存污染:前面非法操作使用内存(没有报错),后面写着写着就出错.如下代码: 当结构体中只有划线部分代码时,在编译器中编写不会报错,但此时已经造成非法操作内存, ...

  3. 你必须知道的----C语言笔试面试中经典易错的一些知识点(持续更新)

    1. 关于二级指针的解析和引用 1.1  二级指针意义  二级指针存放的是一级指针的地址    Ex: Int a = ; Int *p = &a; Int **q = &p; 1.2 ...

  4. JavaScript易错知识点整理

    前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...

  5. JavaScript 易错知识点整理

    本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES ...

  6. 细节!重点!易错点!--面试java基础篇(二)

    今天来给大家分享一下java的重点易错点第二部分,也是各位同学面试需要准备的,欢迎大家交流指正. 1.字符串创建与存储机制:当创建一个字符串时,首先会在常量池中查找是否已经有相同的字符串被定义,其判断 ...

  7. JavaScript易错点转载

    前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...

  8. JavaScript易错知识点整理[转]

    前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...

  9. forEach、map、filter、find、sort、some等易错点整理

    一.常用方法解析   说起数组操作,我们肯定第一反应就是想到forEach().map().filter()等方法,下面分别阐述一下各方法的优劣. 1.forEach 1.1 基础点   forEac ...

随机推荐

  1. charles Glist发布设置

    本文参考:charles Glist发布设置 在这里可以设置Github账户, 发布list的大小限制:等等: 在这里 Auh 就是设置Github账户, 设置登陆你的Github后,才能针对该用户进 ...

  2. 关于ClassLoader的使用

    了解ClassLoader: /* * 了解类的加载器:ClassLoader * */ @Test public void testClassLoader() { // 获取到系统类加载器,加载用户 ...

  3. [LeetCode] 由 “中缀表达式 --> 后缀表达式" 所想

    如何利用栈解决问题. Ref: 如何在程序中将中缀表达式转换为后缀表达式? 本文的引申:如何手写语法分析器 实现调度场算法 “9+(3-1)*3+10/2” --> “9 3 1-3*+ 10 ...

  4. 新手学习FFmpeg - 调用API编写实现多次淡入淡出效果的滤镜

    前面几篇文章聊了聊FFmpeg的基础知识,我也是接触FFmpeg不久,除了时间处理之外,很多高深(滤镜)操作都没接触到.在学习时间处理的时候,都是通过在ffmpeg目前提供的avfilter基础上面修 ...

  5. 3D 飞行器航迹规划

    3D-Route-Planning 三维航迹规划 -Matlab 本次代码主要解决数学建模中多约束条件下的航迹规划问题 解决的问题如下:   复杂环境下航迹快速规划是智能飞行器控制的一个重要课题.由于 ...

  6. java数据结构——红黑树(R-B Tree)

    红黑树相比平衡二叉树(AVL)是一种弱平衡树,且具有以下特性: 1.每个节点非红即黑; 2.根节点是黑的; 3.每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的; 4.如图所示,如果一个 ...

  7. elastic集群单节点停机维护

    为了elastic时时提供服务,需要elastic至少状态维持在yellow状态.所有,维护时需要依次对elastic单个节点进行维护. 操作步骤如下: 1.停止elastic的自动分配功能 curl ...

  8. JAVA知识点总结(五)(常用类)

    第十八章:常用类 一.main方法解读: public static void main(String[] args) //当点击运行时,JVM会自动调用main方法 //public: 被JVM调用 ...

  9. js初学总结

    基础 交换变量方式 //利用第三方变量进行交换 var num1 = 10; var num2 = 20; var temp; temp = num1; num1 = num2; num2 = tem ...

  10. Mycat 关键特性

    关键特性 支持SQL92标准 支持MySQL.Oracle.DB2.SQL Server.PostgreSQL等DB的常见SQL语法 遵守Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理. ...