The near and far keywords(DSP & CCS)

本文详细介绍了C/C++编译器提供的近存取和远存取关键字,用于指定全局和静态变量的访问方式及函数调用。包括数据对象的近存取和远存取、函数调用的近存取和远存取,以及如何在不同场景下合理使用这些关键字以优化程序性能。

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

// 摘自: TMS320C6000 Optimizing Compiler v7.4 User`s Guide.pdf

====================================================================================================

The C6000 C/C++ compiler extends the C/C++ language with the near and far keywords to specify how global and static variables are accessed and how functions are called. 

Syntactically, the near and far keywords are treated as storage class modifiers. They can appear before, after, or in between the storage class specifiers and types. With the exception of near and far, two storageclass modifiers cannot be used together in a single declaration. The following examples are legal combinations of near and far with other storage class modifiers:

far static int x;
static near int x;
static int far x;
far int foo();
static far int foo();

near and far Data Objects


Global and static data objects can be accessed in the following two ways: 

near keywordThe compiler assumes that the data item can be accessed relative to the data page pointer. For example:

LDW  *+dp(_address), a0
far keywordThe compiler cannot access the data item via the DP. This can be required if the total amount of  program data is larger than the offset allowed (32K) from the DP. For example:
MVKL  _address, a1
MVKH  _address, a1

LDW   *a1, a0

Once a variable has been defined to be far, all external references to this variable in other C files or headers must also contain the far keyword. This is also true of the near keyword. However, you will get compiler or linker errors when the far keyword is not used everywhere. Not using the near keyword everywhere only leads to slower data access times.


If you use the DATA_SECTION pragma, the object is indicated as a far variable, and this cannot be overridden. If you reference this object in another file, then you need to use extern far when declaring this object in the other source file. This ensures access to the variable, since the variable might not be in the .bss section. For details, seeSection 6.9.6.


NOTE: Defining Global Variables in Assembly Code

         If you also define a global variable in assembly code with the .usect directive (where the variable is not assigned in the .bss section) or you allocate a variable into separate section using a #pragma DATA_SECTION directive; and you want to reference that variable in C code, you must declare the variable as extern far. This ensures the compiler does not try to generate an illegal access of the variable by way of the data page pointer. 

When data objects do not have the near or far keyword specified, the compiler will use far accesses to aggregate data and near accesses to non-aggregate data. For more information on the data memory model and ways to control accesses to data, see Section 7.1.5.1.


Near and far Function Calls


Function calls can be invoked in one of two ways:
near keyword   The compiler assumes that destination of the call is within ± 1 M word of the caller. Here the compiler uses the PC-relative branch instruction.

B _func


far keyword    The compiler is told by you that the call is not within ± 1 M word.
MVKL _func, al
MVKH _func, al
B _func


By default, the compiler generates small-memory model code, which means that every function call is
handled as if it were declared near, unless it is actually declared far.
For more information on function calls, see Section 7.1.6.

-------------------------------------------------------------------------------------------------------------------------------


<think>嗯,我现在需要解释E121错误,特别是由近和远地址混合引起的。首先,我得回忆一下E121是什么错误。根据以前学过的知识,E121是GCC编译器在编译内核或某些底层代码时可能会遇到的错误,尤其是在处理地址操作的时候。错误信息通常提到“unsupported mix of near and far addresses”,也就是近(near)和远(far)地址的混合使用不被支持。 那么,问题来了:近地址和远地址具体指什么?在x86架构中,特别是在16位模式下,内存是分段管理的。近指针指的是只有偏移量(offset),而默认使用当前的段寄存器(如CS、DS),而远指针则包含段地址和偏移量,可以访问其他段的内存。当程序中同时使用这两种指针,并且试图在不兼容的情况下混合使用它们时,编译器就会报E121错误。 接下来,用户可能需要知道为什么混合使用会导致问题。比如,可能尝试将一个远地址赋值给一个近指针变量,或者在函数调用中错误地传递了不同类型的指针。这会导致编译器无法正确生成代码,因为近和远指针的长度不同(近指针可能是16位,远指针是32位,段+偏移),操作它们的指令也不同。 然后,我需要考虑实际的例子。比如,在旧的内核代码中,可能会有类似这样的代码: ```c char near *np; char far *fp; np = fp; // 这里可能引发E121,因为试图将远指针赋给近指针 ``` 这种情况下,编译器无法自动转换,因为地址空间不同,可能导致截断,因此报错。 解决办法的话,可能需要显式地进行指针转换,或者确保使用同一类型的指针。例如,使用特定的宏或函数来处理远地址,或者在代码中统一使用远指针,尤其是在需要跨段访问的时候。另外,检查内存模型是否正确配置,比如是否应该使用大模式(large model)来支持更多的远指针。 不过,现在可能有人会问,这个问题在现代编程中还常见吗?因为现代的32位或64位系统通常使用平面内存模型,不再需要分段,所以近和远指针的概念已经很少用了。因此,E121错误主要出现在遗留代码或者某些嵌入式系统中,还在使用16位架构的情况下。 总结一下,回答需要涵盖E121的定义、近远地址的区别、混合使用的后果、示例以及解决方法,同时指出其应用场景主要在旧系统。这样用户就能全面了解这个问题了。不过,可能需要确认一些细节,比如GCC的具体版本和错误信息是否准确,但根据常见情况,这样的解释应该是合理的。</think>E121 错误是 GCC 编译器在处理某些底层代码(如内核或旧式分段架构程序)时可能遇到的错误,通常由**混合使用近(near)和远(far)地址**引发。以下是详细说明: --- ### **1. 近(near)与远(far)地址的背景** 在 **x86 16位分段内存模型**中,内存通过“段(Segment)”和“偏移(Offset)”访问: - **近地址(Near)**:仅包含偏移量(16位),默认使用当前段寄存器(如 `CS`、`DS`)。 例:`char near *ptr;`(只能访问当前段内的内存)。 - **远地址(Far)**:包含段地址和偏移量(32位,段:偏移),可跨段访问其他内存区域。 例:`char far *ptr;`(可访问任意段的内存)。 -- ### **2. 错误原因:混合使用近/远地址** 当代码中**隐式或显式混合两种指针类型**时,编译器无法安全处理,导致 E121 错误。常见场景: - 将远指针赋值给近指针变量(可能丢失段地址)。 - 函数参数类型不匹配(如期望传入 `far` 指针但传入了 `near` 指针)。 - 直接对两种指针进行运算或比较(地址空间不一致)。 **示例代码触发 E121**: ```c char near *np; char far *fp; np = fp; // 错误:试图将远指针(32位)赋给近指针(16位) ``` -- ### **3. 解决方法** #### **(1) 显式类型转换** 若确定操作安全,可使用强制转换(需谨慎,可能引发内存问题): ```c np = (char near *)fp; ``` #### **(2) 统一指针类型** - 若需跨段访问,统一使用 `far` 指针。 - 若内存操作限定在同一段内,统一使用 `near` 指针。 #### **(3) 调整内存模型** - 使用支持远指针的编译模式(如 `Large` 或 `Huge` 内存模型),允许默认使用 `far` 指针。 #### **(4) 检查函数声明** 确保函数参数和返回值的指针类型与实际使用一致: ```c // 错误声明 void func(char near *p); // 正确声明(若需处理远指针) void func(char far *p); ``` -- ### **4. 现代系统中的现状** 在 **32/64位保护模式**(现代操作系统)中,内存采用**平坦模型(Flat Memory Model)**,段寄存器作用弱化,不再需要区分近/远指针。因此,E121 错误主要出现在: - **遗留的 16 位系统**(如 DOS 程序)。 - **嵌入式开发**(某些特定架构)。 - **操作系统内核开发**(直接操作硬件分段机制时)。 -- ### **总结** E121 错误的本质是**分段内存模型下的指针类型不兼容**。解决需确保地址操作的一致性,或调整代码适配内存模型。在开发新系统时,通常无需关注此问题,但在维护旧代码或特定底层开发时仍需留意。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值