我正在尝试实现一个固定的内存池(第一个免费列表) 我的结构是:
struct mempool {
void* data;
int offset;
};
data
分为8个字节的块,指向下一个偏移量的4个字节和4个字节的数据。 offset
指向第一个空闲块。我试图了解为什么通过以下方式访问第一个块:
int* address = (int*)((int)&pool->data + pool->offset);
尤其是(int)&pool->data
部分。池->数据不是指针吗?为什么我需要它的地址来执行算术运算并移至偏移量?
答案 0 :(得分:2)
我试图理解为什么通过以下方式完成对第一个块的访问:
int* address = (int*)((int)&pool->data + pool->offset);
尤其是
(int)&pool->data
部分。pool->data
还不是 指针?
是的,pool->data
是一个指针。而且可以获取指针的地址,因此这并没有本质上的错误。在这种情况下,结果的类型为void **
。
此外,假设data
是struct mempool
的第一个成员,则&pool
将指向相同的地址。尽管后者具有不同的类型(struct mempool *
),但可能是因为代码执行了对类型int
的转换。
为什么我需要它的地址来执行算术运算并转移到 偏移量?
效果是相对于data
指针本身的位置而不是相对于其 target 的位置来计算地址。此外,对类型int
的强制转换建议以字节为单位测量偏移量。但是,这方面并不安全,因为不能保证类型int
足以支持从指针到int
到指针的往返转换。
这一切似乎与您对具有与数据相邻的元数据的池的特征保持一致,但仍不清楚data
指针的作用是什么。
总体而言,尽管我不相信所展示的最小代码的正确性或有效性。如果相对于给出的结构定义,实际上它达到了预期的目的,那么这种变化应该做得更好,更清楚:
int* address = (int*)((char *)&pool + pool->offset);
这避免了可以表示指针的整数类型的问题(尽管以intptr_t
的形式存在)。强制转换为char *
的事实是,指针算术是以指向类型的大小为单位执行的,而偏移量似乎以一字节为单位表示。
答案 1 :(得分:1)
您的代码似乎不正确。您要将pool->offset
添加到pool->data
字段的地址,而不是添加到存储在pool->data
字段中的地址。我建议像这样修复:
int* address = (int *)pool->data + pool->offset;
如果您的偏移量是4字节的块,或者像这样:
int* address = (int *)((char *)pool->data + pool->offset);
如果您的偏移量以字节为单位。
答案 2 :(得分:1)
pool->data + pool->offset
是不可能的,因为您不能对void
指针执行指针算术-这不是有效的C。指针算术还假定所有这些的基础类型是数组
&pool->data
给出指针本身的地址,该地址恰好是结构的地址。类型void**
。您也不能对此进行算术。
因此,这里的幼稚,糟糕的解决方案是将指针转换为int
,然后进行简单的加法。这也不起作用,因为不能保证int
能够保存指针的内容。应该使用uintptr_t
代替int
。
最后,只有在编译器已将其中存储的内容视为int*
类型的情况下,才可以通过int
访问该内存块然后取消引用。如果不是,它将调用未定义的行为What is the strict aliasing rule?。
摘要:这是一个非常有问题的代码,并且有很多更好的方法来实现它。