Je viens de recevoir un problème intéressant à régler, et je ne vois pas de moyen facile de le résoudre.Augmenter un pointeur struct avec la moitié de la taille de la structure
J'ai deux structures de données de base qui représente un graphique complexe, a déclaré quelque chose comme ceci:
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;
Les nœuds réels sont disposés immédiatement après l'en-tête, donc un « graph_t » est normalement créé avec
graph_t * pNewBuffer = calloc(1, sizeof(graph_t) + nMaxNodes * sizeof(node_t));
pNewBuffer->nMaxNodes = nMaxNodes;
et le réseau "brute" de noeuds est accessible avec
node_t * pNewBufferNodes = (node_t *) &pNewBuffer[1];
Maintenant, il existe une fonction de support qui fonctionne sur un tampon qui réduit le nombre de nœuds. Il ressemble à ceci:
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;
}
}
Maintenant, cela a fonctionné magnifiquement pendant longtemps. Toute erreur dans ce qui précède vient du fait que j'écris de mémoire, j'essaie juste d'expliquer l'idée. Ce qui me déroute en ce moment, c'est que lorsque j'utilise la fonction de réduction d'un nouveau module, l'entrée n'est pas "correctement" alignée. Lorsque j'examine les adresses, je note les propriétés suivantes:
((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
qui, bien sûr, fait un peu de problème puisque la valeur « décalage » est incorrecte, mais il est pas évident puisque toute autre utilisation des données les structures fonctionnent (il n'y a pas de problème d'alignement "réel").
Ce qui revient à ma question - Voyez-vous une façon intéressante d'incrémenter les pointeurs lorsque le décalage ne peut pas être exprimé en nombre entier d'éléments?
points de bonus pour trouver un moyen qui ne recourt pas à la coulée excessive :)
Je suis perplexe quant à ce qui se passe avec oldBuffer et newBuffer étant des multiples de 'sizeof (node_t)' quand ils sont lancés en 'size_t', et pourtant leur différence n'est pas un multiple. Il n'y a généralement aucune raison pour que l'adresse du tampon * soit * un multiple de 'sizeof (node_t)' - normalement l'exigence d'alignement pour une structure est la plus grande exigence d'alignement de tout membre, pas la taille totale. –
Le fait que "cela a fonctionné magnifiquement pendant longtemps" était une pure chance. Comme on le dit, il n'y a aucune raison pour que les adresses des 2 tampons soient un multiple de size_t (node_t), il suffit d'un multiple de l'exigence d'alignement. Notez également que la façon dont vous allouez les choses n'est pas garantie pour votre tableau node_t, à moins que l'exigence d'alignement de graph_t soit la même ou plus stricte que l'exigence de node_t. –
Légère correction à ce que j'ai dit: pour une allocation, il est spécifié d'être aligné sur la plus grande exigence d'alignement de tout type inférieur à la taille de la structure, il ne doit pas nécessairement être membre. Je dis «spécifié» plutôt que «garanti» parce que j'ai entendu dire que le noyau linux était en conflit avec gcc et qu'il était de sa responsabilité de le faire. Mais si sizeof (node_t) avait 16 ans, il n'est pas du tout invraisemblable que toutes les allocations suffisamment grandes soient alignées sur 16 sur une plateforme particulière. Probablement à cause de la façon dont l'allocateur fonctionne plutôt que parce qu'il y a un type de 16 octets, etc. –