易错、经典问题: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 ...
随机推荐
- webview与webApp页面交互传参
参考网址:https://blog.csdn.net/books1958/article/details/44747045 上一篇说了Android集成极光推送获取了RegistrationId推送标 ...
- 如何让C/S应用支持多端(PC、Android、iOS)同时登录?
在C/S架构中,通常是使用 UserID 作为唯一标志来标记每一个用户的,也就是说,对于一个指定的UserID,只能有一个客户端在线. 如果我们开发的系统要支持同帐号多设备同时登录的场景,即需要像微信 ...
- Unity子弹生成系统
子弹系统和粒子系统比较类似,为了创建和五花八门的子弹,例如追踪,连续继承,散弹等,需要一个拥有众多参数的子弹生成器,这里叫它Shooter好了. Shooter负责把玩各类子弹造型和参数,创建出子弹, ...
- 零基础快速入门Java的秘诀
刚开始学习Java时要端正自己的学习的目标和态度,明确学习Java基础重点要做什么. 首先第一点,Java基础的学习,基础很重要,基础好地基牢,才能爬的高! 大家要从以下4个方向努力: 代码能力:一定 ...
- JAVA用递归来判断回文串
用递归来判断回文串 思路: ①首先如果是空串,一个字符,则判断是回文串 ②若不满足①,则我们需要对字符串进行递归判断,我们会想到要用第一个字符和最后一个字符进行比较,若相同则,第二个和倒数第二个进行比 ...
- [VB.NET Tips]字符串连接
在很多应用场景下我们都需要对字符串进行拼接操作. 在每一次连接字符串时,都要在堆上分配新的内存空间,每一个分配都有一定的消耗. 较长的字符串在堆中分配,对其进行连接操作需要花费很长的时间,先连接小的字 ...
- Flask基础(05)-->路由的基本定义
# 导入Flask from flask import Flask # 创建Flask的应用程序 app = Flask(__name__) # http://127.0.0.1:5000/123或者 ...
- 【SQL server初级】数据库性能优化二:数据库表优化
数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第二部分 数据库性能优化二:数据库表优化 优化①:设计规范化表,消除数据冗余 数据库范式是确保数据库结构合理,满足各种查询 ...
- VirtualBox 安装摄像头驱动
1. 需下载与VM Virtualbox相匹配的驱动,因为我的虚拟机版本是5.2.8,所以就下载5.2版本的驱动: https://www.virtualbox.org/wiki/Download_O ...
- Windows Terminal 安装及美化
windows terminal 是今年微软Build大会上推出的一款的全新终端,用来代替cmder之类的第三方终端.具有亚克力透明.多标签.Unicode支持(中文,Emoji).自带等宽字体等这些 ...