【问题标题】:How to store a variable at a specific memory location?如何将变量存储在特定的内存位置?
【发布时间】:2013-03-07 09:07:43
【问题描述】:

由于我对 C 比较陌生,因此我的项目之一必须使用以下内容: 我必须声明一些全局变量,每次程序在相同的内存地址运行时都必须存储这些变量。 我做了一些阅读,发现我将它声明为“静态”,它将存储在相同的内存位置。

但我的问题是:我能否指出程序在哪里存储该变量。 例如:int a 要存储在 0xff520000。这件事能不能做?我在这里搜索过,但没有找到任何相关的例子。如果他们是关于此的一些旧帖子,请分享链接。

提前谢谢大家。 劳伦蒂乌

更新:我使用的是 32uC

【问题讨论】:

  • 在嵌入式系统中,硬件寄存器位于特定地址是很常见的。但是,在普通 PC 上,无法预测变量的最终位置,并且您同样不能将变量放置在特定的内存位置,因为程序的内存映射不固定。这是用于嵌入式系统还是用于普通 PC?
  • 为什么地址很重要?无论如何,它是虚拟内存。
  • 您好,我必须执行以下操作。做一种32位uC的测试平台,但是通过OCD刷机后,平台应该有(运行)在UART通信上。假设我有一个计数器来计算程序运行了多少次,或者一个计数器存储了程序运行多少次没有错误,然后我的教授通过超级终端提供什么作为输入变量计数器所在的地址,为了获得它的价值。这就是我需要的......任何想法:

标签: c


【解决方案1】:

在您的 IDE 中,将通过一些链接器文件提供内存映射。它将包含程序中的所有地址。阅读 MCU 手册以查看哪些地址有有效的内存供您使用,然后为您的变量保留一些内存。您必须阅读特定开发平台的文档。

接下来,请注意,将变量映射到特定地址没有多大意义,除非它们是硬件寄存器或驻留在闪存或 EEPROM 中的非易失性变量。

如果这样一个内存位置的内容在执行过程中会发生变化,因为它是一个寄存器,或者因为你的程序包含一个改变 NVM 内存单元的引导加载程序/NVM 编程算法,那么变量必须是声明为易失性。否则编译器会在优化时完全破坏你的代码。

特定的编译器很可能有一种非标准的方式来在特定地址分配变量,例如 #pragma 或者有时是奇怪的非标准 @ 运算符。您可以在标准 C 中的固定位置分配变量的唯一明智方法是:

#define MY_REGISTER (*(volatile uint8_t*)0x12345678u)

其中 0x12345678 是其中 1 个字节所在的地址。一旦你有了这样的宏声明,你就可以把它当作一个变量来使用:

void func (void)
{
  MY_REGISTER = 1;  // write
  int var = MY_REGISTER;  // read
}

大多数情况下,您希望这些类型的变量驻留在全局命名空间中,因此是宏。但是如果你出于某种原因想要缩小变量的范围,那么跳过宏并在代码中手动访问地址:

void func (void)
{
  *(volatile uint8_t*)0x12345678u = 1; // write
  int var = *(volatile uint8_t*)0x12345678u; // read
}

【讨论】:

  • 谢谢。这就是我想要的。我听取了您的建议,发现以下内容:MEMORY iROM : ORIGIN = 0x00000000, LENGTH = 3072k iRAM : ORIGIN = 0xFEDC0000, LENGTH = 256k BURAM : ORIGIN = 0xFF760000, LENGTH = 16k
    我想我必须从我需要的 RAM 对吗?
  • 顺便问一下:我为什么使用像 0x12345678u 这样的地址?最后的你代表什么?
  • @Laurentiu 一个u(或U)后缀表示整型文字的类型为unsigned。它是为了保证使用缩进类型,我主要是出于旧习惯写的。默认情况下,C 源代码中的整数文字是(有符号)int 类型,但由于地址永远不能为负,因此使用有符号变量来存储它是没有意义的。通常,您永远不想在嵌入式系统中使用默认的int 类型。在某些情况下进行简单算术时,您可能希望使用 sintN_t 类型,其中 N=8、16 等。但大多数情况下,您只使用无符号变量。
  • 再次感谢。关于我刚刚看到的一个问题,我仍然有你的问题:我做了以下事情:#define counter ((volatile unsigned int)0xfedc0010)。然后在我的函数中我对变量做了一些操作。但是当我在调试模式下检查内存映射时,我看到在那个特定地址编译器还分配了另一个变量?所以从我的角度来看,这是一个真正的问题,因为两者都存储在相同的内存位置,这意味着一个人将覆盖彼此的值。那么有什么想法吗?
  • @Laurentiu 你需要知道你在做什么。除非链接器知道一个对象存储在那个位置,否则什么都不会起作用。 "You have to read the documentation of your specific development platform" 不仅仅是一个建议性声明,如果你不这样做并理解它,你的程序将无法运行。
【解决方案2】:

你可以用链接器脚本来做这种事情,这在嵌入式编程中很常见。

在 Linux 系统上,由于地址空间随机化,您可能永远不会获得相同的虚拟地址(一种安全功能,可避免依赖于了解您所描述的变量的确切位置的漏洞利用)。

如果它只是一个你想要的可重复指针,你也许可以用mmap 映射一个特定的地址,但这不能保证。

【讨论】:

  • 是的。我认为使用 mmap 将地址空间映射到文件可以解决问题。只是必须小心,以便 mmap(CONST_ADDR, ....., fd, CONST_FILE_OFFSET);永远不会失败(或在 diff-addr 上比 COSNT_ADDR 映射)。然后 CONST_ADDR 成为全局变量的地址,对该全局计数器所做的更改将反映在文件中,可以在下一个程序中进一步使用。
【解决方案3】:

就像其他答案中提到的那样 - 你不能。 但是,您可以有一个解决方法。如果可以在 main() 中初始化全局变量,则可以执行以下操作:

int addr = 0xff520000;

int main()
{
    *((int*)addr) = 42;
    ...
    return 0;
}

但是请注意,这在很大程度上取决于您的系统,如果在受保护的环境中运行,您很可能会遇到运行时崩溃。如果您在嵌入式/不受保护的环境中,这可以工作。

【讨论】:

  • 这个有效。但是你能告诉我如何找到存储变量的位置吗?因为我想我不能使用 uC 的一些随机内存区域?
  • @Laurentiu 在您的 IDE 中,将通过一些链接器文件提供内存映射。它将包含程序中的所有地址。阅读 MCU 手册以查看哪些地址有有效的内存供您使用,然后为您的变量保留一些内存。您必须阅读特定开发平台的文档。
【解决方案4】:

不,你不能明确告诉它在内存中存储变量的位置。主要是因为在现代系统上,系统在内存方面做了很多事情,这是你无法控制的。地址布局随机化是一件让这变得非常困难的事情。

【讨论】:

    【解决方案5】:

    如果您使用 XC8 编译器,则根据您的编译器。 你可以简单地写 int x @ 0x12 ;

    在这一行中,您在内存位置 0x12 中设置 x

    【讨论】:

      【解决方案6】:

      不在 C 级别。如果您使用汇编语言,您可以直接控制内存布局。但是 C 编译器会为您执行此操作。你真的不能乱来。

      即使使用汇编,这也只能控制相对布局。虚拟内存可以将其放置在任何(不)方便的物理位置。

      【讨论】:

        【解决方案7】:

        您可以使用一些编译器扩展来做到这一点,但这可能不是您想要做的。操作系统会处理你的记忆,并将东西放在它想要的地方。你怎么知道你想要的内存地址会映射到你的程序中呢?如果您在嵌入式平台上,请忽略本段中的所有内容,那么您应该阅读该平台/编译器的手册,或者至少在此处提及它,以便人们可以给出更具体的答案。

        此外,静态变量在程序运行时不一定具有相同的地址。许多操作系统使用与位置无关的可执行文件,并在每次执行时随机化地址空间。

        【讨论】:

          【解决方案8】:

          您可以声明一个指向特定内存地址的指针,并将该指针的内容用作我想的变量:

          int* myIntPointer = 0xff520000;
          

          【讨论】:

            猜你喜欢
            • 2011-10-03
            • 2021-11-14
            • 1970-01-01
            • 2018-11-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-24
            • 1970-01-01
            相关资源
            最近更新 更多