I just got an interesting problem to take care of, and I see no neat way to solve it.
I have two base data structures that represents a complex graph, declared something like this:
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;
The actual nodes are laid out immediately after the header, so a "graph_t" is normally created with
graph_t * pNewBuffer = calloc(1, sizeof(graph_t) + nMaxNodes * sizeof(node_t));
pNewBuffer->nMaxNodes = nMaxNodes;
and the "raw" array of nodes is accessed with
node_t * pNewBufferNodes = (node_t *) &pNewBuffer[1];
Now, there is a support function that operates on a buffer that reduces the number of nodes. It looks something like this:
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;
}
}
Now, this has worked beautifully for a long time. Any errors in the above comes from the fact that I'm writing from memory, I'm just trying to explain the idea.
What baffles me right now is that when using the reduction function from a new module, the input is not "properly" aligned. When I examine the addresses, I note the following properties:
((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
which, of course, causes a bit of problem since the "offset" value becomes incorrect, but it's not so obvious since all other use of the data structures work (there is no "real" alignment problem).
Which boils down to my question - Do you see a neat way of incrementing the pointers when the offset can not be expressed as a whole number of elements?
Bonus points for finding a way that does not resort to excessive casting :)