当前位置: 首页 > news >正文

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

在 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:上下文指针/句柄(数据地址)
http://www.cnnetsun.cn/news/442524.html

相关文章:

  • TikTok API 开发指南:非官方接口的完整实践教程
  • 揭秘Dify附件ID校验机制:如何避免因ID缺失导致的系统故障
  • GLM-4.6V-Flash-WEB在智能客服中的图像理解应用场景
  • Dify Flask-Restx 版本部署难题全解析,99%开发者都会踩的3个坑
  • Dify隐藏功能曝光:如何批量提取多个Excel文件中的指定数据?
  • ECharts 实战:构建交互式数据可视化应用
  • GLM-4.6V-Flash-WEB模型训练数据来源及隐私保护机制
  • 零基础7天精通AhabAssistantLimbusCompany:智能游戏助手完整使用攻略
  • BiliTools AI视频摘要:开启高效知识获取新纪元
  • VideoMAEv2视频理解模型完整教程:从入门到精通
  • 如何使用Visual Paradigm AI Chatbot创建3D打印机UML状态机图:综合指南
  • 如何在iOS中使用UIViewController的生命周期方法?
  • iOS - UIViewController 生命周期
  • Dify DOCX处理性能翻倍指南:99%的人都忽略的底层优化细节
  • .NET代码混淆终极指南:Obfuscar完整使用教程
  • VutronMusic:跨平台音乐播放器的智能管理体验
  • Obfuscar实战手册:5步打造坚不可摧的.NET代码防线
  • 数字永生争议:用IndexTTS 2.0复活逝者声音合适吗?
  • GLM-4.6V-Flash-WEB与JavaScript前端集成的方法论
  • MaterialDesignInXamlToolkit 终极指南:快速构建现代化 WPF 应用界面
  • GLM-4.6V-Flash-WEB在实时交互系统中的表现如何?
  • 2026年,别再盲目学AI了!这套90天“非编程”速成路径,专为小白设计,有人已靠它涨薪3倍(附内部学习资料)
  • image2csv终极指南:图像表格数据一键转CSV
  • 如何快速掌握TikTok数据采集:非官方API的完整实战指南
  • Dify调用描述生成接口频繁出错?3种高发场景及应对策略
  • 老年认知训练:记忆力游戏搭配AI语音互动
  • Dify触发器调试难?资深工程师教你用5个测试用例精准定位问题
  • GLM-4.6V-Flash-WEB能否替代传统OCR方案?实测告诉你答案
  • access_token频繁失效?Dify平台异常处理实战经验分享
  • GLM-4.6V-Flash-WEB在医疗影像初步筛查中的探索性应用