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

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

相关文章

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R

使用Python的requests库调用API接口的详细步骤

《使用Python的requests库调用API接口的详细步骤》使用Python的requests库调用API接口是开发中最常用的方式之一,它简化了HTTP请求的处理流程,以下是详细步骤和实战示例,涵... 目录一、准备工作:安装 requests 库二、基本调用流程(以 RESTful API 为例)1.

Python pandas库自学超详细教程

《Pythonpandas库自学超详细教程》文章介绍了Pandas库的基本功能、安装方法及核心操作,涵盖数据导入(CSV/Excel等)、数据结构(Series、DataFrame)、数据清洗、转换... 目录一、什么是Pandas库(1)、Pandas 应用(2)、Pandas 功能(3)、数据结构二、安

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

Spring Boot Actuator应用监控与管理的详细步骤

《SpringBootActuator应用监控与管理的详细步骤》SpringBootActuator是SpringBoot的监控工具,提供健康检查、性能指标、日志管理等核心功能,支持自定义和扩展端... 目录一、 Spring Boot Actuator 概述二、 集成 Spring Boot Actuat

如何在Java Spring实现异步执行(详细篇)

《如何在JavaSpring实现异步执行(详细篇)》Spring框架通过@Async、Executor等实现异步执行,提升系统性能与响应速度,支持自定义线程池管理并发,本文给大家介绍如何在Sprin... 目录前言1. 使用 @Async 实现异步执行1.1 启用异步执行支持1.2 创建异步方法1.3 调用

Spring Boot 与微服务入门实战详细总结

《SpringBoot与微服务入门实战详细总结》本文讲解SpringBoot框架的核心特性如快速构建、自动配置、零XML与微服务架构的定义、演进及优缺点,涵盖开发环境准备和HelloWorld实战... 目录一、Spring Boot 核心概述二、微服务架构详解1. 微服务的定义与演进2. 微服务的优缺点三