__far和__near的小问题

本文介绍了在使用瑞萨RL78/G12单片机开发过程中遇到的关于__far和__near关键字的问题及解决方法。在不同优化模式下(small-size与normal-size),这些关键字对于函数原型、声明、调用以及存储数组的定义至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本随笔适用于瑞萨RL78/G12单片机开发

  在使用瑞萨CS+开发的时候,碰到了一个问题,同事同样的函数段能用,移植过来后发现不能用。

  经过排查,发现是__far和__near的问题。同事的工程编译时使用的是small-size优化,我使用的默认的normal-size优化,两种模式针对RAM和ROM地址使用的是不同的编译策略。

  

  在不调整优化模式的情况下,需要将函数原型、声明、调用还有对应的存储数组都确立为const __far uint16_t XXX。

 

  调整优化模式后,在调用时可以忽略这个问题。

 

转载于:https://www.cnblogs.com/PengH/p/6387162.html

<think>嗯,用户现在问的是关于__near、__far、__indirect这三个关键字的区别。之前他们问过一个关于函数声明的问题,里面提到了__indirect,现在可能是在继续深入理解嵌入式开发中的内存管理函数调用机制。 首先,我需要回顾一下之前的对话。之前用户问的是一个函数声明的解析,其中涉及到__indirect,可能是在特定的嵌入式环境中,比如8051或者类似的微控制器,因为这些架构经常有内存分段的限制。现在用户可能想了解这些关键字在不同情境下的应用,以及它们如何影响代码的生成内存的使用。 接下来,我需要确定这三个关键字的常见用途。在嵌入式系统中,尤其是像8051这样的架构,内存分为不同的段,比如nearfarnear通常指默认的数据段或代码段,访问速度较快,而far可能需要更长的指针来访问扩展内存。__indirect通常与函数调用有关,指示编译器生成间接调用的指令,比如通过函数指针来调用函数,适用于函数不在当前代码段的情况。 然后,我应该考虑用户可能的实际应用场景。他们可能正在开发或维护一个嵌入式项目,遇到了内存分段的问题,或者需要优化代码的空间速度。理解这些关键字可以帮助他们更好地管理内存函数调用,避免链接错误或运行时问题。 还需要考虑用户的背景。他们可能是有一定嵌入式开发经验的工程师,但对特定编译器的扩展或内存模型不太熟悉。因此,解释需要结合具体的架构编译器,比如Keil、IAR或SDK中的使用情况,以及这些关键字对代码生成的影响。 用户可能存在的深层需求是希望了解如何在不同内存模型下高效地编写代码,或者解决某些特定的编译错误或性能问题。因此,除了解释每个关键字的定义,还需要举例说明它们的使用场景优缺点,比如near访问快但空间有限,far访问慢但能寻址更大空间,indirect调用灵活但增加开销。 另外,需要注意这些关键字可能因编译器而异,不同的工具链可能有不同的实现方式。因此,提醒用户查阅具体编译器的文档是必要的,避免混淆通用概念特定实现。 最后,确保回答结构清晰,分点解释每个关键字,比较它们的区别,并总结使用建议,这样用户能够快速抓住重点并应用到实际项目中。</think>在嵌入式开发中,`__near`、`__far` `__indirect` 是 **编译器扩展关键字**,主要用于处理 **内存分段架构**(如 8051、DSP 或早期 x86)的地址空间限制。它们的核心区别在于 **寻址方式** **内存访问范围**。以下是详细对比: --- ### **1. `__near`(近指针/近函数)** - **定义** 默认寻址范围,通常指向 **当前内存段(Segment)内的地址**。 - **指针大小**:较小(例如 8 或 16 位),仅覆盖当前段内地址。 - **访问速度**:快(无需切换段寄存器)。 - **内存范围**:受限(如 8051 的 `DATA` 或 `IDATA` 区,通常 128/256 字节)。 - **典型用途** - 频繁访问的变量(如局部变量、堆栈变量)。 - 代码段内直接调用的函数(如 `CALL` 指令直接跳转)。 - **示例** ```c __near uint8 data_buffer[64]; // 变量存储在近内存区 void __near func(void); // 函数在近代码段 ``` --- ### **2. `__far`(远指针/远函数)** - **定义** 扩展寻址范围,允许访问 **跨段或外部扩展内存**。 - **指针大小**:较大(例如 24 或 32 位),包含段地址偏移量。 - **访问速度**:较慢(需切换段寄存器或长地址计算)。 - **内存范围**:更大(如 8051 的 `XDATA` 或外部 RAM,可寻址 64KB 以上)。 - **典型用途** - 访问外部扩展 RAM 或 Flash。 - 调用位于其他代码段的函数(如跨 ROM Bank 的代码)。 - **示例** ```c __far uint8 external_buffer[1024]; // 变量存储在远内存区 void __far func(void); // 函数在远代码段 ``` -- ### **3. `__indirect`(间接调用)** - **定义** 强制编译器生成 **间接调用指令**(通过函数指针跳转),而非直接调用。 - **指针类型**:函数指针,可能隐含 `far` 特性(取决于编译器)。 - **适用场景**: - 函数地址动态加载(如通过跳转表或重定位)。 - 跨段调用时兼容不同寻址模式。 - 安全关键代码防止直接地址劫持。 - **典型用途** - 动态库函数调用(如 Bootloader 跳转到 App)。 - 内存分页机制下的跨段函数调用。 - **示例** ```c void __indirect func(void); // 调用时会通过函数指针跳转 ``` --- ### **对比总结** | 特性 | `__near` | `__far` | `__indirect` | |--|---------------------------|---------------------------| | **地址范围** | 当前段内 | 跨段或扩展内存 | 不直接定义地址范围 | | **指针大小** | 小(如 8/16 位) | 大(如 24/32 位) | 依赖函数指针类型 | | **访问速度** | 快 | 慢 | 中等(需间接寻址) | | **内存管理** | 默认段 | 扩展段 | 动态地址加载 | | **主要用途** | 局部变量、高频访问数据 | 外部内存操作、跨段函数 | 动态调用、安全跳转 | | **编译器指令生成** | 直接 `CALL`/`MOV` | 长 `LCALL`/`MOVX` | 通过寄存器或内存间接跳转 | --- ### **典型场景示例** 1. **混合使用 `__near` `__far`** ```c __near uint8 local_data; // 快速访问的变量 __far uint8 *p_ext_ram = 0x8000; // 指向外部 RAM 的远指针 ``` 2. **`__indirect` 动态调用** ```c typedef void __far (*func_ptr)(void); func_ptr jump_table[] = {func1, func2}; // 跨段函数跳转表 void __indirect execute_function(uint8 index) { jump_table[index](); // 间接调用远函数 } ``` --- ### **注意事项** 1. **编译器依赖性** - 不同编译器(如 Keil、IAR、SDCC)的实现细节可能不同,需参考文档。 - 某些编译器可能将 `__far` `__indirect` 隐式关联(如远函数默认需间接调用)。 2. **性能权衡** - `__near` 适合性能敏感代码,但内存受限。 - `__far` `__indirect` 会增加代码大小执行时间。 3. **兼容性** - 在无分段内存的架构(如 ARM Cortex-M)中,这些关键字可能被忽略或无效。 如果需要进一步分析具体芯片或工具链的行为,可以提供更多上下文!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值