news 2026/4/6 2:48: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/5 18:12:37

【Dify私有化部署终极指南】:从零开始掌握企业级部署全流程

第一章:Dify私有化部署概述Dify 是一个开源的低代码 AI 应用开发平台,支持快速构建基于大语言模型的智能应用。私有化部署允许企业将 Dify 完整运行在自有服务器或私有云环境中,保障数据安全与系统可控性,适用于对合规性、隐私保护…

作者头像 李华
网站建设 2026/4/5 5:42:24

聚势华商·智创未来 | 华商北京校友会年会庆典圆满落幕

2026年1月3日,星河华商书院华商北京校友会年会庆典在北京西国贸大酒店1号宴会厅隆重举行。本次活动以“聚势华商智创未来”为核心口号,汇聚“聚势凝心凝共识,智创赋能启新程,华商同心传薪火,逐梦同行向未来”的奋进力量…

作者头像 李华
网站建设 2026/4/2 19:10:08

GLM-4.6V-Flash-WEB模型推理速度实测报告

GLM-4.6V-Flash-WEB模型推理速度实测报告 在当前AI应用加速落地的背景下,多模态大模型正从实验室走向真实业务场景。但一个现实问题始终存在:很多视觉语言模型虽然能力强大,却“跑得太慢”——一次图文问答动辄耗时半秒以上,用户还…

作者头像 李华
网站建设 2026/4/4 18:41:51

GLM-4.6V-Flash-WEB在电子签名验证中的安全性考量

GLM-4.6V-Flash-WEB在电子签名验证中的安全性考量 在电子合同被广泛用于贷款审批、远程签约和政务办理的今天,一个看似合法的PDF文件可能暗藏玄机:签名区域被人用PS替换过,或者同一份合同里出现了三处一模一样的“手写签名”。这类视觉层面的…

作者头像 李华