访问多字节易失性寄存器的一个字节

问题描述 投票:0回答:1

假设我们有一个指针

p
,类型为
volatile int*
。假设这是一个当前有效的指针,即从
*p
读取执行实现定义的操作,并且不是 UB。

如果程序尝试评估类似

*(volatile char*)p
的内容,会发生什么?假设
int
是 4 个字节,所以这个表达式看起来像“读取
int
指向的
p
的第一个字节”。至少,如果
int
是非易失性的,它就会这样做。但在这种情况下,它是不稳定的,所以这是否使它有可能成为 UB?

对于位于常规内存中的非易失性

int
,执行 1 字节加载指令可能会在硬件级别读取所有 4 个字节,然后屏蔽其中 3 个字节。但这对于软件来说是不可见的,除了可能对性能产生影响之外。但我不确定在 4 字节内存映射寄存器的情况下是否总是“可能”发生类似的情况。是否有任何平台在执行大于 1 字节的内存映射寄存器的 1 字节加载指令时会导致错误,因为该设备根本不支持除寄存器大小之外的任何大小的 I/O ? 以下问题是相关的,但答案并没有明确解决此问题:

使用 memcpy 和带有内存映射 I/O 的朋友

c undefined-behavior volatile memory-mapped-io
1个回答
0
投票

  1. volatile

    仅告诉编译器该对象容易产生副作用。这意味着编译器不能假设所有更改都是由可见代码路径进行的。因此,它在使用之前从其存储位置读取对象,并在进行任何更改后写入该对象。您可以访问任何对象(无论它是

    volatile
    还是不使用指向
    char
    的指针都没有关系。
    
    

但我不确定这样的事情是否总是有可能 发生在 4 字节内存映射寄存器的情况下。

如果内存位置是内存映射寄存器,您需要按照硬件手册(数据表、参考手册等)中描述的方式访问。某些硬件不接受不同于 32 位大小的访问。您
必须

以 32 位方式访问它。如果不这样做,硬件可能无法按预期运行(故障、重置、停顿、数据读/写不正确等)。 最简单的方法是使用临时变量。

volatile uint32_t *reg1 = (volatile uint32_t *)REG1ADDRESS; uint8_t read_byte(int byteno) { uint32_t tmpreg = *reg1; uint8_t *reg8b = (uint8_t *)&tmpreg; return regb[byteno]; } void write_byte(uint8_t val, int byteno) { uint32_t tmpreg = *reg1; uint8_t *reg8b = (uint8_t *)&tmpreg; regb[byteno] = val; *reg1 = reg }

	
© www.soinside.com 2019 - 2024. All rights reserved.