У меня только что возникла интересная проблема, и я не вижу изящного способа ее решения.
У меня есть две базовые структуры данных, которые представляют сложный граф, объявленный примерно так:
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
, что, конечно, вызывает небольшую проблему, поскольку значение «смещения» становится неправильным, но это не так очевидно, так как все остальные виды использования структур данных работают (нет «реальной» проблемы выравнивания).
Что сводится к моему вопросу - вы видите аккуратный способ увеличения указателей, когда смещение не может быть выражено как целое число элементов?
Бонусные баллы за поиск пути, который не прибегает к чрезмерному кастингу:)