C 如何在成对结构中便携保存两个单词的内存?
我有一个总是成对出现的数据结构C 如何在成对结构中便携保存两个单词的内存?,c,bit-manipulation,pointer-arithmetic,C,Bit Manipulation,Pointer Arithmetic,我有一个总是成对出现的数据结构struct foo。现在,每个struct foo都携带一个指向另一个struct foo的指针: struct foo { struct foo *other_half; /* ... */ }; 由于我的程序需要很多(>1'000'000)struct foo,因此我迫切希望减少每个程序的大小。有没有办法去掉另一半指针,并通过其他方法找到一对结构foo的另一半?考虑一个数组结构foo[2]两个结构foo对齐到2*sizeof(struct f
struct foo
。现在,每个struct foo
都携带一个指向另一个struct foo
的指针:
struct foo {
struct foo *other_half;
/* ... */
};
由于我的程序需要很多(>1'000'000)
struct foo
,因此我迫切希望减少每个程序的大小。有没有办法去掉另一半
指针,并通过其他方法找到一对结构foo
的另一半?考虑一个数组结构foo[2]
两个结构foo
对齐到2*sizeof(struct foo)
或更大。请注意,foos[0]
与2*sizeof(struct foo)
或更大的值对齐,而foos[1]
仅与sizeof(struct foo)
对齐。您可以使用该信息来确定指向此类对齐的struct foo[2]
的随机struct foo*
是否指向第一个或第二个成员
要获得充分对齐的内存,请编写自定义分配器或使用C11aligned\u alloc
函数。请注意,实际上并不需要完全对齐内存,只需清除我们在other_half
中测试的位就足够了
一个简单的函数实现,在给定一个指向另一半的指针的情况下,查找一对struct foo
的另一半,如下所示:
struct foo *other_half(struct foo *half) {
if ((uintptr_t)half % (2 * sizeof *half) == 0)
return half + 1;
else
return half - 1;
}
但是,如果
sizeof(struct foo)
不是二的幂,则此函数的效率不是很高,因为它涉及缓慢的模运算。为了加快速度,考虑代码< > siZeof(StultFoO)< /C> >,其形式为2n·q。很容易看出,检查((uintptpr_t)half&(uintptpr_t)1就足够了,这是一种具有较少技巧的替代设计,但每个foo
使用单字节;如果将值与其他字段打包在一起,则使用位
#include
#include
struct foo {
char Index; // can be packed together with some other field
/* ... */
};
typedef struct foo pair[2];
void init_pair(pair *p){
for(size_t i = 0; i < sizeof(*p); i++){
(*p)[i].Index = (char)(i);
}
}
struct foo *pair_index(struct foo *piece, size_t index){
return &piece[index - (size_t)piece->Index];
}
struct foo *pair_other(struct foo *piece){
return pair_index(piece, piece->Index ^ 0x01);
}
遗憾的是,由于所有结构成员都是指针,因此无法提供额外的位。我的意思是,我可以重新利用其中一个指针的最低有效位,但由于指针没有指向它们应该指向的位置,这使得调试代码成了一件非常麻烦的事。执行这种对齐技巧似乎更简单。向结构添加额外的标志是不可能的还有一个问题。整个问题都是关于节省内存,添加更多数据不会减少内存。根据具体情况,对齐的内存可能会浪费/分割内存,例如,如果您需要用3个字节填充以使事情对齐。如果所有字段都是指针,并且在x64上,那么您(可能)也在浪费内存。(注意:我不是说这是一个错误。)“更好”的解决方案,只是一种可进行不同权衡的替代设计)我不是在浪费内存。请再次阅读技巧,这对数组中的一部分总是具有较高的对齐度,因此,如果使用自定义内存池,则在提供结构的大型数组中,最多只需丢弃一个条目。请注意,在C中,每个结构都包含隐式填充,以其最小对齐度乘以design(因此数组可以正常工作)使您的观点变得毫无意义。您能否通过提供第一个结构的奇偶校验来避免对齐要求?如果设置了half&-sizeof(*half)&sizeof(*half)
,(half+1)&-sizeof(*half)&sizeof(*half)
不是,反之亦然。因此它们是交替的,如果它们所在的块对齐,则块中的第一个结构将不设置该位。如果块中的第一个结构设置了该位,则可以翻转条件。@hacatu是,这就是为什么我说:“请注意,实际上并不需要将内存完全对齐,只需清除另一半中测试的位就足够了。”
#include
#include
struct foo {
char Index; // can be packed together with some other field
/* ... */
};
typedef struct foo pair[2];
void init_pair(pair *p){
for(size_t i = 0; i < sizeof(*p); i++){
(*p)[i].Index = (char)(i);
}
}
struct foo *pair_index(struct foo *piece, size_t index){
return &piece[index - (size_t)piece->Index];
}
struct foo *pair_other(struct foo *piece){
return pair_index(piece, piece->Index ^ 0x01);
}
int main(int argc, char const *argv[])
{
pair p;
init_pair(&p);
struct foo *first = &(p[0]);
struct foo *second = &(p[1]);
printf("%p %p\n", pair_index(first, 0), pair_index(first, 1));
printf("%p %p\n", pair_index(second, 0), pair_index(second, 1));
printf("%p %p\n", pair_other(second), pair_other(first));
return 0;
}