【C语言】传值调用与传址调用:深度解析与实现


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: C语言

文章目录

  • 💯前言
  • 💯什么是传值调用和传址调用?
    • 1. 传值调用(Call by Value)
    • 2. 传址调用(Call by Reference)
  • 💯传值调用与传址调用的区别
  • 💯深入理解指针与地址传递
  • 💯Java 中的传值与传址模拟
    • 1. Java 中的值传递
    • 2. Java 中通过对象实现交换
  • 💯传值调用和传址调用的应用场景
  • 💯传址调用中的风险和注意事项
  • 💯小结


在这里插入图片描述


💯前言

  • 在学习 C语言 时,“传值调用”“传址调用” 是两个至关重要的概念,涉及到函数与变量的交互机制,以及如何有效管理内存资源。理解这两个概念对于深入掌握函数的作用域变量的生命周期,以及编写高效和健壮的代码至关重要。
    本文将对这两个概念进行深入探讨,分析它们的原理实现方式、各自的优缺点,并结合实际代码示例来帮助你全面掌握这两种方法。同时,我们将探讨指针的作用及其在 C语言 中的重要性,从多个角度帮助您系统性地理解这些关键概念
    C语言
    在这里插入图片描述

💯什么是传值调用和传址调用?

在这里插入图片描述


1. 传值调用(Call by Value)

在这里插入图片描述
传值调用是指在函数调用过程中,向函数传递的是实参的值的副本,即将实参的值复制一份传递给函数的形参。因此,函数内部对形参的操作是不会影响实参本身的。

传值调用中,函数接收到的是变量的一个副本,而不是变量的原始数据本身。因此,在函数内部对这个副本进行修改,原变量并不会受到任何影响C语言中,传值调用是默认的参数传递方式,通常适用于不需要修改实参数据的场景。

特点

  • 安全性高
    由于函数只操作实参的副本,因此不必担心对原始数据的意外修改

  • 性能开销
    当传递大型结构体时,由于需要复制整个结构体,可能会产生较高的内存和性能开销

  • 适用场景
    传值调用通常用于只需要读取数据而不对其进行修改的场合,例如一些数据分析统计或只进行数据输出的场景。使用传值调用可以确保代码的高可维护性数据安全性

代码示例:

#include <stdio.h>void swap(int x, int y) {int temp = x;x = y;y = temp;printf("Inside function: x = %d, y = %d\n", x, y);
}int main() {int a = 10, b = 20;printf("Before swap: a = %d, b = %d\n", a, b);swap(a, b);printf("After swap: a = %d, b = %d\n", a, b);return 0;
}

输出结果:

Before swap: a = 10, b = 20
Inside function: x = 20, y = 10
After swap: a = 10, b = 20

分析:
在上述示例中,swap 函数中的 xyab 的副本,函数内部虽然交换了 xy 的值,但这种修改仅限于函数的作用域范围内,无法影响到原始的 ab。因此,main 函数中的 ab 的值在调用结束后并未改变。


2. 传址调用(Call by Reference)

在这里插入图片描述
传址调用则不同,它指向函数传递的是变量的地址,而不是值的副本。通过这种方式,函数可以直接访问和修改原始变量的值。在 C语言中,传址调用可以通过指针来实现。

特点

  • 效率高
    函数不需要复制变量的整个值,而是直接操作变量的地址,特别适合于大型数据结构复杂数据类型的操作。

  • 直接修改实参
    函数内部对形参的修改会直接反映在实参上,因此传址调用特别适用于需要频繁修改数据的场景。

  • 灵活性强
    可以实现许多传值调用无法实现的功能,例如交换变量值动态修改外部数据结构的内容。

代码示例:

#include <stdio.h>void swap(int *pa, int *pb) {int tmp = *pa;  // 获取 a 的值*pa = *pb;      // 将 b 的值赋给 a*pb = tmp;      // 将 tmp(原来 a 的值)赋给 b
}int main() {int a = 10;int b = 20;printf("交换前: a=%d b=%d\n", a, b);swap(&a, &b);  // 传递变量 a 和 b 的地址printf("交换后: a=%d b=%d\n", a, b);return 0;
}

输出结果:

交换前: a = 10, b = 20
交换后: a = 20, b = 10

分析:
在这个例子中,swap 函数通过指针 papb 接收到 ab 的地址,使用解引用(*pa*pb)直接修改了 ab 的值。因此,ab 在函数调用之后得到了交换。


💯传值调用与传址调用的区别

特性传值调用传址调用
传递内容参数值的副本参数的地址
修改效果不会影响实际参数会影响实际参数
使用场景不需要修改参数的场合需要修改参数的场合
性能对于大型数据可能性能较低传递指针,性能较高
安全性更安全,数据隔离需谨慎操作,容易修改原始数据

传值调用传址调用之间的核心区别在于它们对实际参数的影响

  • 传值调用
    通过传递实参的副本来保证原数据的完整性。因此,它通常提供了更高的数据安全性,但效率相对较低,特别是对于复杂数据结构而言。

  • 传址调用
    通过直接传递地址,实现对原始数据的修改。它提供了更大的灵活性更高的效率,但使用时需要特别小心,以免误改原始数据

在这里插入图片描述


💯深入理解指针与地址传递

在C语言中,指针是实现传址调用的关键所在。指针是一种特殊的变量,其存储的是另一个变量的内存地址。通过指针可以实现对任意变量的间接访问和修改,从而大大增强了程序的灵活性。

指针的基本概念:

  • 指针变量:指针变量用于存储其他变量的地址。它们为程序提供了访问和操作其他变量的手段,是C语言中强大的工具。
  • 解引用(Dereferencing):通过 * 操作符可以访问指针所指向的变量的值,即所谓的“解引用”。

例如,在传址调用中,int *pa 就是一个指向 int 类型变量的指针,*pa 则表示该指针指向的变量的。指针的使用不仅可以修改外部变量,还能够通过动态内存分配来实现更灵活的内存管理。例如,使用 malloc 函数可以动态分配数组的大小,满足程序在运行时的不确定需求

指针C语言中的作用极为重要,特别是在操作系统开发嵌入式系统编程等需要底层控制的场景中,指针提供了高效的硬件访问方式,使得 C语言 成为一个**“贴近硬件”**的编程语言。

在这里插入图片描述


💯Java 中的传值与传址模拟

有的读者可能会问:“在其他编程语言中,这种传值传址的概念是如何体现的?”

例如,在 Java 中,所有参数传递都是值传递。但是,Java 的对象引用在表现上类似于**“传址调用”,因为通过传递引用,可以对对象的状态**进行修改。

在这里插入图片描述


1. Java 中的值传递

对于基本数据类型,Java是值传递,类似于C语言中的传值调用:
在这里插入图片描述

public class TestSwap {public static void swap(int x, int y) {int temp = x;x = y;y = temp;System.out.println("Inside function: x = " + x + ", y = " + y);}public static void main(String[] args) {int a = 10;int b = 20;System.out.println("Before swap: a = " + a + ", b = " + b);swap(a, b);System.out.println("After swap: a = " + a + ", b = " + b);}
}

输出结果:

Before swap: a = 10, b = 20
Inside function: x = 20, y = 10
After swap: a = 10, b = 20

在这个例子中,Java在调用 swap(a, b) 时传递的也是 ab 的副本,因此原始变量的值并未发生变化。


2. Java 中通过对象实现交换

Java可以通过传递对象来间接实现类似“传址调用”的效果,因为对象的引用是通过值传递的,但引用本身可以指向同一个对象。
在这里插入图片描述

class SwapHelper {int value;SwapHelper(int value) {this.value = value;}
}public class TestSwap {public static void swap(SwapHelper x, SwapHelper y) {int temp = x.value;x.value = y.value;y.value = temp;}public static void main(String[] args) {SwapHelper a = new SwapHelper(10);SwapHelper b = new SwapHelper(20);System.out.println("Before swap: a = " + a.value + ", b = " + b.value);swap(a, b);System.out.println("After swap: a = " + a.value + ", b = " + b.value);}
}

输出结果:

Before swap: a = 10, b = 20
After swap: a = 20, b = 10

这种方式通过对象封装变量,从而实现了交换的效果。虽然Java中没有像C语言的指针,但通过引用对象可以达到类似传址调用的效果。这在需要修改对象内部状态的场景中尤为有效。


💯传值调用和传址调用的应用场景

在这里插入图片描述

  1. 传值调用
    适用于不希望函数修改原始数据的场景,例如对数据进行分析处理或仅仅是输出。这种方式确保了数据的安全性和完整性,避免了因意外修改带来的潜在错误。在大型团队合作开发中,传值调用也是实现模块化编程的一种安全手段,特别是在函数的输出和副作用需要被严格控制时。

  2. 传址调用
    适用于需要函数直接修改原始数据的场景,例如交换数据修改数组内容或者动态调整数据结构。传址调用的最大优势在于其高效性,因为它避免了数据的重复拷贝。特别是在处理大型结构体或者复杂数据类型时,通过指针传递可以大幅减少内存消耗和提升程序的执行效率


💯传址调用中的风险和注意事项

在这里插入图片描述
使用传址调用虽然可以提高程序的灵活性和效率,但也带来了潜在的风险

  • 指针安全性
    指针必须指向有效的内存地址,解引用空指针(NULL)将导致程序崩溃。因此,在使用指针之前,必须确保指针指向有效的内存,并在使用前检查其是否为 NULL

  • 意外修改
    由于传址调用可以直接修改原始数据,稍有不慎就可能引发意外的错误,特别是在大型代码库多人合作的开发环境中。为了避免此类错误,必须对指针进行严格管理,并且在设计函数接口时明确函数对参数的修改行为

为了降低传址调用的风险,可以采用以下几种方法:

  • 指针初始化
    始终将指针初始化为一个有效的地址NULL,以确保指针状态的可预测性

  • 指针有效性检查
    在每次使用指针之前,先检查其是否为 NULL,以避免解引用空指针导致的程序崩溃

  • 封装指针操作
    将指针操作封装在单独的函数或模块中,以减少直接对指针的访问。这种封装可以显著提高代码的安全性可维护性,特别是在大型项目中尤为重要。


💯小结

  • 在这里插入图片描述
    C语言中的传值调用传址调用是函数参数传递的两种基本方式,各有其优缺点适用场景。传值调用通过传递参数的副本确保数据的安全性和独立性,而传址调用通过传递指针提高了数据操作的效率灵活性。在 Java 等其他语言中,这些概念也有所体现,尽管实现方式存在差异,但理解这些基础概念对于编写健壮、高效的代码依然至关重要。
    对于 C语言开发者而言,深入理解指针参数传递方式的区别是非常关键的技能。无论是在数据保护的需求下选择传值调用,还是在需要高效操作数据时采用传址调用,灵活运用这些技巧对于编写高效可靠的程序至关重要。

在这里插入图片描述


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

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

相关文章

科技赋能健康:多商户Java版商城系统引领亚健康服务数字化变革

在当今社会&#xff0c;随着生活节奏的加快和工作压力的增大&#xff0c;越来越多的人处于亚健康状态。据《The Lancet》期刊2023年的统计数据显示&#xff0c;全球亚健康状态的人群比例已高达82.8%&#xff0c;这一数字背后&#xff0c;隐藏着巨大的健康风险和社会成本。亚健康…

vue实现列表滑动下拉加载数据

一、实现效果 二、实现思路 使用滚动事件监听器来检测用户是否滚动到底部&#xff0c;然后加载更多数据 监听滚动事件。检测用户是否滚动到底部。加载更多数据。 三、案例代码 <div class"drawer-content"><div ref"loadMoreTrigger" class&q…

【CSP CCF记录】201809-2第14次认证 买菜

题目 样例输入 4 1 3 5 6 9 13 14 15 2 4 5 7 10 11 13 14 样例输出 3 思路 易错点&#xff1a;仅考虑所给样例&#xff0c;会误以为H和W两人的装车时间是一一对应的&#xff0c;那么提交结果的运行错误就会让你瞬间清醒。 本题关键是认识到H和W的装车时间不一定一一对应&…

学习threejs,使用设置bumpMap凹凸贴图创建褶皱,实现贴图厚度效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.MeshPhongMaterial高…

GoF设计模式——结构型设计模式分析与应用

文章目录 UML图的结构主要表现为&#xff1a;继承&#xff08;抽象&#xff09;、关联 、组合或聚合 的三种关系。1. 继承&#xff08;抽象&#xff0c;泛化关系&#xff09;2. 关联3. 组合/聚合各种可能的配合&#xff1a;1. 关联后抽象2. 关联的集合3. 组合接口4. 递归聚合接…

Unity中动态生成贴图并保存成png图片实现

实现原理&#xff1a; 要生成长x宽y的贴图&#xff0c;就是生成x*y个像素填充到贴图中&#xff0c;如下图&#xff1a; 如果要改变局部颜色&#xff0c;就是从x1到x2(x1<x2),y1到y2(y1<y2)这个范围做处理&#xff0c; 或者要想做圆形就是计算距某个点&#xff08;x1,y1&…

互联网直播/点播EasyDSS视频推拉流平台视频点播有哪些技术特点?

在数字化时代&#xff0c;视频点播应用已经成为我们生活中不可或缺的一部分。监控技术与视频点播的结合正悄然改变着我们获取和享受媒体内容的方式。这一变革不仅体现在技术层面的进步&#xff0c;更深刻地影响了我们。 EasyDSS视频直播点播平台是一款高性能流媒体服务软件。E…

1语言基础

数据结构与算法可以说是每位程序员的必修课&#xff0c;即使是AI高速发展的今天&#xff0c;熟悉数据结构与算法都无疑是面向开发的一项加分项。先从一个问题看起&#xff1a; # 怎么让后面不带空格 print("11",11) # 11 2方案可能有更多&#xff0c;就像一个问题&am…

Redis 可观测最佳实践

Redis 介绍 Redis 是一个开源的高性能键值对&#xff08;key-value&#xff09;数据库。它通常用作数据库、缓存和消息代理。Redis 支持多种类型的数据结构&#xff0c;Redis 通常用于需要快速访问的场景&#xff0c;如会话缓存、全页缓存、排行榜、实时分析等。由于其高性能和…

【前端】JavaScript 变量声明和函数声明的提升机制:深入探讨提升优先级与其行为

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;提升&#xff08;Hoisting&#xff09;概述&#x1f4af;提升机制——函数声明 vs 变量声明&#x1f4af;代码示例&#xff1a;函数与 var 的提升提升后的代码解析分析 …

fastadmin实现站内通知功能

实现效果如下 application/admin/view/common/header.html <style>#notificationMenu {display: none;position: absolute;top: 40px;right: 0;background: #fff;border-radius: 6px;padding: 10px 0;width: 300px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);z-inde…

Axure RP教程:创建高效用户界面和交互

Axure RP是一款广受好评的软件&#xff0c;专门用于设计精致的用户界面和交互体验。这款软件提供了众多UI控件&#xff0c;并根据它们的用途进行了分类。与此同时&#xff0c;国产的即时设计软件作为Axure的替代品&#xff0c;支持在线协作和直接在浏览器中使用&#xff0c;无需…

2024-11-25 二叉树的定义

一、基本概念 1.二叉树是n(n>0)个结点的有限集合: ① 或者为空二叉树&#xff0c;即n0。 ②或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。 特点&#xff1a; ①每个结点至多只有两棵子树。 ②左右子树不能颠倒&am…

部署实战(二)--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

对象的大小

文章目录 一、对象大小 一、对象大小 对象是类实例化出来的&#xff0c;让我们分析一下类对象中哪些成员呢&#xff1f; 类实例化出的每个对象&#xff0c;每个都有独立的数据空间&#xff0c;所以对象中肯定包含 成员变量&#xff0c;那么成员函数是否包含呢&#xff1f; 首…

01-go入门

文章目录 Go语言学习1. 简介安装windows安装linux安装编译工具安装-goland 2. 入门2.1 Helloworld注释 2.2 变量初始化打印内存地址变量交换匿名变量作用域局部变量全局变量 2.3 常量iota 2.4 数据类型布尔整数浮点类型复数字符串定义字符串字符串拼接符定义多行字符串 map数据…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

LeetCode-632. Smallest Range Covering Elements from K Lists [C++][Java]

目录 题目描述 解题思路 【C】 【Java】 LeetCode-632. Smallest Range Covering Elements from K Listshttps://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/description/ 题目描述 You have k lists of sorted integers in non-decreasing o…

UI自动化测试中公认最佳的设计模式-POM

一、概念 什么是POM&#xff1f; POM是PageObjectModule&#xff08;页面对象模式&#xff09;的缩写&#xff0c;其目的是为了Web UI测试创建对象库。在这种模式下&#xff0c;应用涉及的每一个页面应该定义为一个单独的类。类中应该包含此页面上的页面元素对象和处理这些元…

Scala文件读写——成绩分析

根据文件解决下例问题 1.读入txt文件&#xff1a;按行读入 import scala.io.Sourceobject Test文件读写_成绩分析 {def main(args: Array[String]): Unit {//1.按行读入val source Source.fromFile("score.txt")val it source.getLines()it.next()//跳过第一行wh…