您现在的位置是:首页 > 正文

【C++】易错、经典问题:return不可返回指向栈内存的指针

2024-01-30 22:17:19阅读 0

【C++】易错、经典问题:return不可返回指向栈内存的指针

简述

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

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

例子: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。

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

  • 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;
}

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

网站文章

  • 点分治及动态点分治学习笔记

    点分治及动态点分治学习笔记

    前置知识:点分治 点分治,是处理树上路径的一个极好的方法。 如果你需要大规模地处理一些树上路径的问题是,点分治是一个不错的选择。 具体思路 P3806 【模板】点分治 给定一棵有 nnn 个点的树,询...

    2024-01-30 22:16:48
  • SAP 的MPN功能

    SAP 的MPN功能

    我们采买的物料可能直接来源于制造商,也有可能是供应商先去找多个制造商买了物料,供应商作为贸易商再卖给我们。非库存管理的MPN功能制造商料号仅存在基本视图和采购视图,只在采购信息记录维护/采购申请采购订...

    2024-01-30 22:16:40
  • 【ESP32】16.RFID门禁系统实验(SPI总线 / MFRC522库)

    【ESP32】16.RFID门禁系统实验(SPI总线 / MFRC522库)

    RFID(无线射频识别(radio frequency identification devices))

    2024-01-30 22:16:33
  • Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

    Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

    第一章 Thread导论为何要用Thread ?非阻塞I/O     I/O多路技术     轮询(polling)     信号警告(Alarm)和定时器(Timer)独立的任务(Task)并行算法第二章 Thread的创建与管理一、什么是Thread ?    Thread是所在主机执行的应用程序任务(task)。

    2024-01-30 22:16:03
  • 用 git 钩子,检测代码规范性(eslint、standard),让代码更规范

    最终实现效果说明: 用 git commit 提交代码之前,利用 pre-commit git 钩子,实现代码规范检测(eslint、standard 规范),符合规范之后才可以提交到 git 仓库。...

    2024-01-30 22:15:54
  • 七层协议和五层协议

    七层协议和五层协议

    TCP/IP:数据链路层:ARP,RARP网络层: IP,ICMP,IGMP传输层:TCP ,UDP,UGP应用层:Telnet,FTP,SMTP,SNMP.OSI:物理层:EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS数据链路层:Fr

    2024-01-30 22:15:48
  • 凭借着这份Spring面试题,我拿到了阿里,字节跳动美团的offer!

    凭借着这份Spring面试题,我拿到了阿里,字节跳动美团的offer!

    前言Spring作为现在最流行Java开发技术,其内部源码设计非常优秀。Spring这个词对于Java开发者想必不会陌生,可能你每天都在使用Spring,享受着Spring生态提供的服务。现在很多互联...

    2024-01-30 22:15:19
  • arthas工具 windows的安装及trace命令的使用

    arthas工具 windows的安装及trace命令的使用

    定位接口请求超时:atchas工具 windows环境的安装以及trace命令的使用

    2024-01-30 22:15:13
  • 【POSTMAN】导入excel操作(MultipartFile)

    【POSTMAN】导入excel操作(MultipartFile)

    示例图: 1、把你后台代码接收的参数写进来 2、修改默认text 修改为File 3、点击Value就会弹窗,然后找到你的excel表格。 4、请求测就可以了

    2024-01-30 22:15:06
  • Lua 环境搭建

    Lua 环境搭建

    Lua 环境配置(Mac)

    2024-01-30 22:14:58