news 2026/3/29 12:04:41

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

作者头像

张小明

前端开发工程师

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/3/27 7:10:43

Zotero PDF2zh插件:英文文献高效阅读的终极解决方案

Zotero PDF2zh插件:英文文献高效阅读的终极解决方案 【免费下载链接】zotero-pdf2zh PDF2zh for Zotero | Zotero PDF中文翻译插件 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-pdf2zh 还在为阅读英文文献而头疼吗?每天花费大量时间在词…

作者头像 李华
网站建设 2026/3/26 22:00:09

Sigma文件管理器终极指南:免费开源工具打造高效数字工作空间

在日常使用电脑时,你是否曾为杂乱无章的文件管理而烦恼?传统文件管理器功能单一、界面陈旧,难以满足现代用户对高效管理的需求。Sigma文件管理器作为一款免费开源的跨平台工具,正在重新定义现代文件管理的标准。无论你是Windows还…

作者头像 李华
网站建设 2026/3/26 19:29:32

Obfuscar混淆工具完整使用指南

Obfuscar混淆工具完整使用指南 【免费下载链接】obfuscar Open source obfuscation tool for .NET assemblies 项目地址: https://gitcode.com/gh_mirrors/ob/obfuscar Obfuscar是一个开源的.NET程序集混淆工具,通过重命名元数据来保护.NET应用程序中的敏感信…

作者头像 李华
网站建设 2026/3/28 11:31:23

ST7789显示屏终极指南:MicroPython快速上手全攻略

还在为嵌入式项目的显示界面发愁吗?今天我要向你推荐一个改变游戏规则的解决方案——基于MicroPython的ST7789显示屏驱动库。无论你是刚接触嵌入式开发的新手,还是正在寻找更高效显示方案的工程师,这个项目都能让你眼前一亮! 【免…

作者头像 李华
网站建设 2026/3/21 21:43:02

手机玩游戏总卡顿?关闭这2个设置,流畅度提升一倍

很多人都有过这样的体验:手机玩游戏时突然掉帧、操作延迟,明明不是老旧机型,却总被卡顿影响体验。其实不用急着换设备,关闭两个默认开启的 “性能消耗项”,就能让游戏流畅度大幅提升。一、关闭后台应用刷新&#xff0c…

作者头像 李华
网站建设 2026/3/27 3:15:10

如何快速修复和编辑Unreal Engine游戏存档:终极免费工具指南

如何快速修复和编辑Unreal Engine游戏存档:终极免费工具指南 【免费下载链接】uesave-rs 项目地址: https://gitcode.com/gh_mirrors/ue/uesave-rs 还在为游戏存档损坏而烦恼吗?想备份珍贵游戏进度却不知从何下手?ue save-rs开源工具…

作者头像 李华