【发布时间】:2009-07-10 14:59:38
【问题描述】:
我刚刚处理了一个有趣的问题,但我没有找到解决它的好方法。
我有两个表示复杂图形的基本数据结构,声明如下:
typedef struct _node_t node_t;
typedef struct _graph_t graph_t;
struct {
/* Data fields omitted */
node_t * pNextByLevel;
node_t * pNextByProximity;
node_t * pNextByRank;
} node_t;
struct {
/* Data fields omitted */
size_t nNodes;
size_t nMaxNodes;
node_t * pFirstByLevel;
node_t * pFirstByProximity;
node_t * pFirstByRank;
} graph_t;
实际的节点紧跟在标题之后,因此通常使用“graph_t”创建
graph_t * pNewBuffer = calloc(1, sizeof(graph_t) + nMaxNodes * sizeof(node_t));
pNewBuffer->nMaxNodes = nMaxNodes;
并且使用
访问“原始”节点数组node_t * pNewBufferNodes = (node_t *) &pNewBuffer[1];
现在,有一个支持函数在减少节点数量的缓冲区上运行。它看起来像这样:
status_t reduce(graph_t** ppBuffer)
{
graph_t * pReplacement, * pOld = *ppBuffer;
size_t nRequired;
node_t * oldBuffer = (node_t *) &pOld[1];
/* complex calculation ultimately computes 'nRequired' */
pReplacement = realloc(pOld, sizeof(graph_t) + nRequired * sizeof(node_t));
if ( pReplacement != pOld )
{
int i;
node_t * newBuffer = (node_t *) &pReplacement[1];
ptrdiff_t offset = newBuffer - oldBuffer;
for ( i = 0; i < requiredNodes; i++ )
{
newBuffer[i].pFirstByLevel += offset;
newBuffer[i].pFirstBySimilarity += offset;
newBuffer[i].pFirstByRank += offset;
}
*ppBuffer = pReplacement;
}
}
现在,这已经很好地工作了很长时间。以上任何错误都源于我是凭记忆写的,我只是想解释一下这个想法。
现在让我感到困惑的是,当使用新模块的缩减功能时,输入没有“正确”对齐。当我检查地址时,我注意到以下属性:
((char *) newBuffer - (char *) oldBuffer) % sizeof(graph_t) == 0
((size_t) newBuffer) % sizeof(node_t) == 0
((size_t) oldBuffer) % sizeof(node_t) == 0
((char *) newBuffer - (char *) oldBuffer) % sizeof(node_t) == sizeof(node_t) / 2
这当然会导致一些问题,因为“偏移”值变得不正确,但由于数据结构的所有其他使用都有效(不存在“真正的”对齐问题),所以它并不那么明显。
这归结为我的问题 - 当偏移量不能表示为元素的整数时,您是否看到了一种增加指针的简洁方法?
找到不过度施法的方法的奖励积分:)
【问题讨论】:
-
我很困惑 oldBuffer 和 newBuffer 在转换为
sizeof(node_t)时是sizeof(node_t)的倍数,但它们的差异不是倍数。通常没有理由说明任何一个缓冲区的地址应该 是sizeof(node_t)的倍数 - 通常结构的对齐要求是任何成员的最大对齐要求,而不是总大小。 -
“这个功能已经很好地工作了很长时间”这一事实纯粹是运气。正如onebyone所说,2个缓冲区的地址没有理由应该是size_t(node_t)的倍数,它只需要是对齐要求的倍数。另请注意,您分配事物的方式也不能保证您的 node_t 数组,除非 graph_t 的对齐要求与 node_t 的要求相同或更严格。
-
对我所说的稍作更正:对于分配,它被指定为与小于结构大小的任何类型的最大对齐要求对齐,它实际上不必是成员.我说“指定”而不是“保证”,因为最后我听说 linux 内核与 gcc 就实际执行此操作的责任发生了争执。但是如果 sizeof(node_t) 是 16,那么在某个特定平台上所有足够大的分配都是 16 对齐的,这一点也不令人难以置信。可能是因为分配器的工作方式,而不是因为有一个 16 字节的类型,ofc。
标签: c c89 pointer-arithmetic