我正在努力实现共享内存缓冲区而不破坏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()可能在操作期间被多次调用,并且就性能和内存碎片而言,遍布整个地方的堆分配可能非常糟糕。一个明显的解决方案是分配一个适当大小的公共共享内存缓冲区,然后将其作为参数传递给foo()和bar(),BYOB样式:
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!
// ...
}
我想我现在有两个问题:
- 如何实现不违反别名规则的共享公共暂存内存缓冲区?
- 即使上面的代码确实违反了严格的别名规则,别名也没有“伤害”。因此,任何理智的编译器都可以生成(优化的)代码,但仍会让我遇到麻烦吗?
由于
答案 0 :(得分:3)
实际上,你所写的并不是严格的别名冲突。
C ++ 11规范3.10.10说:
如果程序试图通过除了其中一个之外的glvalue访问对象的存储值 以下类型行为未定义
因此导致未定义行为的是访问存储的值,而不仅仅是创建指向它的指针。你的例子没有违反任何规定。它需要做下一步:浮动badValue = smem [0]。 smem [0]从共享缓冲区中获取存储的值,从而产生别名冲突。
当然,在设置之前,你不会只是抓住smem [0]。你要先写信给它。分配给相同的内存不会访问存储的值,因此没有任何关联但是,在对象仍处于活动状态时写入对象的顶部是非法的。为了证明我们是安全的,我们需要3.8.4的对象寿命:
程序可以通过重用对象占用的存储空间来结束任何对象的生命周期 使用非平凡的析构函数显式调用析构函数以获取类类型的对象。对于一个对象 对于具有非平凡析构函数的类类型,程序不需要显式调用析构函数 在重新使用或释放对象占用的存储之前; ...... [继续关于不调用析构函数的后果]
你有一个POD类型,所以很简单的析构函数,所以你可以简单地声明“int对象都在他们的生命周期结束时,我正在使用浮动空间。”然后,您可以将该空间重用于浮点数,并且不会发生别名冲突。
答案 1 :(得分:1)
将一个对象解释为一个字节序列总是有效的(即不一个别名冲突将任何对象指针视为指向chars数组的第一个元素的指针),你可以在任何一块足够大且适当对齐的记忆中构造一个物体。
因此,您可以分配大量char
个s(任何签名),并找到alignof(maxalign_t)
处的偏移量;现在,您可以在那里构建适当的对象后将该指针解释为对象指针(例如,在C ++中使用placement-new)。
你当然必须确保不要写入现有对象的记忆;事实上,对象的生命周期与代表对象的内存发生的情况密切相关。
示例:
char buf[50000];
int main()
{
uintptr_t n = reinterpret_cast<uintptr_t>(buf);
uintptr_t e = reinterpret_cast<uintptr_t>(buf + sizeof buf);
while (n % alignof(maxalign_t) != 0) { ++n; }
assert(e > n + sizeof(T));
T * p = :: new (reinterpret_cast<void*>(n)) T(1, false, 'x');
// ...
p->~T();
}
请注意,malloc
或new char[N]
获取的内存总是对齐以进行最大对齐(但不是更多,您可能希望使用过度对齐的地址)。< / p>
答案 2 :(得分:0)
如果使用union来保存int和float变量,那么可以通过严格别名来传递。有关这方面的更多信息请参阅 http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
另见以下文章。
http://blog.regehr.org/archives/959
他提供了一种使用工会来做到这一点的方法。