我发现自己想要编写一个可以在易失性和非易失性存储器块上运行的例程。有点像:
void byte_swap (unsigned * block_start, size_t amount) {
for (unsigned * lp = block_start; lp < block_start + amount; lp++) {
*lp = htonl(*lp);
}
memcpy (block_start, some_other_address, amount);
}
(不是我的真实代码,而是一个例子)。
我遇到的问题是,如果我尝试使用指向易失性内存区域的指针,编译器会抱怨丢失volatile限定符。我可以抛弃volatile,但似乎这可能会使例程本身不安全,如果它试图缓存更改(或以前的读取),不是吗?
另一种选择是使例程本身采用unsigned volatile *
,但这需要我将所有非易失性调用者转换为易失性指针。
我认为第三个选项是制作两个完全相同的例程,区别仅在于volatile
关键字是否出现。太糟糕了。
是否有正确的方式来处理这个问题,如果是的话,它是什么?
作为一个主要相关的问题,预定义的例程(特别是memcpy)可以安全地使用volatile指针吗?如果我这样做会发生什么坏事?因为它不使用volatile void指针,所以必须自己重新滚动任何与内存相关的库例程似乎有点愚蠢。
答案 0 :(得分:2)
在处理内存映射IO时,只需要使用volatile
,即连续读取或写入同一内存地址的地方可以并且应该返回不同的值。如果您正在编写简单的共享内存,那么一个简单的正式切换机制就可以分配访问CPU或硬件设备(例如DMA控制器)就足够了。
还要注意,在非缓存内存上执行操作,或者更糟糕的是,在内存映射设备上执行操作会非常慢。在执行任何字节交换操作之前,复制到普通的未缓存内存可能要好得多。将大量数据复制到内存映射IO时,DMA可以保护大量的CPU周期。
volatile
往往会成为编译器优化的障碍,应该在必要时予以避免。
答案 1 :(得分:1)
虽然代码可能对非易失性对象持悲观态度(虽然给定了您的功能,但在这种情况下可能不是这样),您应该能够为volatile unsigned*
编码并传入unsigned*
。就像const
一样,您可以添加volatile
限定符,而不会显示(并且可能存在危险)。
您不必将所有指针更改为volatile
,您的非volatile
来电者可以保持原样。
虽然memcpy
未被定义为使用volatile
内存,但由于它的性质(它从一个地方读取内存并在其他地方写入一次),因此有问题的优化类{{1}抑制不适用。您可能会抛弃volatile
并使用volatile
而不会出现任何问题(除了那些已经存在易失性内存区域的问题)。