将易失性数组转换为非易失性数组

ear*_*ing 14 c arrays volatile type-conversion

我有一个全局易失性无符号char数组volatile unsigned char buffer[10],在Interupt中将数据写入该数组。我有一个函数,该函数采用无符号char *并将该值存储到硬件(EEPROM)中void storeArray(unsigned char *array),在本例中为前三个值。像这样将易失性数组转换为非易失性数组是否安全?

store_array((unsigned char *) buffer);
Run Code Online (Sandbox Code Playgroud)

我阅读了以下内容,虽然我不太了解,但与我有关:

6.7.3:5如果尝试通过使用具有非挥发性限定类型的左值引用具有挥发性限定类型定义的对象,则该行为是不确定的。

这会影响我的代码吗?

然后我有一个后续问题:缓冲区数组只有一部分要存储的数据(不能更改),对于本示例,该数据以第三个值开头。做以下事情合法吗?

store_array((unsigned char *) buffer + 3);
Run Code Online (Sandbox Code Playgroud)

如果是的话,如果将演员表3添加到数组中,将如何影响演员表?BR,谢谢!

编辑:@Cacahuete Frito链接了一个非常类似的问题:`memcpy((void *)dest,src,n)`与`volatile`数组安全吗?

Rei*_*ica 13

是的,您发布的标准报价恰好涵盖了您要执行的操作。通过执行强制转换,您可以假装数组中的对象unsigned char实际上是它们的实际存在时间volatile unsigned char,因此在函数内部,您是volatile通过没有volatile限定符的左值引用对象。未定义的行为。

如果您不能更改函数storeArray,则必须先将数据从易失性阵列复制到非易失性阵列,然后再将其传递给函数。

关于第二个问题:指针算法很好,它将简单地转换buffer为an unsigned char*,然后将结果指针加3,指向buffer[3](但限定条件错误)。


Lun*_*din 6

您已找到标准的正确部分,此代码导致未定义的行为。

volatile根据“硬件”的不同,“向硬件”写东西的函数可能应具有-qualifier参数。如果它是内存映射寄存器,DMA缓冲区或非易失性存储器,则该参数肯定应该是volatile unsigned char*(或可选地,volatile uint8_t*也应被视为字符类型)。


详细信息:C允许我们使用字符指针C17 6.3.2.3/7遍历任何数据块:

当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。结果的连续递增(直到对象的大小)会产生指向对象剩余字节的指针。

您引用的有关访问“左值”的部分是指通过与实际存储在该位置不同的指针类型访问数据。明确地说:无论您使用多少指针来指向它,实际数据都将保留其原始类型。

通常甚至不允许通过错误的指针类型访问数据,但是字符访问又是“严格别名规则” C17 6.5 / 7的特殊例外:

对象的存储值只能由具有以下类型之一的左值表达式访问:
...-
字符类型。

因此,您可以通过字符指针访问任何类型的数据,但是如果该指针不是volatile限定的,则会根据引用的部分C17 6.7.3 / 5调用未定义的行为。

实际上,使用非易失性指针类型可能会导致编译器以意外方式优化访问。因此,这不仅是理论上的“语言法律”,您实际上还可以在启用优化的情况下生成非常奇怪的代码。嵌入式系统中的许多非常难以发现的错误都来自于这种缺失volatile


关于后续问题,强制类型转换和buffer + 3更改均不改变:您仍在处理不带volatile限定符的字符指针-相同类型。实际数据仍为类型volatile unsigned char,因此您无法通过函数从函数访问数据unsigned char*