【问题标题】:Memory mapped address in C (how to dereference)C中的内存映射地址(如何取消引用)
【发布时间】:2014-04-03 21:10:34
【问题描述】:

我想假设 C 语言中的数组是微处理器中的一个内存区域,因此我可以在 PC 上编译一些代码。我编写了一个小程序来尝试使语法正确,但程序无法运行,当我更改访问变量的方式时,它要么崩溃,要么无法编译——太晚了,我不明白为什么.请问这是怎么回事?

// original code in microprocessor header that I need to change if I compile on the host
// BASE is simply a hex value that is later used as an address or a hex value
#define BASE (0x0000)
// used later in header like this (cannot change the way this is done)
#define OFFSET 0x0001
#define PERIPHERAL (BASE + OFFSET)
// also used like (also cannot change):
uint32_t var = PERIPHERAL | HEXMASK;

// here is how I intend to replace the uC specific code
// replace the BASE DEFINE with the next 2 lines of code:

// instead of writing to memory location, write to array of bytes instead, so declare it:    
uint8_t BASE_memory[4] = {0, 0, 0, 0};
// define BASE as hex value that can be used as drop-in replacement in either of the 2 uses shown above
#define BASE ((uint32_t)(BASE_memory))

// now test usage
// access contents of BASE_memory[0]
printf("contents of  BASE_memory[0] == %02x\n", *((uint32_t *)(BASE)));


// now I want to access PERIPHERAL, the second element of the array, i.e. BASE_memory[1]
printf("contents of  BASE_memory[1] == %02x\n", *((uint32_t *)(PERIPHERAL)));

【问题讨论】:

  • 你似乎在玩指针算法。当您尝试访问 BASE[1] 时,应该获取字节 4 到 8 还是字节 1 到 5?两者似乎都没有被分配,这是一个潜在的(但不太可能)错误,但是第二个有可能在某些架构上触发未对齐的访问错误,但在其他架构上则不会。为什么 (uint32_t *) ?
  • 抱歉,现在在 iPad 上输入并输入 BASE 而不是 BASE_memory,现在已编辑。微处理器是 32 位的,但在数据表中对微处理器内存映射的引用是以字节为单位的,因此 uint32_t 因为更容易看到我在硬件代码中正在做什么。我基本上希望能够在我的 PC 代码中使用 (BASE + hex offset) 结构来允许在 PC 上进行单元测试(不必费力地从制造商那里获得巨大的标头并进行更改)。非常感谢您看这个!
  • 您是要完成 8 位访问还是 32 位访问?因为现在您正在执行未对齐的(在第一种情况下或第二种情况下或两者兼有)32 位访问,而在第二种情况下包括未分配的(至少为此目的)内存,因为您正在访问 4 的第 5 个字节字节数组...
  • 啊,谢谢。我当然是个白痴。当我增加数组时,它运行正常,我可以通过 printfs 中的 32 位或 8 位访问来访问。我收到有关强制转换 #define BASE ((uint32_t)(BASE_memory)) 的警告
  • 非常感谢您的帮助,非常感谢 - 从您提到的几点开始,这里存在许多问题。当我跟进他们将 uint32_t 更改为 uintptr_t 时,一切都按预期工作。

标签: c dereference


【解决方案1】:

我认为您使用的是 64 位系统。

#include <stdint.h>

uint8_t BASE_memory[4] = {1, 2, 3, 4};

int func1()
{
    return *(uint32_t *) (uint32_t) BASE_memory;
}

int func2()
{
    return *(uint32_t *) (uintptr_t) BASE_memory;
}

这是func1 的程序集输出:

leaq    _BASE_memory(%rip), %rax
movl    %eax, %eax
movl    (%rax), %eax

这是func2 的程序集:

movl    _BASE_memory(%rip), %eax

您可以看到,如果您将地址转换为uint32_t,那么还有一个额外的步骤,将高位设置为零。然后地址是错误的,你会得到一个分段错误。这就是为什么你使用uintptr_tintptr_t 而不是uint32_t

【讨论】:

  • 好点,这也可能是个问题。不应将地址强制转换为太小而无法容纳它的类型。
  • 非常感谢你 - 太好了,直到现在我才知道 uintptr_t!这里实际上存在许多问题:在我遵循@ChrisStratton 的建议并删除了初始错误后,代码已正确编译,但随后在运行时崩溃。将相关数据类型更改为 uintptr_t 解决了它。为了将来参考,如果其他人有兴趣,我已经在我们公司的博客上详细介绍了用于在 Mac 上构建的双目标 STM32F4 Discovery 板,允许在主机上进行单元测试:blog.blackkitetechnology.com/?p=58
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 2023-03-03
相关资源
最近更新 更多