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

news2024/11/26 10:10:59

在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: 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.coloradmin.cn/o/2247782.html

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

相关文章

C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现&#xff0c;那么接下来我们将进一步的学习二叉树&#xff0c;在此会先后学习到二叉搜索树、AVL树、红黑树&#xff1b;通过这些的学习将让我们更易于理解后面set、map、哈希等…

Leetcode647. 回文子串(HOT100)

链接 代码&#xff1a; class Solution { public:int countSubstrings(string s) {int res 0;for(int i 0;i<s.size();i){for(int j i,k i;j>0&&k<s.size();j--,k){if(s[j]!s[k])break;else res;}for(int j i,k i1;j>0&&k<s.size();j--…

ubuntu, 安装部署comfyui,记录2:下载模型GGuf及测试

0.清除工作流 1.安装manager 2024年最新ComfyUI汉化及manager插件安装详解&#xff01;_comfyui-manager-CSDN博客 ComfyUI Manager安装 转到ComfyUI的安装目录ComfyUI/custom_nodes; 使用git拉取ComfyUI Manager&#xff0c;git clone https://github.com/ltdrdata/Comf…

【Y20030006】基于php+mysql的课程学习网站的设计与实现(附源码 配置 文档)

网络购物商城的设计与实现 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 随着互联网的普及和在线教育的兴起&#xff0c;课程学习网站已经成为越来越多人获取知识和提升技能的重要途径。在这样的背景下&#xff0c;开发一个基于Laravel框架的课程学习…

O2O: (BOORL) Bayesian Design Principles for Offline-to-Online RL

ICML 2024 paper code Intro O2O如何避免悲观学习导致sample efficiency较低&#xff0c;亦或者乐观估计导致的performance drop。本文提出贝叶斯准则&#xff0c;指导在线学习过程中的探索和利用。通过构建一个Q值相关的信念分布&#xff0c;agent可以对不同策略的优劣有一个…

Hyper-V配置-cnblog

启用Hyper-V以在 Windows 10上创建虚拟机 &#xff08;1&#xff09;控制面板检查系统要求&#xff1a; 确保您的计算机符合 Hyper-V 的系统要求。通常情况下&#xff0c;您的计算机需要运行 Windows 10 专业版、企业版或教育版&#xff0c;并且具有启用了虚拟化技术的处理器。…

基础免杀 从.rsrc加载shellcode上线

.rsrc 段是PE文件中的一个特定部分&#xff0c;专门用来存储资源数据。这些资源通常包括图标、位图、字符串表、对话框、菜单、版本信息、字体等 具体的shellcode加载方式不在此探讨 在这使用传统的指针执行 WindowsAPI 需要用到如下API FindResource 获取指定资源的信息块…

Excel把其中一张工作表导出成一个新的文件

excel导出一张工作表 一个Excel表里有多个工作表&#xff0c;怎么才能导出一个工作表&#xff0c;让其生成新的Excel文件呢&#xff1f; 第一步&#xff1a;首先打开Excel表格&#xff0c;然后选择要导出的工作表的名字&#xff0c;比如“Sheet1”&#xff0c;把鼠标放到“She…

第四期书生大模型实战营——基础岛第4关-L1G4000-InternLM + LlamaIndex RAG 实践

Tutorial 基础任务 任务要求1&#xff08;必做&#xff0c;参考readme_api.md&#xff09;&#xff1a;基于 LlamaIndex 构建自己的 RAG 知识库&#xff0c;寻找一个问题 A 在使用 LlamaIndex 之前 浦语 API 不会回答&#xff0c;借助 LlamaIndex 后 浦语 API 具备回答 A 的能…

搭建文件服务器并使用Qt实现文件上传和下载(带账号和密码)

文章目录 0 背景1 搭建文件服务器2 代码实现文件上传和下载2.1 在pro文件中添加网络支持2.2 创建网络管理类2.3 文件上传2.4 文件下载 3 扩展&#xff08;其他方法实现文件上传和下载&#xff09;3.1 python3.2 npm3.3 ftp服务器 4 完整的代码 0 背景 因为需要使程序具备在远程…

【初阶数据结构和算法】leetcode刷题之设计循环队列

文章目录 一、实现循环队列1.大致思路分析2.循环队列的结构定义和初始化结构定义初始化 3.循环队列的判空和判满判空和判满难点分析判空判满 4.循环队列的入队列和出队列入队列出队列 5.循环队列取队头和队尾元素取队头元素取队尾元素 6.循环队列的销毁7.最后题解源码 一、实现…

llama-factory 系列教程 (七),Qwen2.5-7B-Instruct 模型微调与vllm部署详细流程实战

文章目录 介绍llama-factory 安装装包下载模型 微调模型数据集训练模型 微调后的模型推理 介绍 时隔已久的 llama-factory 系列教程更新了。本篇文章是第七篇&#xff0c;之前的六篇&#xff0c;大家酌情选看即可。 因为llama-factory进行了更新&#xff0c;我前面几篇文章的实…

矩阵的重复

重复时自身也算一次重复 r e p m a t ( r e p e a t repmat(repeat repmat(repeat m a t l a b ) matlab) matlab)重复矩阵函数 ( ( ( 对矩阵整体 ) ) ) r e p m a t ( a , m , n ) repmat(a,m,n) repmat(a,m,n)将矩阵纵向重复 m m m次&#xff0c;横向重复 n n n次 r e …

【三维重建】windows10环境配置tiny-cuda-nn详细教程

1. 前言 本人在复现water-splatting时&#xff0c;需要配置tiny-cuda-nn&#xff0c;与此同时&#xff0c;出现了很多问题&#xff0c;在此进行简单概述。 2.安装Pytorch 环境版本要求保持一致&#xff1a;CUDA(物理机)&#xff0c;Pytorch&#xff0c;CUDA Toolkit 注意这里…

Js-函数-03

函数定义 在java中我们为了提高代码的复用性&#xff0c;可以使用方法。同样&#xff0c;在JavaScript中可以使用函数来完成相同的事情。JavaScript中的函数被设计为执行特定任务的代码块&#xff0c;通过关键字function来定义。 <!DOCTYPE html> <html lang"en…

MySQL45讲 第29讲 如何判断一个数据库是不是出问题了?——阅读总结

文章目录 MySQL45讲 第二十九讲 如何判断一个数据库是不是出问题了&#xff1f;——阅读总结一、检测数据库实例健康状态的重要性二、常见检测方法及问题分析&#xff08;一&#xff09;select 1 判断法&#xff08;二&#xff09;查表判断法&#xff08;三&#xff09;更新判断…

IO多路复用(Linux epoll)

文章目录 一、IO多路复用介绍1. 缓存 I/O (各种IO模型缘起) 二、目前有哪些IO多路复用的方案三、关联基础知识1. 用户空间和内核空间2. 文件描述符fd 四、Linux IO多路复用 select五、Linux IO多路复用 epoll1. epoll 介绍2. epoll只提供三个函数ET模式与LT模式 3. demo验证 六…

【数据结构与算法】相交链表、环形链表(判断是否有环)、环形链表(返回入环节点)

主页&#xff1a;HABUO&#x1f341;主页&#xff1a;HABUO &#x1f341;如果再也不能见到你&#xff0c;祝你早安&#xff0c;午安&#xff0c;晚安&#x1f341; 1.相交链表 题目&#xff1a;给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表…

Move 合约部署踩坑笔记:如何解决 Sui 客户端发布错误Committing lock file

Move 共学活动&#xff1a;快速上手 Move 开发 为了帮助更多开发者快速了解和掌握 Move 编程语言&#xff0c;Move 共学活动由 HOH 社区、HackQuest、OpenBuild、KeyMap 联合发起。该活动旨在为新手小白提供一个良好的学习平台&#xff0c;带领大家一步步熟悉 Move 语言&#…

【C语言】野指针问题详解及防范方法

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;什么是野指针&#xff1f;&#x1f4af;未初始化的指针代码示例问题分析解决方法 &#x1f4af;指针越界访问代码示例问题分析解决方法 &#x1f4af;指向已释放内存的…