news 2026/4/23 18:51:32

函数的变量是地址与函数变量是数值的区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
函数的变量是地址与函数变量是数值的区别

在 C/C++ 里,“变量是地址”和“变量是数值”的区别,本质上是:这个变量里保存的比特位被解释成“数据本身”还是被解释成“指向某个内存位置/代码位置的地址”。二者在语义、可做的运算、以及对程序行为的影响上差别很大。

下面用最常见的场景把它讲清楚。


1) 变量存“数值”(value variable)

intx=5;
  • x的内存单元里直接存放数值 5(例如二进制0b...0101)。
  • 你读x得到的就是 5。
  • 你写x = 7;改的是x 自己这块内存里的内容。

特点

  • 变量内容就是数据本身(整数、浮点、结构体等)。
  • 不需要再“跳转到别的地址”才能得到真正的数据。
  • 传参时(f(x))默认是拷贝值

2) 变量存“地址”(pointer / address variable)

intx=5;int*p=&x;
  • p的内存单元里存放的是x 的地址(例如0x2000_1000)。
  • p本身不是 5,它是“指向 5 所在位置”的一个引用。
  • *p才是访问这个地址里存放的数据,也就是 5。

关键:p*p完全不是一回事

  • p:地址(指针值)
  • *p:该地址处的数据(解引用)

特点

  • p里存的是一个“数”,但这个数被解释为内存地址
  • p做运算(例如p+1)通常是按类型步进(sizeof(*p))的地址运算。
  • *p = 7;改的不是p,而是p 指向的那块内存(这里就是改x)。

3) “函数参数是地址”和“函数参数是数值”的区别(最重要)

A. 参数是数值:函数拿到的是副本,改不了外部变量

voidf(inta){a=10;}intx=5;f(x);// x 仍然是 5
  • ax的拷贝。
  • a不影响x

B. 参数是地址(指针):函数可以改外部变量

voidg(int*a){*a=10;}intx=5;g(&x);// x 变成 10
  • a保存的是x的地址(地址本身也是“按值传递”进来的,但这个值恰好是地址)。
  • 通过*a可以修改到外部的x

结论:C 语言里“传引用”通常就是“把地址当作参数传进去”。


4) 函数指针:变量里存的是“代码地址”

你前面提到的XSpiPs_StatusHandler FunctionPtr就是典型:

  • FunctionPtr变量里存的是某个函数入口的地址(代码段地址)。
  • 当驱动写InstancePtr->StatusHandler = FunctionPtr;
    就是把“要回调的函数地址”保存起来。
  • 之后调用InstancePtr->StatusHandler(...)
    本质是跳转到这个地址执行代码

这和CallBackRef的区别非常明确:

  • CallBackRef:数据地址(告诉回调“上下文对象在哪”)
  • StatusHandler:代码地址(告诉驱动“发生事件时执行哪段代码”)

5) 一句话总结

  • 数值变量:保存“数据本身”,读出来就能用。
  • 地址变量(指针/句柄/函数指针):保存“某个东西的位置”(数据位置或代码位置),必须通过“解引用/调用/传给 API”才能真正作用到目标。

函数的变量是地址与函数变量是数值的区别

可以用一个非常实用的决策框架来判断:你是要“传数据本身”,还是要“让对方能访问/修改某块存储或执行某段代码”。


1) 用“数值”(value)的典型场景

A. 参数只是“配置/选择/常量”,不需要让函数改你的变量

例如:

  • 分频系数、模式位、寄存器 offset、长度、状态码
  • ByteCountDelayInit=10prescaler=256
XSpiPs_SetClkPrescaler(&SpiInstance,XSPIPS_CLK_PRESCALE_256);// 分频值是数值XSpiPs_SetDelays(&SpiInstance,10,10,10,10);// 延时字段是数值

原因:这些是命令参数,函数拿到副本就够了。

B. 数据很小,复制成本低,且你不希望被修改

例如int,u32, 小的枚举。

优点:简单、安全、不会出现指针悬空。


2) 用“地址/指针”(pointer)的典型场景

A. 需要让函数“修改/填充”你提供的存储(输出参数)

例如读寄存器、读数据、返回多个结果:

intread_reg(u32 addr,u8*val_out);

原因:C 只有值传递;要把结果写回调用者,就要传地址。

B. 数据量大,不想拷贝(尤其是 buffer / struct)

SPI 发送/接收 buffer 必须传指针:

XSpiPs_Transfer(&SpiInstance,SendBufPtr,RecvBufPtr,ByteCount);

原因:ByteCount可能很多字节,拷贝一份代价高且没必要;硬件 DMA/ISR 也必须知道 buffer 在哪。

C. 需要共享同一份对象状态(驱动实例、设备上下文)

例如XSpiPs *InstancePtr

  • 驱动要在InstancePtr里更新IsBusy、指针、计数器等
  • 所以必须传地址,不然改不到原来的实例

3)const指针:只读访问(非常推荐的习惯)

当你传的是“地址”,但你希望对方只读不改,就用const

intfoo(constu8*data,u32 len);// data 指向的内容不会被 foo 改

例如你自己封装的spi_write_then_read(const uint8_t* txbuf, ...)就是这个思路:发送缓冲不应被驱动改写。


4) 句柄/指针作为CallBackRef:什么时候用

当你使用中断/异步机制(SPI 中断完成才回调)时,回调函数需要知道“该操作哪一个对象/同步原语”。

  • StatusHandler:告诉驱动“发生事件时调用哪段代码”(函数地址)
  • CallBackRef:告诉这段代码“要操作谁”(数据地址/句柄)

典型用法

  • 裸机:传一个struct ctx指针,回调里置位done

  • FreeRTOS:传SemaphoreHandle_t/TaskHandle_t/QueueHandle_t

    • 回调里GiveFromISRNotifyGiveFromISR

什么时候用句柄当 CallBackRef?

  • 你上层是“等待某个 RTOS 对象”来同步的(信号量/通知/队列)
  • 你希望回调尽量短、在 ISR 内只做“唤醒/通知”

5) 函数指针(XSpiPs_StatusHandler)什么时候用

当你需要把行为/策略交给用户实现,驱动不能写死时,就用函数指针:

  • 传输完成时怎么通知?(打印?置位?发信号量?)
  • 出错时怎么处理?(重试?记录?报警?)

这属于“回调/多态”机制:同一套驱动可以接不同上层策略。


6) 一个最实用的“选择口诀”

你可以按下面 4 个问题判断:

  1. 我需要函数改我的变量/填我的缓冲吗?

    • 需要 → 传地址(指针)
    • 不需要 → 传数值
  2. 数据是否很大(buffer/结构体)?

    • 很大 → 传地址(避免拷贝)
    • 很小 → 传数值
  3. 我是在做异步/中断回调吗?

    • 是 → 用函数指针(回调)+ CallBackRef(上下文)
    • 否 → 可能只用普通函数返回值/输出参数
  4. 对象是否需要跨函数持续保存状态?(driver instance)

    • 是 → 传对象地址(实例指针)
    • 否 → 传数值即可

7) 对你当前 SPI 代码的直接对照

  • &SpiInstance:必须是地址(驱动要改它内部状态)
  • XSPIPS_CLK_PRESCALE_25610:是数值(配置参数)
  • SendBufPtr/RecvBufPtr:必须是地址(buffer)
  • StatusHandler:函数指针(代码地址)
  • CallBackRef:上下文指针/句柄(数据地址)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 13:03:02

GitHub Desktop中文界面一键切换:告别英文困扰的终极解决方案

GitHub Desktop中文界面一键切换:告别英文困扰的终极解决方案 【免费下载链接】GitHubDesktop2Chinese GithubDesktop语言本地化(汉化)工具 项目地址: https://gitcode.com/gh_mirrors/gi/GitHubDesktop2Chinese 还在为GitHub Desktop的全英文界面而头疼吗&a…

作者头像 李华
网站建设 2026/4/23 13:00:20

GLM-4.6V-Flash-WEB在多语言环境下的表现评估

GLM-4.6V-Flash-WEB在多语言环境下的表现评估 在全球化加速的今天,用户不再满足于单一语言的信息交互。从一份日文产品说明书到一张西班牙语菜单的照片,再到社交媒体上混杂中英文的截图,现实世界中的图像内容早已突破语言边界。如何让AI真正“…

作者头像 李华
网站建设 2026/4/23 13:00:56

使用大数据分析平台

使用大数据分析平台撰写博文 一、引言 在数字化时代,大数据分析已成为推动创新和决策的核心工具。通过利用大数据分析平台(如Apache Hadoop或Spark),我们可以高效收集关于大数据重要性和应用领域的最新数据。例如,全…

作者头像 李华
网站建设 2026/4/22 14:52:23

触发器响应延迟高达30秒?3个真实压力测试案例揭示性能瓶颈根源

第一章:触发器响应延迟高达30秒?现象解析与问题定位在现代事件驱动架构中,触发器(Trigger)作为连接事件源与处理逻辑的核心组件,其响应延迟直接影响系统的实时性。当观测到触发器响应延迟高达30秒时&#x…

作者头像 李华
网站建设 2026/4/23 12:38:35

掌握这5种Dify DOCX处理优化方法,轻松应对高负载场景

第一章:Dify DOCX处理优化的核心价值在现代企业级文档自动化场景中,高效、精准地处理DOCX文件已成为提升业务流程效率的关键环节。Dify通过深度集成文档解析与AI工作流引擎,显著优化了DOCX文件的读取、修改与生成能力,使非结构化文…

作者头像 李华