当前位置: 首页 > news >正文

C/C++ 中的函数返回局部变量以及局部变量的地址?

        C/C++中,函数内部的一切变量(函数内部局部变量,形参)都是在其被调用时才被分配内存单元。形参和函数内部的局部变量的生命期和作用域都是在函数内部(static变量的生命期除外)。子函数运行结束时,所有局部变量的内存单元会被系统释放。在C中,函数被调用时的传参方式有两种形式:传值传址
        传址的好处:
(1) 能在函数内部通过实参地址间接地改变实参的值。
(2) 当所传实参内容比较庞大时,传址只是复制了整个实参的地址过去,指针依据同一个地址访问实参变量。而传值就会将实参内容整个拷贝过去,形参会跟实参占一样大的内存,栈空间是有限的。当然了,在弱小的程序中,传址的这个优点不会被体现出来。

        在函数中,可以随意的返回一个局部变量。
        但如果返回一个局部变量的地址(指针),编译器就会给出警告。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放,这样指针指向的内容就是不可预料的内容,程序就会出错。

        准确的来说,函数不能返回指向栈内存的指针(返回指向堆内存的指针是可以的)。

返回局部变量       

        C++中,函数是可以返回局部变量的,原因:返回值是拷贝值,局部变量的作用域为函数内部,函数执行结束,栈上的局部变量会销毁,内存释放。

可返回的局部变量:

1、返回局部变量本身

#include <iostream>int display() {int num = 9;return num;
}int main() {int p = display();std::cout << p << std::endl; // 9return 0;
}

        display 函数返回一个 int 类型的局部变量 num,函数会把局部的num的值复制一份拷贝给主函数里面的 p 变量。这样是可以的,而且这种方式在程序里面还是经常用到的。
上面程序输出:9

返回局部变量地址

         引用一位博主的分析:https://blog.csdn.net/qq_34801642/article/details/88411252
        C/C++语言函数是不能返回局部变量地址(特指存放于栈区的局部变量地址),除非是局部静态变量地址,字符串常量地址、动态分配地址。其原因是一般局部变量的作用域只在函数内,其存储位置在栈区中,当程序调用完函数后,局部变量会随此函数一起被释放。其地址指向的内容不明(原先的数值可能不变,也可能改变)。而局部静态变量地址和字符串常量地址存放在数据区,动态分配地址存放在堆区,函数运行结束后只会释放栈区的内容,而不会改变数据区和堆区。

        举例如下:

#include <iostream>int* display() {int num[3] = {8,6,5};return num;
}int main() {int* p = display();for(int i = 0; i < 3; i++) {std::cout << *(p+i) << std::endl;}return 0;
}

        这段代码存在一个问题,即在函数display()中返回了一个指向局部变量num的指针。这是不安全的操作,因为当函数display()结束时,局部变量num将被销毁,指向它的指针p将变成悬空指针,使用它可能导致未定义的行为。
上述程序输出结果为:

若要完整的打印num数组中的3个数,我们该怎么做呢?

函数返回局部变量的地址通常有以下几种方法

1、返回一个字符串常量的指针
const char* buffer = "helloword";

#include <iostream>const char* display() {const char* buffer = "helloword";return buffer;
}int main() {const char* str;str = display();std::cout << str << std::endl;return 0;
}

        这样程序运行是没有问题的;buffer存在只读内存区,在 display() 退出的时候,字符串常量不会被收回,因此把地址赋给str时可以正确访问。
        上面这个方式只是最简单的解决方案,因为字符串存放在只读内存区,以后需要修改它的时候就会很麻烦。 

上述程序输出:

(以下为错误方法 char buffer[] = "helloword"; ) 

#include<iostream>
char* display()
{char buffer[] = "helloword";return buffer;
}int main()
{char* str;str = display();std::cout << str;return 0;
}

        在display()函数中,你定义了一个局部字符数组buffer,并将其作为指针返回给主函数。然而,当函数执行完毕后,局部数组buffer的生命周期结束,它所占用的内存将被释放。因此,在主函数中访问指针str指向的值将会导致未定义的行为。
        运行上述程序,输出乱码。

2、使用全局声明的数组

 没有使用全局声明的数组的情况

#include <iostream>int* display() {int num[5] = { 3,4,5,6,8 };return num;
}int main() {int* p;p = display();for (int i = 0; i < 5; i++) {std::cout << "*(p+" << i << "):" << *(p + i) << std::endl;}return 0;
}

输出结果: 

 使用了全局声明的数组的情况

#include <iostream>int num[5] = { 3,4,5,6,8 };
int* display() {return num;
}int main() {int* p;p = display();for (int i = 0; i < 5; i++) {std::cout << "*(p+" << i << "):" << *(p + i) << std::endl;}return 0;
}

输出结果:

        这种情况简单容易。缺点就是任何人都有可能在任何时候修改这个全局数组,而且该函数的下一次调用会覆盖数组的内容。 

3、使用静态数组 static 

#include<iostream>int* display() {static int num[5] = { 3,4,5,6,8 };return num;
}int main() {int* p;p = display();for (int i = 0; i < 5; i++) {std::cout << "*(p+" << i << "):" << *(p + i) << std::endl;}return 0;
}

输出结果: 

 

        使用静态数组可以保证内存不被回收,而且可以防止任何人修改这个数组。只有拥有指向该数组的指针的函数才能修改这个静态数组,不过同时该函数的下一次调用会覆盖数组的内容。同时和全局数组一样,大型缓冲区闲置是非常浪费空间的。 

补充:

static int num[5]和int num[5]的区别在于变量的作用域和生命周期。

1. static int num[5]:在函数内部或者代码块内部使用static关键字声明的数组,称为静态数组。静态数组的特点是:
   - 作用域:静态数组的作用域限定在声明它的函数内部或者代码块内部。
   - 生命周期:静态数组在程序运行期间一直存在,即使函数执行完毕或者代码块结束,数组仍然存在于内存中。
   - 存储位置:静态数组存储在静态存储区,不会随着函数的调用而创建和销毁。

2. int num[5]:只使用int关键字声明的数组,称为自动数组(也称为局部数组)。自动数组的特点是:
   - 作用域:自动数组的作用域限定在声明它的函数内部或者代码块内部。
   - 生命周期:自动数组的生命周期与所在的函数执行周期相关,函数执行完毕或者代码块结束时,数组会被销毁。
   - 存储位置:自动数组存储在栈上,随着函数的调用和返回而动态创建和销毁。

总结:
静态数组的作用于是全局的,生命周期是整个程序运行期间,存储在静态存储区;
自动数组的作用域是局部的,生命周期与所在函数相关,存储在栈上。

4、显式的分配内存,在堆上动态分配内存 new / malloc

使用new动态分配内存 


#include<iostream>
char* display()
{char* buffer = new char[11];strcpy_s(buffer,11, "helloworld");return buffer;
}int main()
{char* str;str = display();std::cout << str;delete[] str; // 释放内存return 0;
}

输出结果为: 

        在上述代码中,我们使用strcpy_s函数来进行字符串复制操作。注意,我们还将缓冲区大小作为第二个参数传递给strcpy_s,确保不会发生缓冲区溢出。
        注意,在使用动态内存分配时,需要确保在不再使用时手动释放内存,以避免内存泄漏。

 使用malloc动态分配内存


#include<iostream>
char* fun()
{int i;char* buffer = (char*)malloc(sizeof(char) * 20); if (buffer != NULL) {strcpy_s(buffer,20, "abcdefgwwwwwwweeeww");}return buffer;
}int main()
{char* str;str = fun();if (str != NULL) {std::cout << str << std::endl;free(str); // 释放内存}return 0;
}

输出结果: 

部分引用自:

C/C++ 返回函数内局部变量和局部变量的地址_c++返回地址的函数-CSDN博客 

相关文章:

C/C++ 中的函数返回局部变量以及局部变量的地址?

C/C中&#xff0c;函数内部的一切变量(函数内部局部变量&#xff0c;形参)都是在其被调用时才被分配内存单元。形参和函数内部的局部变量的生命期和作用域都是在函数内部(static变量的生命期除外)。子函数运行结束时&#xff0c;所有局部变量的内存单元会被系统释放。在C中&…...

springboot和vue:七、mybatis/mybatisplus多表查询+分页查询

mybatisplus实际上只对单表查询做了增强&#xff08;速度会更快&#xff09;&#xff0c;从传统的手写sql语句&#xff0c;自己做映射&#xff0c;变为封装好的QueryWrapper。 本篇文章的内容是有两张表&#xff0c;分别是用户表和订单表&#xff0c;在不直接在数据库做表连接的…...

【Leetcode】 51. N 皇后

按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一种…...

Java数据库连接:JDBC介绍与简单示例

Java数据库连接&#xff1a;JDBC介绍与简单示例 在Java程序中&#xff0c;操作数据库是必不可少的。JDBC&#xff08;Java Database Connectivity&#xff09;是Java中用于连接和操作数据库的一种技术。通过JDBC&#xff0c;Java程序可以与各种关系型数据库进行交互&#xff0…...

智慧茶园:茶厂茶园监管可视化视频管理系统解决方案

一、方案背景 我国是茶叶生产大国&#xff0c;茶叶销量全世界第一。随着经济社会的发展和人民生活水平的提高&#xff0c;对健康、天然的茶叶产品的消费需求量也在逐步提高。茶叶的种植、生产和制作过程工序复杂&#xff0c;伴随着人力成本的上升&#xff0c;传统茶厂的运营及…...

springboot整合pi支付开发

pi支付流程图&#xff1a; 使用Pi SDK功能发起支付由 Pi SDK 自动调用的回调函数&#xff08;让您的应用服务器知道它需要发出批准 API 请求&#xff09;从您的应用程序服务器到 Pi 服务器的 API 请求以批准付款&#xff08;让 Pi 服务器知道您知道此付款&#xff09;Pi浏览器向…...

类 ChatGPT 模型存在的局限性

尽管类ChatGPT模型经过数月的迭代和完善&#xff0c;已经初步融入了部分领域以及人们的日常生活&#xff0c;但目前市面上的产品和相关技术仍然存在一些问题&#xff0c;以下列出一些局限性进行详细说明与成因分析&#xff1a; 1&#xff09;互联网上高质量、大规模、经过清洗…...

Nginx的安全控制

安全控制 关于web服务器的安全是比较大的一个话题&#xff0c;里面所涉及的内容很多&#xff0c;Nginx反向代理是安全隔离来提升web服务器的安全&#xff0c;通过代理分开了客户端到应用程序服务器端的连接&#xff0c;实现了安全措施。在反向代理之前设置防火墙&#xff0c;…...

字符串与字符编码 - GO语言从入门到实战

字符串与字符编码 - GO语言从入门到实战 字符串 与其他主要编程语⾔的差异 基本数据类型&#xff1a;string 是基础数据类型&#xff0c;而不是引用类型或指针类型。string 在内存中占用的空间大小是固定的&#xff0c;且只读、不可改变。字节切片&#xff1a;string 是只读…...

12P4375X042-233C KJ2005X1-BA1 CE3007 EMERSON servo controller

12P4375X042-233C KJ2005X1-BA1 CE3007 EMERSON servo controller 我们提供三种不同类别的EDGEBoost I/O模块供选择&#xff0c;以实现最大程度的I/O定制: 数字和模拟输入/输出网络和连接边缘人工智能和存储 利用EDGEBoost I/O实现变革性技术 EBIO-2M2BK EBIO-2M2BK载板支持…...

WPF向Avalonia迁移(四、其他事项)

开发必备 1. Avalonia项目源代码&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;没有源代码&#xff0c;你连控件的背景色怎么改都找不着&#xff01;&#xff01; 2.下载你所使用的版本&#x…...

Python 代码调试

from pdb import set_trace as stx 是一个Python代码中常用的调试技巧之一&#xff0c;它用于在代码中插入断点以进行调试。这行代码的作用是将Python标准库中的 pdb&#xff08;Python Debugger&#xff09;模块中的 set_trace 函数导入&#xff0c;并将其重命名为 stx&#x…...

DM宣传单制作,利用在线模板,快速替换文字

如果你需要制作一批宣传单&#xff0c;但是时间很紧&#xff0c;而且没有专业的设计人员协助&#xff0c;那么你可以选择使用在线模板来快速制作宣传单。本文将介绍如何使用乔拓云平台&#xff0c;快速制作宣传单的方法。 步骤一&#xff1a;选择适合的在线制作工具 首先&…...

【力扣】42. 接雨水

这道题我卡了差不多1个小时&#xff0c;不是不会做&#xff0c;是不知道怎么能用栈来实现&#xff0c;后面看了一个博主的视频&#xff0c;豁然开朗&#xff0c;我主要的纠结点在于当指针指到7的时候&#xff0c;我计算出4到7的水块是2&#xff0c;但实际上是0&#xff0c;因为…...

IPETRONIK数据采集设备携手Softing Q-Vision软件致力于ADAS测试方案

一 背景 汽车ADAS技术是当下国内外的重点研究方向&#xff0c;且ADAS的发展水平和市场竞争力紧密相关&#xff0c;因此一套完善的ADAS测试方案对各整车厂而言非常重要。然而&#xff0c;国内ADAS测试却面临着很多阻碍&#xff0c;主要原因在于&#xff1a;相关测试设备昂贵&am…...

Go语言中的指针介绍

Go语言中的指针 文章目录 Go语言中的指针一、Go语言中的指针介绍1.1 指针介绍1.2 基本语法1.3 声明和初始化1.4 Go 指针的3个重要概念1.4.1 指针地址&#xff08;Pointer Address&#xff09;1.4.2 指针类型&#xff08;Pointer Type&#xff09;1.4.3 指针取值&#xff08;Poi…...

简单理解区块链

这篇是挖矿篇详细介绍区块链之挖矿-CSDN博客的后置文章&#xff0c;咱们通过之前的解释进一步复习学习区块链叭&#xff01; 百度百科定义 区块链&#xff0c;就是一个又一个区块组成的链条。每一个区块中保存了一定的信息&#xff0c;它们按照各自产生的时间顺序连接成链条。这…...

[尚硅谷React笔记]——第3章 React应用(基于React脚手架)

目录&#xff1a; react脚手架创建项目并启动react脚手架项目结构一个简单的Hello组件样式的模块化功能界面的组件化编码流程&#xff08;通用&#xff09;组件的组合使用-TodoList 1.react脚手架 xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需…...

《Linux 内核设计与实现》13. 虚拟文件系统

通用文件接口 VFS 使得可以直接使用 open()、read()、write() 这样的系统调用而无需考虑具体文件系统和实际物理介质。 好处&#xff1a;新的文件系统和新类型的存储介质需要挂载时&#xff0c;程序无需重写&#xff0c;甚至无需重新编译。 VFS 将各种不同的文件系统抽象后采…...

2021-06-09 51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次

缘由51单片机:两个独立按键控制一个led,k1按下松开led闪烁三次,k2按下LED闪烁五次_嵌入式-CSDN问答 #include "REG52.h" sbit K1 P1^0; sbit K2 P1^1; sbit LEDP0^0; void main() {unsigned char Xd0,ss0;unsigned int wei0;while(1){if(K10&&Xd0){ss3*2;…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

C++.OpenGL (20/64)混合(Blending)

混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分&#xff1a; 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...

node.js的初步学习

那什么是node.js呢&#xff1f; 和JavaScript又是什么关系呢&#xff1f; node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说&#xff0c; 需要在node.js的环境上进行当JavaScript作为前端开发语言来说&#xff0c;需要在浏览器的环境上进行 Node.js 可…...