传值、传址、空间释放详细图解

news2024/11/15 18:00:47

目录

前言

一.进程

1.1 进程的映射

1.2 进程的虚拟空间

二.函数传参

2.1 函数传参

2.2 函数传值

2.2.1 函数传值案例1

2.2.2 函数传值案例2

2.2.3 返回值为常量

2.3 函数传送地址

2.3  字符串使用


前言

详细介绍函数传值和传地址区别:进行数据操作的区别,函数传值为临时拷贝,不会对实参影响。而传地址时,就是传实参,是可以影响到实参的..........

一.进程

1.1 进程的映射

物理空间映射到虚拟空间是计算机内存管理中的一个重要概念,它允许程序使用的地址(虚拟地址)与计算机物理内存的地址(物理地址)之间进行转换

这种映射机制主要涉及以下几个关键点:

  1. 虚拟地址与物理地址:用户在编程时使用的地址称为虚拟地址或逻辑地址,而计算机物理内存中的地址称为物理地址。虚拟地址通过内存管理单元(MMU)的转换,映射到物理内存的实际位置。
  2. 虚拟存储空间与物理存储空间:虚拟地址对应的存储空间称为虚拟存储空间或逻辑地址空间,而物理地址对应的存储空间称为物理存储空间或主存空间。
  3. 虚拟页与物理页:在虚拟内存系统中,虚拟页是虚拟地址空间的一部分,它们会映射到物理内存中的物理页。这种分页机制使得程序可以拥有比实际物理内存更大的地址空间。
  4. 固定映射区与动态映射区:在固定映射区中,虚拟内存地址固定映射到物理内存的高端地址上,而在动态映射区和永久映射区中,虚拟地址不是固定的,可以被动态改变以映射到不同的物理地址上。
  5. 内存映射mmap:内存映射是一种将文件或其他资源映射到进程的虚拟地址空间的技术,这样进程就可以像访问内存一样访问这些资源。这通常用于优化文件I/O操作,因为它减少了数据从磁盘到内存的复制次数。

1.2 进程的虚拟空间

进程的虚拟空间是一个逻辑意义上的内存空间概念,它允许每个进程拥有独立的地址空间,使得程序的运行更加安全和高效

二.函数传参

2.1 函数传参

函数传参时可以通过传递地址的方式来实现对实际参数的修改。具体来说:

  1. 指针传递:在函数定义中,参数使用指针类型,调用函数时传递变量的地址。在函数内部,通过解引用操作可以访问和修改该地址指向的数据。这样,当函数返回时,原变量的值可能会被改变。
  2. 数组传参:在C语言中,当数组作为函数参数时,实际上传递的是数组首元素的地址。这是因为数组在内存中是连续存储的,所以传递首元素地址足以让函数访问整个数组。
  3. 引用传递:在某些编程语言(如C++)中,可以使用引用传递的方式。引用本质上是一个别名,它允许函数直接操作传递给它的变量。在函数内部对引用参数的任何修改都会反映到原始变量上。
  4. const修饰符:如果形参是指针并且加了const修饰,则该指针指向的数据是不可修改的。这有助于保护数据不被意外修改。
  5. 值传递:与地址传递相对的是值传递,值传递会创建一个实参的副本,函数内部的操作不会影响原始变量。但对于数组,由于发生了“降维”,实际上传入的是数组首元素的地址,因此即使是值传递,函数内部对数组元素的修改也是真实存在的。

总之,在实际应用中,选择哪种传递方式取决于具体的编程需求。例如,如果需要在函数内部修改外部变量的值,通常会选择指针传递或引用传递。而如果只是需要读取数据而不做修改,可能会选择值传递。

2.2 函数传值

2.2.1 函数传值案例1

这是最常见的传参方式,当调用函数时,实参的值被复制给形参,形参和实参分别占用不同的存储空间。在函数内部对形参的修改不会影响到外部的实参。这种方式简单且安全,但可能会因为参数复制而降低效率。

 #include<stdio.h>
 #include <stdlib.h>
 #include <string.h>
 void GetMemory1(char *p) // p ==== NULL
 {
    *p = (char *)malloc(100); // 向系统申请100个字节的堆空间, 让p 指向该区域
 }

 void Test1(void)
 {
    char *str = NULL;
    GetMemory1(str); /// str ==== NULL
    // 经过GetMemory1 的操作后 str的指向依然没有变换还是指向NULL
    strcpy(str, "hello world"); // 提示: 拷贝字符串, 把"hello world" 拷贝到str 所指向的内存空间中
    // 拷贝函数出现段错误

    printf("%s\n" , str);
 }

 int main(int argc, char const *argv[])
 {
    Test1();
    return 0;
 }

函数传值进行操作内存时,需要注意,如下:调用开辟内存函数时,没有传入地址,会出现野指针问题。这样传值就是错误的,正确的写法写在后面传地址案例中。

2.2.2 函数传值案例2

这里参考使用static数据段的操作

#include<stdio.h>
 #include <stdlib.h>
 #include <string.h>
char *GetMemory2(void)
{
    // 数组 p 所存放的位置为 栈空间, 当函数 GetMemory2 退出返回时, 该区域会被系统回收
    // 不应该返回该内存中的地址
    // 可以使用 static 来修饰该数组, 使其的内存区域改为数据段
    static char p[] = "hello world";
    return p;
}
 void Test2(void)
 {
    char *str = NULL;
    str = GetMemory2();
    printf("TEST‐2:%s\n",str);
 }
int main(int argc, char const *argv[])
{
   Test2();
   return 0;
}

这里如果不使用static,程序会报错,因为它是传送值的

2.2.3 返回值为常量

这里的函数调用的返回值为常量,也就是存储在虚拟空间数据段

 #include<stdio.h>
#include <stdlib.h>
#include <string.h>
// TEST3
char *GetMemory3(void)
{
    // 直接返回 常量区的内存地址 , 注意该区域只读
    return "hello world";
}
void Test3(void)
{
    char *str = NULL;
    str = GetMemory3();
    printf("TEST‐3:%s\n",str);
 }

int main(int argc, char const *argv[])
{
   Test3();
   return 0;
}

2.3 函数传送地址

在这种传参方式中,实参的地址被传递给形参,形参通常是指针类型。函数内部通过指针来访问和修改实参的值。这种方式允许函数修改外部变量的内容,适用于需要输出结果或者大型数据结构的情况。

 #include<stdio.h>
 #include <stdlib.h>
 #include <string.h>
 void GetMemory1(char **p) // p ==== NULL
 {
    *p = (char *)malloc(100); // 向系统申请100个字节的堆空间, 让p 指向该区域
 }

 void Test1(void)
 {
    char *str = NULL;
    GetMemory1(&str); /// str ==== NULL
   
    strcpy(str, "hello world"); // 提示: 拷贝字符串, 把"hello world" 拷贝到str 所指向的内存空间中
    

    printf("%s\n" , str);
 }

 int main(int argc, char const *argv[])
 {
    Test1();
    return 0;
 }

这里的传值为传地址,把char* str 的地址&str 传入GetMemonry1(char **p)作为形参,

解引用后*p 其实就是str 。调用的函数进入栈中进行内存的开辟操作,又因为str==*p,所以

即使栈销毁后str仍然指向malloc开辟的空间。

2.3  字符串使用

调用函数时,strcopy字符串时,存在'\0',

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
 // TEST6
 void Test6()
 {
    char *str=(char *)malloc(100);
    strcpy(str, "hello");
    printf("TES1:%s\n" ,str); // 输出 hello
    str+=6; //
    strcpy(str, "world");
     printf("TES2:%s\n" ,str); //输出 word
    str-=6;
    printf("TES3:%s\n" ,str); //输出 hello
    *(str+5)=' ';
    printf("TES4:%s\n" ,str); //输出 hello word
 }
int main(int argc, char const *argv[])
{
   Test6();
   return 0;
}

以上是本期补齐内容,若有不懂或者错误,欢迎评论与私信!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1527017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

08|记忆:通过Memory记住客户上次买花时的对话细节

无论是LLM还是代理都是无状态的&#xff0c;每次模型的调用都是独立于其他交互的。也就是说&#xff0c;我们每次通过API开始和大语言模型展开一次新的对话&#xff0c;它都不知道你其实昨天或者前天曾经和它聊过天了。 使用ConversationChain from langchain import OpenAI…

中国国际光伏展

河北省京津冀国际光伏展是一个旨在推动光伏产业发展的展览会。该展览会的举办地点在河北省&#xff0c;也被称为京津冀地区&#xff0c;这个地区是中国重要的经济发展区域之一&#xff0c;拥有丰富的太阳能资源和光伏产业基础。 光伏展览会将展示最新的光伏技术、产品和解决方案…

ABS210-ASEMI新能源专用整流桥ABS210

编辑&#xff1a;ll ABS210-ASEMI新能源专用整流桥ABS210 型号&#xff1a;ABS210 品牌&#xff1a;ASEMI 封装&#xff1a;ABS-4 正向电流&#xff08;Id&#xff09;&#xff1a;2A 反向耐压&#xff08;VRRM&#xff09;&#xff1a;1000V 正向浪涌电流&#xff1a;3…

Fabric.js在vue2中使用

Fabric.js安装 这里我是基于vue来使用的&#xff0c;先安装上Fabric.js npm install fabric 在main.js中 import fabric from fabric Vue.use(fabric);Fabric 提供了 7 种基础形状&#xff1a; fabric.Circle (圆)fabric.Ellipse (椭圆)fabric.Line (线)fabric.Polyline (多条…

汽车电子零部件(8):T_Box

前言: 网联汽车(Connected Vehicles ,CV)是一个广泛的概念,四个主要的CV线程已发展起来:互联、自主、共享和电动。这些应用于包括CV在内的垂直领域:汽车、通信、互联网和共享手机服务。中国汽车工程师学会(SAEC)提倡将车载ADAS(高级驾驶员辅助系统)与通信技术相结合…

概述工业4.0时代的数据采集

在当今全球制造业转型升级的大潮中&#xff0c;工业4.0以其智能化、网络化和个性化的特征&#xff0c;引领了新的工业革命。其中&#xff0c;数据采集作为工业4.0的核心要素&#xff0c;是实现智能制造的关键环节&#xff0c;它的重要性不言而喻。 工业4.0时代的数据采集&#…

Linux中判断某个Docker容器服务的内存是否超过临界值,比如到达10G,则在凌晨4点执行定时任务执行重新构建命令

监测NAME是blade-jiangxinzhougarden的服务&#xff0c;如果内存&#xff08;MEM USAGE&#xff09;超过10G则在凌晨4点执行重新构建命令 1.编写脚本文件restart_jxz.sh #!/bin/bash# 设置要监控的容器名称或服务名称(替换成你的服务名) SERVICE_NAME"blade-jiangxinzhou…

SpringSecurity(SpringBoot2.X版本实现)

资料来源于 SpringSecurity框架教程-Spring SecurityJWT实现项目级前端分离认证授权 侵权删 目录 介绍 快速开始 认证 认证流程 登录校验流程 SpringSecurity完整流程 认证流程详解 代码实现 准备工作 mysql mybatis-plus redis 统一返回类 核心代码 密码加密存…

前端UI框架是不是大厂给中小企业“画地为牢”?

先说结论&#xff1a;不能说全是吧&#xff0c;但也大部分是。大厂赏饭&#xff08;开源&#xff09;给你吃&#xff0c;你就得按照他的规矩来。 一、大厂为什么喜欢开源 大厂愿意开源他们的前端框架有以下几个原因&#xff1a; 社区贡献&#xff1a;开源框架可以吸引更多的开…

【简写Mybatis】04-数据源的解析、创建和使用

前言 在学习MyBatis源码文章中&#xff0c;斗胆想将其讲明白&#xff1b;故有此文章&#xff0c;如有问题&#xff0c;不吝指教&#xff01; 注意&#xff1a; 学习源码一定一定不要太关注代码的编写&#xff0c;而是注意代码实现思想&#xff1b; 通过设问方式来体现代码中的…

Redis中的String编码转换底层原理及6.0新特性

String编码转换底层原理 String对象为什么把大于39字节或者44字节的字符串编码为raw&#xff0c;小于的时候编码为embstr? 在Redis3.2以前的版本中,SDS作为字符串类型中存储字符串内容的结构&#xff0c;源码如下&#xff1a; 3.2版本SDS结构 struct sdshdr {// 记录buf数…

【DFS】树的重心

树的邻接表 存储方式 int N; int h[N];//存以N为编号的节点的下一个节点的idx int e[2N];//存idx的节点的编号 int nex[2N];//存idx节点的下一个节点的idxvoid add(int a, int b){ e[idx] b; nex[idx] h[a]; h[a]idx; }dfs遍历方式&#xff1a; void dfs(int u){state[u]…

day6 3/18

2.试编程&#xff1a; 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&#xff08;整…

sqllab第二十四关通关笔记

知识点&#xff1a; 二次注入 先埋一个炸弹&#xff0c;然后通过其他路径引爆它 查看界面发现是一个登录框&#xff0c;尝试进行登录框的注入发现这里不存在注入点 那么就注册一个新的账户吧 通过点击注册&#xff0c;进入注册面板&#xff0c;注册一个新的账户 用户名为 re…

【算法】多路归并(鱼塘钓鱼)

有 N 个鱼塘排成一排&#xff0c;每个鱼塘中有一定数量的鱼&#xff0c;例如&#xff1a;N5 时&#xff0c;如下表&#xff1a; 鱼塘编号12345第1分钟能钓到的鱼的数量&#xff08;1..1000&#xff09;101420169每钓鱼1分钟钓鱼数的减少量&#xff08;1..100)24653当前鱼塘到下…

【Java 并发】AbstractQueuedSynchronizer 中的 Condition

1 简介 任何一个 Java 对象都天然继承于 Object 类, 在线程间实现通信的往往会应用到 Object 的几个方法, 比如 wait(), wait(long timeout), wait(long timeout, int nanos) 与 notify(), notifyAll() 几个方法实现等待 / 通知机制。同样的, 在 Java Lock 体系下也有同样的方…

代码随想录算法训练营第day27|93.复原IP地址 、 78.子集 、 90.子集II

93.复原IP地址 93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 …

AI论文速读 | UniTS:构建统一的时间序列模型

题目&#xff1a;UniTS: Building a Unified Time Series Model 作者&#xff1a;Shanghua Gao&#xff08;高尚华&#xff09;, Teddy Koker, Owen Queen, Thomas Hartvigsen, Theodoros Tsiligkaridis, Marinka Zitnik 机构&#xff1a;哈佛大学&#xff08;Harvard&#x…

学完排序算法,终于知道用什么方法给监考完收上来的试卷排序……

由于每个老师批改完卷子之后装袋不一定是有序的&#xff0c;鼠鼠我被拉去当给试卷排序的苦力。面对堆积成山的试卷袋&#xff0c;每一份试卷袋的试卷集又很重&#xff0c;鼠鼠我啊为了尽早下班&#xff0c;决定用一种良好的办法进行排序。 1.插入排序 首先考虑的是插入排序。…

Python 井字棋游戏

井字棋是一种在3 * 3格子上进行的连珠游戏&#xff0c;又称井字游戏。井字棋的游戏有两名玩家&#xff0c;其中一个玩家画圈&#xff0c;另一个玩家画叉&#xff0c;轮流在3 * 3格子上画上自己的符号&#xff0c;最先在横向、纵向、或斜线方向连成一条线的人为胜利方。如图1所示…