51 votes

Arithmétique des pointeurs avec deux tampons différents

Considérons le code suivant :

int* p1 = new int[100];
int* p2 = new int[100];
const ptrdiff_t ptrDiff = p1 - p2;

int* p1_42 = &(p1[42]);
int* p2_42 = p1_42 + ptrDiff;

Maintenant, est-ce que la norme garantit que p2_42 Les points suivants p2[42] ? Si non, est-ce toujours vrai sur Windows, Linux ou webassembly heap ?

3 votes

Il n'y a même pas de garantie que int les objets sont sizeof(int) aligné (c'est le cas sur toutes les ABI que je connais, mais il y a des exceptions à presque toutes les règles en programmation, donc certaines ABI peuvent ne pas être ainsi) ; quand ce n'est pas le cas, le code ne peut évidemment pas être garanti de fonctionner.

1 votes

@curiousguy Il n'y a pas de raison particulière de ne pas aligner sur les limites d'octets sur Intel, si ce n'est les performances. Si au lieu de int nous avons utilisé struct i5 { int i[5]; }; en pratique p1 y p2 ne serait pas sizeof(i5) alignés.

0 votes

Une question complémentaire (bien qu'elle ait été posée plus tôt) : Quelle est la raison d'être des limitations sur l'arithmétique ou la comparaison des pointeurs ?

56voto

Max Langhof Points 19174

Pour ajouter le devis standard :

expr.add#5

Lorsque deux expressions pointées P y Q sont soustraits, le type du résultat est un type d'intégrale signée défini par l'implémentation ; ce type doit être le même que celui qui est défini en tant que std::ptrdiff_­t dans le <cstddef> ([support.types]).

  • (5.1) Si P y Q évaluent tous deux à des valeurs de pointeur nulles, le résultat est 0.

  • (5.2) Sinon, si P y Q pointent, respectivement, vers les éléments x[i] y x[j] du même objet du tableau x l'expression P - Q a la valeur ij .

  • (5.3) Sinon, le comportement est indéfini. [ Nota: Si la valeur ij n'est pas dans la gamme des valeurs représentables du type std::ptrdiff_­t le comportement est indéfini. -note de fin ]

(5.1) ne s'applique pas car les pointeurs ne sont pas des nullptrs. (5.2) ne s'applique pas car les pointeurs ne sont pas dans le même tableau. Il nous reste donc (5.3) - UB.

29voto

Matteo Italia Points 53117
const ptrdiff_t ptrDiff = p1 - p2;

C'est un comportement non défini. La soustraction entre deux pointeurs n'est bien définie que s'ils pointent vers des éléments du même tableau. ( [expr.add] ¶5.3 ).

Lorsque deux expressions pointées P y Q sont soustraits, le type du résultat est un type d'intégrale signée défini par l'implémentation ; ce type doit être le même que celui qui est défini en tant que std::ptrdiff_­t dans le <cstddef> ([support.types]).

  • Si P y Q évaluent tous deux à des valeurs de pointeur nulles, le résultat est 0.
  • Sinon, si P et Q désignent respectivement des éléments x[i] y x[j] du même objet du tableau x l'expression P - Q a la valeur ij .
  • Sinon, le comportement est indéfini

Et même s'il existait un moyen hypothétique d'obtenir cette valeur de manière légale, même cette sommation est illégale, car même une sommation pointeur+nombre entier doit rester dans les limites du tableau ( [expr.add] ¶4.2 )

Lorsqu'une expression J qui est de type intégral est ajouté ou soustrait d'une expression P de type pointeur, le résultat a le type de P .

  • Si P est évalué à une valeur de pointeur nulle et J est égal à 0, le résultat est un pointeur de valeur nulle.
  • Sinon, si P pointe vers l'élément x[i] d'un objet de type tableau x avec n éléments, 81 les expressions P + J y J + P (où J a la valeur j ) pointent vers l'élément (peut-être hypothétique) x[i+j] si 0i+jn et l'expression P - J pointe vers l'élément (peut-être hypothétique) x[ij] si 0ijn .
  • Sinon, le comportement est indéfini.

9voto

MSalters Points 74024

La troisième ligne est un comportement non défini, donc la norme autorise tout ce qui suit.

Il est seulement légal de soustraire deux pointeurs pointant vers (ou après) le même tableau.

Windows ou Linux ne sont pas vraiment pertinents ; les compilateurs et surtout leurs optimiseurs sont ce qui casse votre programme. Par exemple, un optimiseur peut reconnaître que p1 y p2 indiquent tous deux le début d'un int[100] donc p1-p2 doit être égal à 0.

3 votes

Puisque la troisième ligne est un comportement non défini, la norme permet n'importe quoi avant cela aussi :(

7voto

supercat Points 25534

La norme permet des implémentations sur des plates-formes où la mémoire est divisée en régions discrètes qui ne peuvent être atteintes les unes des autres en utilisant l'arithmétique des pointeurs. À titre d'exemple simple, certaines plates-formes utilisent des adresses de 24 bits qui se composent d'un numéro de banque de 8 bits et d'une adresse de 16 bits à l'intérieur d'une banque. En ajoutant un à une adresse qui identifie le dernier octet d'une banque, on obtient un pointeur sur le premier octet de cette banque. même plutôt que le premier octet de la banque suivant banque. Cette approche permet de calculer l'arithmétique d'adresse et les décalages en utilisant les mathématiques 16 bits plutôt que 24 bits, mais exige qu'aucun objet ne chevauche une frontière de banque. Une telle conception imposerait une certaine complexité supplémentaire à malloc Le code utilisateur n'a généralement pas besoin de se soucier du partitionnement de la mémoire en banques.

De nombreuses plates-formes n'ont pas de telles restrictions architecturales, et certains compilateurs conçus pour la programmation de bas niveau sur de telles plates-formes permettront d'effectuer l'arithmétique d'adresse entre des pointeurs arbitraires. La norme note qu'une façon courante de traiter le comportement non défini est de "se comporter pendant la traduction ou l'exécution du programme d'une manière documentée caractéristique de l'environnement", et la prise en charge de l'arithmétique des pointeurs généralisée dans les environnements qui la prennent en charge s'inscrirait parfaitement dans cette catégorie. Malheureusement, la norme ne fournit aucun moyen de distinguer les implémentations qui se comportent de cette manière utile de celles qui ne le font pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X