当前位置: 首页 > 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;…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...