我最近看到一个代码库,我担心会违反对齐约束。我已经擦洗它以产生一个最小的例子,如下所示。简而言之,球员们是:
池。对于“有效”的某些定义,这是一个有效分配内存的类。 池保证返回一块符合所请求大小的内存。
OBJ_LIST 。此类存储同类的对象集合。一旦对象数量超过某个阈值,它就会将其内部表示从列表更改为树。 Obj_list 的大小是一个指针(64位平台上的8个字节)。它人口稠密的商店当然会超过这个。
聚合。此类表示系统中非常常见的对象。它的历史可以追溯到早期的32位工作站时代,它被“优化”(在同一个32位时代),因此使用尽可能少的空间。 聚合可以为空,也可以管理任意数量的对象。
在此示例中, Aggregate 项始终从 Pool 分配,因此它们始终对齐。此示例中唯一出现的 Obj_list 是 Aggregate 对象中的“隐藏”成员,因此始终使用 placement new 分配它们。以下是支持类:
class Pool
{
public:
Pool();
virtual ~Pool();
void *allocate(size_t size);
static Pool *default_pool(); // returns a global pool
};
class Obj_list
{
public:
inline void *operator new(size_t s, void * p) { return p; }
Obj_list(const Args *args);
// when constructed, Obj_list will allocate representation_p, which
// can take up much more space.
~Obj_list();
private:
Obj_list_store *representation_p;
};
这里是Aggregate。请注意,成员声明 member_list_store_d :
// Aggregate is derived from Lesser, which is twelve bytes in size
class Aggregate : public Lesser
{
public:
inline void *operator new(size_t s) {
return Pool::default_pool->allocate(s);
}
inline void *operator new(size_t s, Pool *h) {
return h->allocate(s);
}
public:
Aggregate(const Args *args = NULL);
virtual ~Aggregate() {};
inline const Obj_list *member_list_store_p() const;
protected:
char member_list_store_d[sizeof(Obj_list)];
};
这是我最关心的数据成员。这是初始化和访问的伪代码:
Aggregate::Aggregate(const Args *args)
{
if (args) {
new (static_cast<void *>(member_list_store_d)) Obj_list(args);
}
else {
zero_out(member_list_store_d);
}
}
inline const Obj_list *Aggregate::member_list_store_p() const
{
return initialized(member_list_store_d) ? (Obj_list *) &member_list_store_d : 0;
}
您可能会建议我们使用指向 Obj_list 类型的指针替换char数组,初始化为NULL或类的实例。这给出了正确的语义,但只是改变了内存成本。如果内存仍然非常宝贵(可能是,这是一个EDA数据库表示),那么在 obj_list 的指针替换char数组时,将会花费一个指针。聚合对象做有成员。
除此之外,我真的不想分散这里的主要问题,即对齐。我认为上面的构造是有问题的,但是在标准中找不到比'system / library' new 的对齐行为的模糊讨论更多的东西。 / p>
那么,上述构造是否会做什么,而不是偶尔造成管道失速?
编辑:我意识到有一些方法可以使用嵌入式字符数组替换方法。原建筑师也是如此。他们放弃了他们,因为记忆力很高。现在,如果我有理由触摸该代码,我可能会改变它。
但是,我的问题是关于这种方法固有的对齐问题,我希望人们能够解决这个问题。谢谢!
答案 0 :(得分:2)
好的 - 有机会正确阅读。您有一个对齐问题,并在作为Obj_list访问char数组时调用未定义的行为。很可能你的平台会执行以下三种操作之一:让你逃脱它,让你在运行时遭到惩罚或偶尔因总线错误而崩溃。
您可以通过以下方式解决此问题:
正如Arkadiy所说,让你的缓冲区成为Obj_list成员:
Obj_list list;
但你现在不想支付建设费用。您可以通过提供仅用于创建此实例的内联无操作构造函数来缓解此问题 - 正如默认构造函数所发布的那样。如果您遵循此路线,请强烈考虑调用dtor
list.~Obj_list();
在将新广告投放到此存储空间之前。
否则,我认为你留下了非便携式选项:要么依赖平台对错位访问的容忍度,要么使用编译器给你的任何非便携式选项。
免责声明:完全有可能我错过了工会或其他一些技巧。这是一个不寻常的问题。
答案 1 :(得分:1)
编译器将根据其默认值选择对齐,这可能在GCC / MSVC下最终为4个字节。
如果存在需要特定对齐的代码(SIMD / DMA),这应该只是一个问题。在这种情况下,您应该能够使用编译器指令来确保member_list_store_d已对齐,或者通过(alignment-1)增加大小并使用适当的偏移量。
答案 2 :(得分:1)
如果您想确保结构对齐,只需执行
即可// MSVC
#pragma pack(push,1)
// structure definitions
#pragma pack(pop)
// *nix
struct YourStruct
{
....
} __attribute__((packed));
确保在Aggregate
中对char数组进行1字节对齐答案 3 :(得分:1)
你能简单地在Aggregate中有一个Obj_list实例吗? ,
class Aggregate:public Lesser { ... 保护: Obj_list列表; };
我一定错过了什么,但我无法弄清楚为什么这很糟糕。
关于你的问题 - 它完全依赖于编译器。但是,大多数编译器默认情况下会在字边界处对齐每个成员,即使成员的类型不需要以这种方式对齐以进行正确访问。
答案 4 :(得分:0)
使用malloc或全局运算符new []分配char数组member_list_store_d
,其中任何一个都将为任何类型提供存储对齐。
编辑:再次阅读OP - 您不想为另一个指针付费。将在早上再次阅读。