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

2024-03-19 00:28

本文主要是介绍传值、传址、空间释放详细图解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

前言

一.进程

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的指向依然没有变换还是指向NULLstrcpy(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 ==== NULLstrcpy(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>// TEST6void Test6(){char *str=(char *)malloc(100);strcpy(str, "hello");printf("TES1:%s\n" ,str); // 输出 hellostr+=6; //strcpy(str, "world");printf("TES2:%s\n" ,str); //输出 wordstr-=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.chinasem.cn/article/824196

相关文章

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

IntelliJ IDEA 中配置 Spring MVC 环境的详细步骤及问题解决

《IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决》:本文主要介绍IntelliJIDEA中配置SpringMVC环境的详细步骤及问题解决,本文分步骤结合实例给大... 目录步骤 1:创建 Maven Web 项目步骤 2:添加 Spring MVC 依赖1、保存后执行2、将新的依赖

如何为Yarn配置国内源的详细教程

《如何为Yarn配置国内源的详细教程》在使用Yarn进行项目开发时,由于网络原因,直接使用官方源可能会导致下载速度慢或连接失败,配置国内源可以显著提高包的下载速度和稳定性,本文将详细介绍如何为Yarn... 目录一、查询当前使用的镜像源二、设置国内源1. 设置为淘宝镜像源2. 设置为其他国内源三、还原为官方

最详细安装 PostgreSQL方法及常见问题解决

《最详细安装PostgreSQL方法及常见问题解决》:本文主要介绍最详细安装PostgreSQL方法及常见问题解决,介绍了在Windows系统上安装PostgreSQL及Linux系统上安装Po... 目录一、在 Windows 系统上安装 PostgreSQL1. 下载 PostgreSQL 安装包2.

MySql match against工具详细用法

《MySqlmatchagainst工具详细用法》在MySQL中,MATCH……AGAINST是全文索引(Full-Textindex)的查询语法,它允许你对文本进行高效的全文搜素,支持自然语言搜... 目录一、全文索引的基本概念二、创建全文索引三、自然语言搜索四、布尔搜索五、相关性排序六、全文索引的限制七

python中各种常见文件的读写操作与类型转换详细指南

《python中各种常见文件的读写操作与类型转换详细指南》这篇文章主要为大家详细介绍了python中各种常见文件(txt,xls,csv,sql,二进制文件)的读写操作与类型转换,感兴趣的小伙伴可以跟... 目录1.文件txt读写标准用法1.1写入文件1.2读取文件2. 二进制文件读取3. 大文件读取3.1

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

如何在Mac上安装并配置JDK环境变量详细步骤

《如何在Mac上安装并配置JDK环境变量详细步骤》:本文主要介绍如何在Mac上安装并配置JDK环境变量详细步骤,包括下载JDK、安装JDK、配置环境变量、验证JDK配置以及可选地设置PowerSh... 目录步骤 1:下载JDK步骤 2:安装JDK步骤 3:配置环境变量1. 编辑~/.zshrc(对于zsh

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve