现在,我有以下B树的代表:
#define PLUM_BTREE_MIN_COUNT 85
#define PLUM_BTREE_MAX_COUNT 170
struct bentry {
size_t key;
size_t value;
};
struct btree {
size_t count; // number of entries being used
struct bentry entries [PLUM_BTREE_MAX_COUNT];
struct btree *children[PLUM_BTREE_MAX_COUNT + 1];
};
以下操作非常常见:
struct btree src, dest;
size_t from, to, count;
// initialized somehow
// this manipulates elements, say, 80..100
memcpy(dest->entries + to ,
src ->entries + from,
count * sizeof (struct bentry));
// and then this manipulates elements 81..101
memcpy(dest->children + to + 1,
src ->children + from + 1,
count * sizeof (struct btree *));
这两个memcpy
电话真让我烦恼。我希望我可以将子指针和条目存储在内存中交错,例如,像这样:
struct btree;
struct bslot {
struct btree *child;
struct bentry entry;
};
struct btree {
size_t count;
struct bslot slots[PLUM_BTREE_MAX_COUNT];
struct btree *child;
};
如果对齐和填充从来不是问题,那么对于任何struct btree foo
,都可以保证:
foo.slots[PLUM_BTREE_MAX_COUNT].child == foo.child
所以我可以使用一个memcpy
来电:
memcpy( &(dest->slots[to ].entry),
&(src ->slots[from].entry),
count * sizeof (struct bslot) );
但是,当然,对齐和填充有时也是一个问题。有没有办法将子指针和条目存储在内存中,保证连续子指针之间的距离始终是固定常量,如下图所示?
----------------------------------------------
| p0 | e0 | p1 | e1 | ... | pn | en | p{n+1} |
----------------------------------------------
| | | | | | | | |
w x w+k x+k w+2k y z y+k z+k <--- addresses
答案 0 :(得分:0)
我相信你想使用打包的结构。如何实现这取决于您使用的编译器。在gcc中,你通过这样定义它来创建一个打包的结构:
struct __attribute__((__packed__)) btree
{
size_t key;
size_t value;
};
这样做是告诉编译器不要在内存边界上对齐结构,这样如果你创建了这些结构的数组,那么数组的大小就是numberOfStructs * exactSizeOfStruct(我的意思是你的大小)如果你把所有struct的内部元素加起来,或者sizeof()
)。任何指针算法都可以正常工作。
但是,这不能在GCC之外移植,必须使用预处理器宏进行检查。
答案 1 :(得分:0)
一种解决方案是在btree上使用struct btree __attribute__((__packed__)) {...};
(或类似于#ifdef __GNUC__
之类的东西,但对于不同的编译器选项),或者在没有struct padding的情况下编译,尽管这取决于如何便携式您希望如此。通过将结构声明为打包,编译器不会混淆对齐。否则,编译器或ABI在其实现中必须具有“优化”。如果无法阻止编译器更改编译环境中的结构,也可以痛苦地添加一些unsigned char pad[foo]
的手动填充。
参见资源:
http://www.catb.org/esr/structure-packing/
答案 2 :(得分:0)
通常最简单是最好的,这次也不例外。 (出于某种原因,昨天我只是没有看到它。)我本来可以使用一个灵活的数组成员:
// These macros must be #ifdef'd to account for multiple
// architectures. The following values work on my machine
// (x86-64 Linux).
#define PLUM_BTREE_NODE_SIZE 4096
#define PLUM_BTREE_MIN_COUNT 85
#define PLUM_BTREE_MAX_COUNT 170
struct btree;
struct bentry {
size_t key;
size_t value;
};
struct bslot {
struct btree *child;
struct bentry entry;
};
struct btree {
size_t count;
struct bslot slots[];
};
然后分配了适量的内存:
struct btree *tree = malloc( PLUM_BTREE_NODE_SIZE );
然后:
tree->slots[i].child
适用于从i
到0
的所有PLUM_BTREE_MAX_COUNT
tree->slots[i].entry
适用于从i
到0
的所有PLUM_BTREE_MAX_COUNT - 1