易错、经典问题:return不可返回指向栈内存的指针
预备知识:内存的分类
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不可返回指向栈内存的指针的更多相关文章
- C++ —— 返回数组指针的函数 和 返回指向函数的指针的函数
返回数组指针的函数 基础知识:数组不能被拷贝,函数不能返回数组,只能返回数组的指针或者引用. 定义一个 返回数组指针的函数 的方法,以 一个接收参数为 含有10个整型元素的数组的引用 和 返回一个含 ...
- C语言指针的易错点
1.内存泄漏:申请的堆内存没有释放. 2.内存污染:前面非法操作使用内存(没有报错),后面写着写着就出错.如下代码: 当结构体中只有划线部分代码时,在编译器中编写不会报错,但此时已经造成非法操作内存, ...
- 你必须知道的----C语言笔试面试中经典易错的一些知识点(持续更新)
1. 关于二级指针的解析和引用 1.1 二级指针意义 二级指针存放的是一级指针的地址 Ex: Int a = ; Int *p = &a; Int **q = &p; 1.2 ...
- JavaScript易错知识点整理
前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...
- JavaScript 易错知识点整理
本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES ...
- 细节!重点!易错点!--面试java基础篇(二)
今天来给大家分享一下java的重点易错点第二部分,也是各位同学面试需要准备的,欢迎大家交流指正. 1.字符串创建与存储机制:当创建一个字符串时,首先会在常量池中查找是否已经有相同的字符串被定义,其判断 ...
- JavaScript易错点转载
前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...
- JavaScript易错知识点整理[转]
前言 本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一 ...
- forEach、map、filter、find、sort、some等易错点整理
一.常用方法解析 说起数组操作,我们肯定第一反应就是想到forEach().map().filter()等方法,下面分别阐述一下各方法的优劣. 1.forEach 1.1 基础点 forEac ...
随机推荐
- Spring boot 集成 Druid 数据源
Druid是阿里开源的一个JDBC应用组件,其中包括三部分: DruidDriver:代理Driver,能够提供基于Filter-Chain模式的插件体系. DruidDataSource:高效可管理 ...
- C# 表达式树Lambda扩展(四)
一.前言 本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧. 看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知 ...
- 【第十篇】easyui-datagrid排序 (转)
本文体验datagrid的排序. □ 思路 当点击datagrid的标题,视图传递给Controller的Form Data类似这样:page=1&rows=10&sort=Custo ...
- NOIP2006 1.明明的随机数
题目:明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不 ...
- Linux 笔记 - 第六章 Linux 磁盘管理
博客地址:http://www.moonxy.com 一.前言 1.1 硬盘 硬盘一般分为 IDE 硬盘.SCSI 硬盘和 SATA 硬盘.在 Linux 中,IDE 接口的设备被称为 hd,SCSI ...
- 无法解析的外部符号,该符号在xxx函数中被引用
无法解析的外部符号.........,该符号在函数.........被引用 在我们敲代码的过程中,我们偶尔会遇到这个问题,这个问题大多数都是因为你自己的程序有问题,而不是缺少相应的库文件.话不多说,直 ...
- vue把链接转二维码
使用qrcodejs2插件 1. 安装qrcodejs2:npm install qrcodejs2 --save 2. 在组件里面引入: import QRCode from 'qrcodejs2' ...
- filebeat使用multiline丢失数据问题
最近部署filebeat采集日志. 发现配置multiline后,日志偶尔会丢失数据,而且采集到的数据长度都不相同,所以和日志长度没有关系. 查阅filebeat官网后,找到了问题.filebeat有 ...
- Cocos Creator实现左右跳游戏
1. 玩法说明 游戏开始后,点击屏幕左右两侧,机器人朝左上方或右上方跳一步,如果下一步有石块,成功得1分,否则游戏结束. 2. 模块介绍 游戏场景分为2个:主页场景(home).游戏场景(game) ...
- SpringBootSecurity学习(03)网页版登录添加自定义登录页面
自定义登录页面 前面无论是使用默认配置,还是自定义配置类,都是使用的springboot-security自带的登录页面,自带的登录页面在这个版本虽然设计的非常不错,但是在实际开发中,我们通常还是使用 ...