将易失性数组转换为非易失性数组
Cast volatile array to non volatile array
我有一个全局 volatile unsigned char 数组 volatile unsigned char buffer[10]
在中断中向其写入数据。我有一个函数,它接受一个 unsigned char * 并将该值存储到硬件 (EEPROM) void storeArray(unsigned char *array)
,在这个例子中是前三个值。像这样将易失性数组转换为非易失性数组是否安全?
store_array((unsigned char *) buffer);
看了下面的内容,不是很明白,但很关心:
6.7.3:5 If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
这会影响我的代码吗?
然后我有这个后续问题:缓冲区数组只有一段我想存储的数据(无法更改),对于这个例子,从第三个值开始。这样做是否合法?
store_array((unsigned char *) buffer + 3);
如果是,如果将 3
添加到数组中,转换会受到怎样的影响? BR,谢谢!
编辑:
@Cacahuete Frito 链接了一个非常相似的问题:
是的,您发布的标准引述恰好涵盖了您想要做的事情。通过进行转换,您假装数组中的对象是 unsigned char
,而实际上它们是 volatile unsigned char
,因此在函数内部,您是通过左值引用 volatile
对象没有 volatile
限定符。未定义的行为。
如果您无法更改函数 storeArray
,则必须先将数据从易失性数组复制到 non-volatile 数组,然后再将其传递给函数。
关于第二个问题:指针算法没问题,它只是将buffer
转换为unsigned char*
,然后将结果指针加3,指向buffer[3]
(但是资格错误)。
您已找到标准的正确部分,此代码会导致未定义的行为。
一个写了一些东西的函数 "to hardware" 可能应该有一个 volatile
-qualifier 参数,这取决于 "hardware" 是什么。如果它是一个 memory-mapped 寄存器,一个 DMA 缓冲区或 non-volatile 内存,那么参数肯定应该是 volatile unsigned char*
(或者可选地, volatile uint8_t*
这也被视为字符类型)。
详细信息:C 允许我们使用字符指针遍历任何数据块,C17 6.3.2.3/7:
When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
您引用的关于访问 "lvalue" 的部分是指通过与实际存储在该位置的指针类型不同的指针类型访问数据。说白了:不管你怎么投各种指针指向它,实际的数据还是保持原来的类型。
通常甚至不允许通过错误的指针类型访问数据,但字符访问也是 "strict aliasing rule"、C17 6.5/7:
的特殊例外
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
...
- a character type.
因此您可以通过字符指针访问任何类型的数据,但如果该指针不是 volatile-qualified,您将根据引用的部分调用未定义的行为,C17 6.7.3/5。
实际上,使用 non-volatile 指针类型可能会导致编译器以意想不到的方式优化访问。所以这不仅仅是理论上的 "language-lawyering",在实践中您可以在启用优化的情况下生成非常奇怪的代码。嵌入式系统中很多很难发现的错误都源于这样一个缺失 volatile
.
关于您的 follow-up 问题,转换和 buffer + 3
没有任何改变:您仍在处理没有 volatile
限定符的字符指针 - 相同类型。实际数据仍然是 volatile unsigned char
类型,因此您无法通过 unsigned char*
.
从函数访问它
如果数组在中断中发生变化,您需要提供一种机制来以原子方式访问和修改它。如果您不这样做,任何 RW 或 RMW 操作都可能不成功并且数据不一致。
您访问易失性数据也会使 f=unction 参数易变。 storeArray(volatile unsigned char *)
并且不需要强制转换。强制转换仅删除警告。即使您将 non-volatile 数据传递给它,它也会正常工作。
如您所见,您依赖 "undefined behavior"。但是,除其他外,取决于编译单元的分离(以及 "whole-program-optimization" (WPO) 之类的东西),它可能会起作用。在大多数情况下,编译器(至少是 gcc)不会 "smart enough" 来优化不同编译单元中跨函数的数组访问。也就是说,干净、安全和可移植的方法是复制数组,使 non-volatile 数组的值对编译器可见的易失性值的依赖性。
我有一个全局 volatile unsigned char 数组 volatile unsigned char buffer[10]
在中断中向其写入数据。我有一个函数,它接受一个 unsigned char * 并将该值存储到硬件 (EEPROM) void storeArray(unsigned char *array)
,在这个例子中是前三个值。像这样将易失性数组转换为非易失性数组是否安全?
store_array((unsigned char *) buffer);
看了下面的内容,不是很明白,但很关心:
6.7.3:5 If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
这会影响我的代码吗?
然后我有这个后续问题:缓冲区数组只有一段我想存储的数据(无法更改),对于这个例子,从第三个值开始。这样做是否合法?
store_array((unsigned char *) buffer + 3);
如果是,如果将 3
添加到数组中,转换会受到怎样的影响? BR,谢谢!
编辑:
@Cacahuete Frito 链接了一个非常相似的问题:
是的,您发布的标准引述恰好涵盖了您想要做的事情。通过进行转换,您假装数组中的对象是 unsigned char
,而实际上它们是 volatile unsigned char
,因此在函数内部,您是通过左值引用 volatile
对象没有 volatile
限定符。未定义的行为。
如果您无法更改函数 storeArray
,则必须先将数据从易失性数组复制到 non-volatile 数组,然后再将其传递给函数。
关于第二个问题:指针算法没问题,它只是将buffer
转换为unsigned char*
,然后将结果指针加3,指向buffer[3]
(但是资格错误)。
您已找到标准的正确部分,此代码会导致未定义的行为。
一个写了一些东西的函数 "to hardware" 可能应该有一个 volatile
-qualifier 参数,这取决于 "hardware" 是什么。如果它是一个 memory-mapped 寄存器,一个 DMA 缓冲区或 non-volatile 内存,那么参数肯定应该是 volatile unsigned char*
(或者可选地, volatile uint8_t*
这也被视为字符类型)。
详细信息:C 允许我们使用字符指针遍历任何数据块,C17 6.3.2.3/7:
When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
您引用的关于访问 "lvalue" 的部分是指通过与实际存储在该位置的指针类型不同的指针类型访问数据。说白了:不管你怎么投各种指针指向它,实际的数据还是保持原来的类型。
通常甚至不允许通过错误的指针类型访问数据,但字符访问也是 "strict aliasing rule"、C17 6.5/7:
的特殊例外An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
...
- a character type.
因此您可以通过字符指针访问任何类型的数据,但如果该指针不是 volatile-qualified,您将根据引用的部分调用未定义的行为,C17 6.7.3/5。
实际上,使用 non-volatile 指针类型可能会导致编译器以意想不到的方式优化访问。所以这不仅仅是理论上的 "language-lawyering",在实践中您可以在启用优化的情况下生成非常奇怪的代码。嵌入式系统中很多很难发现的错误都源于这样一个缺失 volatile
.
关于您的 follow-up 问题,转换和 buffer + 3
没有任何改变:您仍在处理没有 volatile
限定符的字符指针 - 相同类型。实际数据仍然是 volatile unsigned char
类型,因此您无法通过 unsigned char*
.
如果数组在中断中发生变化,您需要提供一种机制来以原子方式访问和修改它。如果您不这样做,任何 RW 或 RMW 操作都可能不成功并且数据不一致。
您访问易失性数据也会使 f=unction 参数易变。
storeArray(volatile unsigned char *)
并且不需要强制转换。强制转换仅删除警告。即使您将 non-volatile 数据传递给它,它也会正常工作。
如您所见,您依赖 "undefined behavior"。但是,除其他外,取决于编译单元的分离(以及 "whole-program-optimization" (WPO) 之类的东西),它可能会起作用。在大多数情况下,编译器(至少是 gcc)不会 "smart enough" 来优化不同编译单元中跨函数的数组访问。也就是说,干净、安全和可移植的方法是复制数组,使 non-volatile 数组的值对编译器可见的易失性值的依赖性。