我有一个全局易失性无符号字符数组volatile unsigned char buffer[10]
,在中断期间将数据写入其中。我有一个采用无符号char *并将该值存储到硬件(EEPROM)void storeArray(unsigned char *array)
的函数,在本例中为前三个值。这样将易失性数组转换为非易失性数组是否安全?
storeArray((unsigned char *) buffer);
我阅读了以下内容,虽然我不太理解,但与我有关:
6.7.3:5如果试图通过使用具有非挥发限定类型的左值来引用以挥发限定类型定义的对象,则行为是不确定的。
这会影响我的代码吗?
然后我有一个后续问题:缓冲区数组只有一部分要存储的数据(不能更改),对于本示例,该数据以第三个值开头。做以下事情合法吗?
storeArray((unsigned char *) buffer + 3);
如果是的话,如果将3
添加到数组,转换会受到什么影响? BR,谢谢!
编辑: @Cacahuete Frito链接了一个非常相似的问题:Is `memcpy((void *)dest, src, n)` with a `volatile` array safe?
答案 0 :(得分:13)
是的,您发布的标准报价恰好涵盖了您要执行的操作。通过执行强制转换,您假装数组中的对象实际上是unsigned char
时是volatile unsigned char
,因此在函数内部,您是通过左值引用volatile
对象没有volatile
限定词。不确定的行为。
如果您无法更改函数storeArray
,则必须先将数据从易失性数组复制到非易失性数组,然后再将其传递给函数。
关于第二个问题:指针算法很好,它将简单地将buffer
转换为unsigned char*
,然后将3添加到结果指针中,指向buffer[3]
(但使用资格错误)。
答案 1 :(得分:6)
您已找到标准的正确部分,此代码导致未定义的行为。
根据“硬件”的不同,向“硬件”中写入内容的函数可能应具有一个stop3 <- c(
"()", "(3):", "article", "..", "etal.", "fig.", "natgenet",
"artical", "articleinitiallypublished"
)
library("quanteda")
## Package version: 1.4.3
# import the tm corpus as a quanteda corpus
data(crude, package = "tm")
reuters <- corpus(crude)
# example of removing tokens
(toks <- tokens("this () etal. is in artical fig. two", what = "fasterword"))
## tokens from 1 document.
## text1 :
## [1] "this" "()" "etal." "is" "in" "artical" "fig."
## [8] "two"
tokens_remove(toks, stop3)
## tokens from 1 document.
## text1 :
## [1] "this" "is" "in" "two"
# in this problem
dtm <- tokens(reuters, what = "fasterword") %>%
tokens_remove(c(stopwords("en"), stop3)) %>%
dfm()
topfeatures(dtm, 20)
## oil said opec prices mln last crude reuter
## 80 52 38 33 29 24 20 20
## dlrs saudi said. bpd one new official kuwait
## 19 18 17 16 15 14 14 13
## price market pct sheikh
## 12 12 12 11
限定符参数。如果它是内存映射寄存器,DMA缓冲区或非易失性存储器,则该参数肯定应该为volatile
(或可选地为volatile unsigned char*
,也应被视为字符类型)
详细信息:C允许我们使用字符指针C17 6.3.2.3/7遍历任何数据块:
将对象的指针转换为字符类型的指针时, 结果指向对象的最低寻址字节。的连续增量 结果,直到对象的大小,才产生指向对象剩余字节的指针。
您引用的有关访问“左值”的部分是指通过与实际存储在该位置不同的指针类型访问数据。说白了:无论您使用多少指针来指向它,实际数据都将保留其原始类型。
通常甚至不允许通过错误的指针类型访问数据,但是同样,字符访问是“严格别名规则” C17 6.5 / 7的特殊例外:
一个对象的存储值只能由具有以下之一的左值表达式访问 以下类型:
...
-字符类型。
因此,您可以通过字符指针访问任何类型的数据,但是如果该指针不是volatile限定的,则会根据引用的部分C17 6.7.3 / 5调用未定义的行为。
实际上,使用非易失性指针类型可能会导致编译器以意外方式优化访问。因此,这不仅是理论上的“语言法律”,您实际上还可以在启用优化的情况下生成非常奇怪的代码。如此缺失的volatile uint8_t*
是嵌入式系统中很多很难发现的错误。
关于后续问题,强制转换和volatile
不会改变:您仍在处理没有buffer + 3
限定符的字符指针-相同类型。实际数据仍为volatile
类型,因此您不能通过volatile unsigned char
从函数中访问它。
答案 2 :(得分:3)
如果数组在中断中发生更改,则需要提供一种访问和修改原子方式的机制。如果您不执行任何RW或RMW操作,可能会失败并且数据不一致。
您访问易失性数据也会使f = unction参数也易失。 storeArray(volatile unsigned char *)
,并且不需要强制转换。演员只删除警告。即使您将非易失性数据传递给它,它也将正常工作。
答案 3 :(得分:1)
您发现,您所依赖的是“未定义的行为”。但是,除其他因素外,取决于编译单元的分离(以及诸如“整个程序优化”(WPO)之类的东西),它可能会起作用。在大多数情况下,编译器(至少是gcc)不够“智能”,无法优化跨不同编译单元中函数的数组访问。也就是说,干净,安全和可移植的方法是复制数组,从而使编译器可以看到非易失性数组值对易失性值的依赖性。