我正在尝试实现一个共享内存缓冲区,但不想违反C99的严格别名规则。
假设我有一些处理数据的代码,并需要一些“临时”内存来操作。我可以像这样编写它:
void foo(... some arguments here ...) {
int* scratchMem = new int[1000]; // Allocate.
// Do stuff...
delete[] scratchMem; // Free.
}
然后我有另一个函数,它会执行一些其他需要刮擦缓冲区的操作:
void bar(...arguments...) {
float* scratchMem = new float[1000]; // Allocate.
// Do other stuff...
delete[] scratchMem; // Free.
}
问题在于foo()和bar()可能在操作期间被调用多次,在各个地方进行堆分配可能会对性能和内存碎片化造成很大影响。一个显而易见的解决方案是一次性分配适当大小的共享内存缓冲区,然后将其作为参数BYOB-style传递给foo()和bar():
void foo(void* scratchMem);
void bar(void* scratchMem);
int main() {
const int iAmBigEnough = 5000;
int* scratchMem = new int[iAmBigEnough];
foo(scratchMem);
bar(scratchMem);
delete[] scratchMem;
return 0;
}
void foo(void* scratchMem) {
int* smem = (int*)scratchMem;
// Dereferencing smem will break strict-aliasing rules!
// ...
}
void bar(void* scratchMem) {
float* smem = (float*)scratchMem;
// Dereferencing smem will break strict-aliasing rules!
// ...
}
我现在有两个问题:
- 如何实现一个共享的通用划痕存储器,而不违反别名规则?
- 尽管上述代码确实违反了严格的别名规则,但使用别名并没有造成任何“伤害”。因此,任何理智的编译器都可能生成(优化的)代码,使我陷入麻烦吗?
谢谢。
scratchMem[i]
之前写入scratchMem[i]
将始终从别名的角度安全,因为我通过简单地写入它来结束了位于scratchMem + i
位置的对象生命周期。是这样吗?此外,我对“重用存储”一词感到困惑。它是如何定义的?例如:int64_t a = 0; ((int16_t*)a)[1] = 1;
。a
的生命周期是否已经在第二个赋值中结束?正确的“重用存储”是什么样子的? - rsp1984smem[0]
,我仍然认为可能会遇到与严格别名相关的问题。编译器可能会自由地发出修改以int类型解释的smem
中的内存的指令,从而在此之后通过后门更改smem[0]
的值,因为它假定内存不重叠。但愿能够得到澄清! - rsp1984void* mem = malloc(max(sizeof(A), sizeof(B)); A* a = new (mem) A; a->~A(); B* b = new (mem) B;
这样的代码,而不必担心构造函数的顺序被打乱。 - Cort Ammon