Ceci est juste comme la structure hack. Est-il valide selon la norme C?
// error check omitted!
typedef struct foo {
void *data;
char *comment;
size_t num_foo;
}foo;
foo *new_Foo(size_t num, blah blah)
{
foo *f;
f = malloc(num + sizeof(foo) + MAX_COMMENT_SIZE );
f->data = f + 1; // is this OK?
f->comment = f + 1 + num;
f->num_foo = num;
...
return f;
}
6 Réponses :
Oui, c'est complètement valide. Et je l'encouragerais fortement à ce que cela vous permet d'éviter des allocations supplémentaires inutiles (et la gestion des erreurs et la fragmentation de la mémoire qu'ils entraînent). D'autres peuvent avoir des opinions différentes.
Au fait, si vos données ne sont pas et allouer de l'espace pour la quantité de données dont vous avez besoin. La syntaxe Void * CODE> mais quelque chose que vous pouvez accéder directement, il est encore plus facile (et plus efficace car il permet d'économiser de l'espace et d'éviter les autres Indirection) Pour déclarer votre structure comme suit: P> [] code> est valable uniquement en C99, donc pour C89-Compatibilité, vous devez utiliser [1] code>, mais cela peut perdre quelques octets. P> < / p>
Comme vous le savez, mais cela vaut probablement la peine d'être signalé, l'élément de matrice flexible à la fin de la structure ne fonctionne que lorsque vous avez un élément de longueur variable. La question a deux éléments de longueur variable ( data code> et commentaire code>) et ne peut pas utiliser facilement la technique.
Dans ce cas, stockez le code> code> comme normal MALLOC () code> String.
FWIW, j'ai donné cette réponse avant que le champ de commentaire ait été ajouté. Avec des commentaires ajoutés, suivez les conseils de Donal.
C'est un ancien jeu, bien que la forme habituelle soit comme , puis allouez l'espace aussi gros que vous le souhaitez et utilisez la matrice comme si elle avait la taille souhaitée. P > It est em> valide, mais je vous encourageons à trouver un autre moyen si possible: il y a beaucoup de chance de baiser cela. p> p>
Cet ancien hack n'est pas * valide * (strictement) selon la norme.
Non, ce n'est pas le cas, mais i> travaillera avec une implémentation conforme de MALLOC code>. :: pense un peu sur les architectures de mémoire pathologiquement segmentées: presque i> tout MALLOC code>.
Il est valable comme des conséquences d'autres exigences de la norme, par exemple l'exigence selon laquelle si une structure correspond à la partie initiale d'une autre, l'accès à ces éléments via le 2 est compatible. C99 le rend explicitement pris en charge avec la notation de tableau flexible [] code> mais l'ancienne [1] code> méthode b> fonctionne nécessairement!
@dmckee, tout ce que pathologique ne peut pas être conforme. La norme nécessite que la mémoire renvoyée par MALLOC code> soit accessible en tant que tableau de non signé Char code> et qu'elle soit alignée de manière appropriée pour tout type.
@R: Le comité a en réalité discuté qu'à un moment donné, et décidé que cela n'était vraiment pas défini en C89. L'accès au-delà de la taille définie d'une matrice provoque un comportement non défini. Bien que ce soit rare, le compilateur pourrait i> faire la plage de contrôle et de lancer une sorte d'exception (ou autre) lorsque vous essayez d'accéder à la mémoire après quel que soit [0] code>.
@Jerry: Ce ne serait pas le compilateur, mais plutôt le temps d'exécution. (Le compilateur ne peut surtout pas le dire, car ce n'est pas et ne devrait pas être aussi intelligent.) Ce type de chose est utile de purifier et de purifier cependant, qui sont efficacement des roulements non standard. Vous ne voulez tout simplement pas que Normalement (puisque le succès de la performance est très douloureux).
@Donal: le compilateur génère le code, qui exécute au moment de l'exécution. Oui, le succès de la performance est assez grave que, dans la pratique, personne ne le fait, mais le comité a décidé que c'était autorisé de toute façon.
@Jerry: Mais dans certains cas, le temps d'exécution modifie le code compilé lorsqu'il est chargé pour ajouter le support pour la vérification des limites. (Vraiment. C'est exactement la façon dont le travail d'accès à la mémoire fonctionne.) Pour mon astuce, discutons de la nature de l'infinité des anges autorisée à danser sur la tête d'une épingle et quelle école de ballet ils sont allés ... ;-)
@Jerry: quoi que ce soit [1] code> est identique à * (& autre [0] +1) code>, qui est une déréférence de pointeur valide dans l'objet attribué. Je suis désolé de dire que le comité a du mal à lire ses propres documents.
@R: Il est syntaxiquement valide, mais produit toujours un comportement indéfini. Je ne serais pas trop rapide de décider que vous lisez le document plus précisément que le comité.
Oui, l'idée générale du piratage est valide, mais au moins comme je l'ai lu, vous ne l'avez pas mis en œuvre assez correctement. Cela vous avez beaucoup fait correctement: mais c'est faux: p> car casting OTOH, car vous allouez toujours puis l'allouate comme: p> et cela fonctionnera sans aucune manipulation de pointeur du tout. Si vous avez un compilateur C99, vous pouvez le modifier légèrement: p> et allouer: p> ceci a le supplément Avantage que la norme bénit réellement, cependant, dans ce cas, l'avantage est assez mineur (je crois que la version avec f code> est < code> foo * code>, le f + 1 + num code> est calculé en termes de Tailleof (foo) code> - c'est-à-dire que c'est équivalent à dire f [1 + NUM] code> - Il (tente de) indexer sur le 1 + num code> th sup> foo code> dans un tableau. Je suis sûr que c'est pas em> ce que vous voulez. Lorsque vous allouez les données, vous passez Tailleof (FOO) + num + max_comment_size code>, donc ce que vous allouez de l'espace pour IS num char code> s et ce que vous ( Vraisemblablement) Vouloir est de pointer f-> commentaire code> à un endroit en mémoire qui est num char code> S après f-> données code>, ce qui serait plus Comme ceci: p> f code> à un char * code> force les mathématiques à effectuer en termes de Char code> S au lieu de FOO code> s. p> max_comment_size code> pour Commentaire code> Je simplifierais probablement des choses (tout à fait) un peu et utilisez quelque chose comme ceci: p> Data [1] code> fonctionnera avec chaque compilateur C89 / 90 existant). P> p>
Vous avez repéré cela à peu près au même moment, nous avons composé nos réponses en parallèle.
@Jonathan Leffler: Yup - surtout avec une réponse verbeuse comme la mienne, c'est presque inévitable ...
La ligne que vous question est valide - comme d'autres personnes ont dit.
Fait intéressant, la ligne suivante, que vous n'avez pas interrogée, est syntaxiquement, est valide syntaxiquement, mais ne vous donne pas la réponse que vous souhaitez (sauf dans le cas où la valeur de La valeur de Qu'est-ce que vous aviez probablement à l'esprit était: p> ou: p> Notez que pendant que GCC vous permettra d'ajouter num == 0 code>). p> F + 1 code> est un foo * code> (implicitement forcé dans un vide * code> par l'affectation). p> f + 1 + num code> est aussi un foo * < / code>; Il pointe vers le num + 1 code> th sup> foo code>. p> num code> à un pointeur vide, et il le traitera comme si tailleof (void) == 1 code>, la norme C ne vous donne pas cette autorisation. p> p>
Un autre problème possible pourrait être un alignement. P>
Si vous savez simplement Malloc votre F-> Data code>, vous pouvez alors vous pouvez préciser par E.G. Convertissez votre Void * code> sur double * code> et utilisez-le pour lire / écrire un double (à condition que NUM soit suffisamment grand). Cependant, dans votre exemple, vous ne pouvez plus faire cela, car les données F-> peuvent ne pas être correctement alignées. Par exemple, pour stocker une doublure en données F->, vous devrez utiliser quelque chose comme MEMCY au lieu d'une simple typast. P>
L'utilisation de mon approche (tableau à la fin) résout le problème d'alignement automatiquement. Il peut également être résolu en affectant suffisamment d'espace supplémentaire que vous pouvez arrondir f-> données code> au prochain multiple de Tailleof (double) code> ou quel que soit le type dont vous avez besoin.
Je préférerais utiliser une fonction pour allouer les données de manière dynamique et libérer correctement.
L'utilisation de cette astuce ne vous permet que d'initialiser la structure de données et peut conduire à de très mauvais problèmes (voir le commentaire de Jerry) . P>
Je ferais quelque chose comme ceci: p> Notez que je n'ai pas fait de vérification sur la validité des données et que mon ALLOC peut être optimisé (remplaçant SHLEN () appels par une valeur de longue durée stockée). P> Il me semble que ce comportement est plus sûr ... au prix d'une chunque de données diffusée peut-être. P> p>