我刚接到一个有趣的问题需要处理,但我没有找到一个简洁的解决方法。
我有两个基本数据结构来表示一个复杂的图形,声明如下:
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;
“raw”节点数组可以通过以下方式访问
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
当然,这会导致一些问题,因为“偏移量”值变得不正确,但这并不那么明显,因为数据结构的所有其他用途都有效(没有“真正”的对齐问题)。
这归结为我的问题 - 当偏移量不能表示为整数个元素时,您是否看到了一种简洁的方法来增加指针?
如果找到一种不需要过多转换的方法,将获得额外的奖励分数 :)。
sizeof(node_t)
的倍数,但它们之间的差异却不是倍数感到困惑。通常情况下,任何缓冲区地址都没有理由成为sizeof(node_t)
的倍数 - 结构体的对齐要求通常是所有成员中最大的对齐要求,而不是总大小。 - Steve Jessop