Le standard de C99 indique que:
Lorsque deux pointeurs sont soustraits, les deux doivent pointer vers les éléments du même objet de tableau, ou un autre élément du dernier élément de l'objet réseau p> blockQuote>
Considérez le code suivant: P>
struct test { int x[5]; char something; short y[5]; }; ... struct test s = { ... }; char *p = (char *) s.x; char *q = (char *) s.y; printf("%td\n", q - p);
5 Réponses :
Oui, vous êtes autorisé à effectuer le pointeur arithmétrique sur structure octets: P>
N1570 - 6.3.2.3 Pointeurs P7: P>
... Quand un pointeur sur un objet est converti en un pointeur sur un type de caractère, Le résultat pointe vers l'octet adressé le plus bas de l'objet. incréments successifs de la Résultat strong>, jusqu'à la taille de l'objet, rendez-vous des pointeurs aux octets restants de l'objet. P> blockQuote>
Cela signifie que pour le programmeur, les octets de la conducture doivent être considérés comme une zone continue, quelle que soit la manière dont il a peut-être été mis en œuvre dans le matériel. P>
pas avec
Void * code> des pointeurs, c'est-à-dire une extension de compilateur non standard. Comme mentionné sur le paragraphe de la norme, il s'applique uniquement aux pointeurs de type de caractères. P>
EDIT: strong> p> Comme la MAFSO a souligné dans des commentaires, ci-dessus n'est que vrai tant que le type de résultat de soustraction
pTRDIFF_T code> a suffisamment de portée pour le résultat. Depuis la plage de
Taille_T CODE> peut être plus grande que
PTRIFF_T CODE> et si la structure est assez grande, il est possible que les adresses soient trop éloignées. p>
À cause de cela, il est préférable d'utiliser
offsetof code> macro sur les membres de la structure et calculer le résultat de ceux-ci. P>
+1, je crois aussi que le mot "éléments" dans la règle que citée est utilisé pour distinguer des pointeurs de type char * code> et des pointeurs correctement alignés du type correspondant aux éléments de réseau.
Cette réponse semble impliquer que le rembourrage pourrait être considéré comme un objet que je doute fortement était prévu.
Cette réponse manque le point. 1. Si p + n code> se compare égal à
q code>, cela n'implique pas que
q-p code> est défini. 2. L'important est, quel est l'objet ici (toute la structure ou juste le membre). J'ai tendance à interpréter la norme d'une manière en disant ce dernier.
@Shafikyaghmour Cette clause de la norme semble seulement couvrir le fait que vous pouvez obtenir un pointeur valide de cette façon. Qu'il soit ou non possible de dréérence, ledit pointeur est couvert ailleurs dans la norme.
@mafso dis-tu que le résultat de (char *) & s + offsetof (S, S.Y) == q - p code> n'est pas nécessairement vrai? Les règles du pointeur arithmétrique sont plutôt strictes et je ne vois pas comment la mise en œuvre pourrait le faire sans casser les règles.
PTRIFF_T CODE> est autorisé à être un moyen plus petit que
Taille_T CODE> (la norme ne dit rien de leur relation), il est donc possible que la différence est simplement indéfinie (si < Code> QP code> est défini, le résultat est comme prévu). (J'ai a posé une question à ce sujet il y a quelque temps.) Pour la structure inférieure à 2 ** 15, ceci est toujours défini, et ce n'est pas vraiment lié au problème ici, mon point était que l'implication "si
p + n code> est défini, de même que
p + nn code> "n'est pas vrai (supposé silencieusement dans votre message).
@MAFSO Vous avez raison sur possible pTRDIFF_T CODE> Overflow, j'ai édité la réponse pour accueillir cela.
Est un pointeur pour rembourrer un pointeur valide? Pouvez-vous justifier cette affirmation?
Comme je l'ai dit, le problème ptrdiff_t code> n'est pas vraiment lié, il n'était qu'un contre-exemple à la mauvaise implication "
p + n code> défini =>
p + nn code> défini ", que vous supposez toujours.
@Shafikyaghmour paragraphe I CITATED dit que vous obtiendrez des pointeurs qui restaient des octets de l'objet jusqu'à la taille de l'objet code>. Et
Tailleof (anyobject) code> est égal à la taille de l'objet, y compris le remplissage. Donc, pour moi, il semble assez clair que le pointeur est valable en ce sens qu'il s'agit d'une adresse valide pour le pointeur arithmétrique. Pas en sens que ce serait nécessairement sûr de la désarence.
@MAFSO Qu'essayez-vous de dire? Si nous gardons dans les limites de nos types, p + n-n code> est bien défini.
@ user694733: Bien sûr, il est bien défini (si dans les limites ou p + n code> était valide (et réellement utilisé!)), mais c'est le point réel ici (et défini dans 6.5.6 P9). Et il n'est défini que pour les pointeurs dans le même objet de tableau. Et c'est la question ici: quel est l'objet? Le membre (un pointeur sur lequel est converti en
char * code> et auquel cas la différence est UB) ou la structure contenant (ce qui répond à une interprétation courante, mais n'est pas défini dans la norme, dans la mesure où Je sais)?
@MAFSO Mon interprétation est que cet objet est la structure contenant. 3.15 décrit l'objet comme "région de stockage de données ..." "... content des valeurs" i>. La structure convient à cette description. De plus, les pointeurs d'origine des tableaux sont gagnés à travers la structure (avec syntaxe s.x code>). Si nous prenions
Char * Code> Pointeur de
S code>, le 6.5.6P7 indique que le PTR à l'objet peut être traité comme PTR au premier membre de la matrice. 6.3.2.3P7 garantit que, à des fins de pointeur arithmétrique, cette zone peut être considérée comme continue. En d'autres termes, vous pouvez traiter la région de la mémoire de la structure en tant que réseau d'octets. ...
@mafso ... Donc, il semble n'y avoir aucune idée que 6.3.2.3P8 et P9 ne pouvaient pas être appliqués dans ce cas. Bien sûr, si x code> et
y code> étaient des matrices distinctes, cela ne fonctionnerait bien sûr pas, mais comme ils sont contenus dans la même structure, les chapitres précédemment mentionnés donnent la garantie sécurisée. La norme ne mentionne pas explicitement cette affaire comme étant UB (qui n'est pas une grande partie de quarantage), il n'ya donc pas de preuves pour prouver le contraire. Désolé pour le mur de texte :)
La notion d'objet est également intéressante pour les règles d'aliasing, voir par ex. Cette question sur restreint code>. Et, comme je l'ai mentionné ci-dessus, une implémentation de vérification des limites est le problème suivant (voir par exemple ici et ). Une discussion ultérieure est probablement meilleure dans le chat.
@MAfso I Je n'ai enflammé que dans les liens, mais de la réponse et de leurs commentaires, il semblerait que la vérification des limites entre en jeu dans la phase de déséroférence. En tout cas, je vais en savoir plus à ce sujet et vous avez raison; Une discussion plus approfondie devrait être sur le chat. Laissons ceci pour l'instant. Heureusement, je n'ai pas eu et n'aurez pas dans un avenir prévisible, je dois accéder à des structures autres que le moyen sûr habituel, donc je ne suis pas pressé de résoudre ce problème. :)
Je dois signaler les éléments suivants: p>
de la norme C99, section 6.7.2.1: p>
dans un objet de structure, les membres non-bits et les unités dans lesquels des champs de bits
résider a des adresses qui augmentent dans l'ordre dans lequel ils sont déclarés. Un pointeur à un
objet de structure, convenablement converti, pointe vers son membre initial (ou si ce membre est un
champ de bits, puis à l'unité dans laquelle il réside), et vice versa. Ce n'est pas tellement que le résultat de la soustraction du pointeur entre les membres n'est pas défini tant qu'il n'est pas fiable (c'est-à-dire non garanti d'être identiques entre différentes instances du même type de structure lorsque la même arithmétique est appliquée). < / p>
L'arithmétique du pointeur nécessite que les deux pointeurs soient ajoutés ou soustraits pour faire partie du même objet car il n'a pas de sens autrement.
La section citée de la norme désigne spécifiquement deux objets non liés tels que IE P> int A [b]; code> et
int b [5] code>. L'arithmétique du pointeur nécessite de connaître le type de l'objet que les pointeurs pointant vers (je suis sûr que vous en êtes déjà conscient).
printf("%zu\n", offsetof(struct test, y) - offsetof(struct test, x));
Bonne réponse, mais vous oubliez que cela est autorisé lorsque les pointeurs sont tous les deux de type char * code> et pointez dans le même objet. Sans cela, il est impossible de définir
offsetof code>.
Bien sûr. Mais je ne suis pas sûr d'où je contredis cela ou implique?
Vous ne le contrariez pas directement, mais c'est une "échappatoire" importante de mentionner, IMHO.
TBH, j'ai lutté de l'endroit où je pouvais l'inclure logiquement autre que comme un fait disjoint après votre commentaire. Édité. Merci.
Opérateurs arithmétiques et relationnels du pointeur ( << / code>
<= code>
> code>
> = code>) entre les pointeurs d'objets distincts ne < Je> nécessairement i> pas de sens. La langue pourrait i> aurait rendu le résultat indéterminé plutôt que non défini le comportement et l'obligeait à se comporter de manière cohérente (de sorte que
& x <& y &&& <& z code> implique
& x <& z code>, et ainsi de suite). Et sur de nombreux systèmes, cela fonctionne réellement de cette façon. La norme a formulé de telles opérations non définies car elles peuvent être difficiles à mettre en œuvre de manière cohérente sur certaines architectures et que cet effort de mise en œuvre supplémentaire ne vous achèterait pas particulièrement utile.
Je crois que la réponse à cette question est plus simple qu'elle ne l'apparaît, l'OP demande: p>
Mais pourquoi ce résultat devrait-il être "indéfini"? p> blockQuote>
Eh bien, voyons que la définition du comportement non défini est dans le projet de section standard de C99
3.4.3 code>: p>
comportement, lors de l'utilisation d'une construction de programme non-sport ou erronée ou de données erronées, pour lesquelles cette norme internationale impose non Exigences p> blockQuote>
Il s'agit simplement de comportement pour lequel la norme n'impose pas une exigence, ce qui correspond parfaitement à cette situation, les résultats vont varier en fonction de l'architecture et tenter de spécifier les résultats auraient probablement été difficiles sinon impossibles dans un portable manière. Cela laisse la question, pourquoi choisiraient-ils un comportement indéfini par opposition à la mise en œuvre du comportement non précisé? P>
Très probablement, il a été fait un comportement indéfini de limiter le nombre de façons un pointeur non valide em> pourrait être créé, cela correspond au fait que nous sommes fournis avec
offset de code> à Retirez le besoin potentiel de la soustraction de pointeur d'objets non liés. P>
Bien que la norme ne définisse pas vraiment le terme pointeur non valide, nous obtenons une bonne description dans Justification pour les langages de programmation standard internationaux-C , qui dans la section
6.3.2.3 code> les pointeurs em> dit ( emphasis. / em>): p>
Implicity dans la norme est la notion de pointeurs non valides. Dans discuter des pointeurs, la norme se réfère généralement à "un pointeur à un objet "ou" un pointeur à une fonction "ou" un pointeur null ". Une spéciale étui dans l'adresse arithmétique permet à un pointeur juste après la fin d'un tableau. tout autre pointeur est invalide. strong> p> blockQuote>
La justification de C99 ajoute: p>
quel que soit la création d'un pointeur non valide, toute utilisation des rendements informatiques comportement non défini fort>. Même affectation, comparaison avec un pointeur NULL constante ou comparaison avec elle-même, pourrait sur certains systèmes entraîner une exception. strong> p> blockQuote> Cela me suggère fortement qu'un pointeur sur le rembourrage em> serait un pointeur em>, bien qu'il soit difficile de prouver que le rembourrage em> n'est pas Un objet em>, la définition de objet em> dit: p>
Région de stockage de données dans l'environnement d'exécution, le contenu de qui peut représenter des valeurs p> blockQuote>
et notes: p>
Lorsqu'il est référencé, un objet peut être interprété comme ayant un particulier taper; Voir 6.3.2.1. P> blockQuote>
Je ne vois pas comment nous pouvons raisonner sur le type em> ou la la valeur em> de remplissage entre éléments d'une structure et donc ils ne sont donc pas objets EM> ou au moins est fortement indique que le rembourrage em> n'est pas censé être considéré comme un objet em>. P>
Je ne vois pas comment le pointeur du pointeur peut être un pointeur invalide. Le rembourrage n'est pas un objet, mais une partie d'un objet. Après tout, standard garantit que le remplissage existera, seule la valeur de celui-ci n'est pas spécifiée (6.2.6.1P1). Voir Keith Thompson Réponse .
Les opérateurs de soustraction et de relation relationnels (sur type n'importe quel objet peut être traité comme une matrice de citant N1570 6.2.6.1 Paragraphe 4: p>
valeurs stockées dans des objets de champ non bits de tout autre type d'objet
consister en n em> × ... p>
Mes seuls suspicions sont des architectures de mémoire segmentées où les membres
pourrait se retrouver dans différents segments. Est-ce le cas? P>
blockQuote>
non. Pour un système avec une architecture de mémoire segmentée, le compilateur imposera normalement une restriction que chaque objet doit s'intégrer dans un seul segment. Ou cela peut permettre des objets qui occupent plusieurs segments, mais il doit toujours garantir que les arithmétiques du pointeur et les comparaisons fonctionnent correctement. P> Char * code>) entre les adresses de membre de la même structure sont bien définies. P>
non signé char code>. p>
char_bit code> strong> bits, où n est la taille d'un objet de cette
type, en octets. La valeur peut être copiée dans un objet de type
non signé Char [ code> strud> n em>
] code> strong> (par exemple, par
memcpy code> strong>); L'ensemble d'octets résultant est
appelé la représentation objet de la valeur. P>
blockQuote>
+1 Je pense que la réponse parvient à mieux comprendre que ma réponse.
Je ne suis pas convaincu. Si un pointeur dans un objet pourrait toujours être traité comme un pointeur dans l'objet enfermé la plus à l'extérieur, par exemple le piratage de la structure serait également légal ...
@MAFSO bien, en standard, est i> entrée dans l'index: struct piratage, voir membre flexible de tableau code> ...
@ user694733: Les membres de la matrice flexibles ont été ajoutés à la norme en 1999 en tant que remplacement i> pour le piratage de la structure, qui était de légalité discutable.
@Keiththompson je sais. Je viens de devoir. :)
@MAFSO: C'est dommage que les matrices de taille zéro standard font une violation de contrainte. Si cela avait dit à la place d'un tableau de taille zéro dans une structure insertion de remplissage nécessaire pour forcer l'alignement approprié, puis produit l'adresse résultante sans rien attribuer, cela aurait offert une sémantique plus utile que les membres de la matrice flexibles, mais sur la plupart des implémentations. ne coûterait rien (changer simplement le tableau minimum de 1 à 0). Le piratage de la structure pourrait alors être justifié en disant que l'indice maximum pour un tableau, dans une allocation suffisante, est (LEN- (Taille_T) 1).
@supercat: Nous avons plutôt des membres de tableau flexibles. Ils travaillent. Faites avec.
@Keiththompson: ils ne fonctionnent que pour des objets de durée allouée; Il n'y a pas de moyen standard de créer une structure de durée statique ou automatique compatible avec une fonction qui s'attend à une structure avec une FAM.
Et dans votre exemple, le pointeur arithmétique sur le vide est interdit de toute façon. Si les types ne sont pas les mêmes, comment pouvez-vous les soustraire?
Vous êtes correct que l'arithmétique sur des pointeurs vide n'est pas autorisé par la norme et c'est une extension GNU. Imaginez que les deux pointeurs soient
char * code>.
GCC permet à l'arithmétique du pointeur sur
Void * code> en traitant
Tailleof (VOID) code> comme 1 (identique à un
char * code>). Donc, pour les besoins de votre question, cela ne fait aucune différence.
Ceci est nécessaire pour permettre aux implémentations de vérification des bornes, afaik, l'intention du comité standard (ou au moins était pour C89) de le permettre. Je pense qu'un contrôle de la mise en œuvre des limites peut (doit) attraper ce cas (c'est-à-dire que c'est UB, bien que cela fonctionne en réalité). Une telle mise en œuvre briserait cependant beaucoup de code existant. Et la norme est un peu vague sur sa notion d'un objet qui rend difficile la réponse exacte.
@MAFSO: En l'absence de règles d'aliasing, chaque objet pourrait être considéré comme membre d'une union contenant un membre de chaque type pouvant occuper l'espace. Donné
struct {int x, y;} foo; code>, si pour une valeur entière
n code>, l'adresse de
foo.y code> serait égal
((int *) & foo) + n code>, puis
& foo.x code> et
& foo.y code> serait les adresses des éléments 0 et
n < / Code> d'un réseau d'entiers qui commencent à l'adresse
& foo code>. Malheureusement, les auteurs du jambon standard ont lancé des règles d'aliasing qui dépendent des détails des "objets" qu'ils n'ont jamais définis parce que la langue n'était pas nécessaire.