我知道编译器可以将多个非易失性加载合并为单个加载。这是否意味着内存映射函数可以创建未定义的行为?
例如:
int k = *p;
unmapFile(fm, fileHandle1);//the unmapped memory includes the address pointed to by p
//p is no longer valid, pointing to a virtual address that is unmapped
mapFile(fm, fileHanlde2);//p now points to an address somewhere in a different file
int j = *p;
//use k and j here
p 的两个取消引用是否可以合并为一个,从而导致代码行为不可预测?
另外,我知道文件映射函数不允许您指定映射的起始地址(至少在 posix 和 windows 中),但这是我能想到的最简单的假设。这种范例确实有用例。
C 本身没有定义任何此类函数,也没有说明如果定义它们可以或应该如何工作。因此,就 C 语言规范而言,与内存映射函数相关的任何事物都有未定义的行为。在问题的普遍性层面上没有其他答案。正如@AtsushiYokoyama 所观察到的,存在一些实际问题可能会阻止编译器合并 的两次读取
*p
,但这不应被视为任何给定编译器确实不会合并它们的保证。在使内存映射函数可用的编程和执行环境中,与其行为相关的大多数细节(就其定义而言)都是特定于实现的。以支持文件映射和 POSIX 函数的 POSIX 环境
mmap()
为例munmap()
,POSIX 表示:上面写着:
和
编译器有责任确保其执行的任何优化都保留了由所有相关规范组合定义的程序语义。我认为,对于 POSIX 环境(其中 您的
unmapFile()
对应于munmap()
并且 您的mapFile()
对应于 ) ,编译器无法mmap()
证明将 的两次读取组合起来是合理的*p
。这并不意味着没有编译器会这样做,但这确实意味着这样做的编译器可能会产生不符合 POSIX 的程序行为。因此,对于这种情况,我将加强上面的评论:存在一些实际问题,应该会阻止编译器将 的两次读取组合起来*p
。编译器在对这类代码进行优化时,如果不知道
unmapFile()
或 的实现mapFile()
(比如它们是外部函数),那么一般来说,两个通过指针的引用p
不会合并为一个。这是因为编译器无法保证这些函数不会产生影响对 的引用的副作用p
。另一方面,如果编译器确实
unmapFile()
知道和的实现mapFile()
(例如,如果它们是inline static
函数),并且确信这些函数不会影响对的引用p
,那么它可能会通过折叠引用p
。当然,正如你所指出的,如果是这样
volatile
,那就另当别论了。希望这有帮助!