33 votes

Est-ce que ptrdiff_t peut représenter toutes les soustractions de pointeurs vers des éléments d'un même objet tableau ?

Pour la soustraction de pointeurs i y j à des éléments du même objet de tableau la note dans [expr.add#5] lit :

[ Note : 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 ]

Mais étant donné [support.types.layout#2] qui stipule que ( accentuation mine) :

  1. Le type ptrdiff_­t est un type d'entier signé défini par l'implémentation qui peut tenir la différence de deux indices dans un objet de type tableau, comme décrit dans la rubrique [expr.add] .

Est-il même possible que le résultat de i-j de ne pas être dans la gamme des valeurs représentables de ptrdiff_t ?

PS : Je m'excuse si ma question est due à ma mauvaise compréhension de la langue anglaise.

EDIT : En rapport : Pourquoi la taille maximale d'un tableau est-elle "trop grande" ?

1 votes

Sur de nombreuses architectures populaires (la plupart des plates-formes <=32 bits), il serait plutôt difficile et coûteux de fournir un ptrdiff_t qui puisse toujours contenir i-j (et ils ne fournissent pas en fait un tel ptrdiff_t). L'intention de la norme n'est pas de rendre les choses difficiles et coûteuses, ou de rendre la plupart des implémentations existantes non conformes, mais plutôt l'inverse. Donc oui, il "peut faire la différence"... quand il le peut.

0 votes

Oui, la deuxième citation dit que - si i y j sont des indices valides pour le même tableau, qu'un ptrdiff_t peut représenter le résultat de i - j . La première citation revient à l'exigence inverse, à savoir que i - j doit également pouvoir être représentée dans un ptrdiff_t ou le comportement n'est pas défini (je dirais que la première note est redondante étant donné la présence de la seconde, mais elle réduit probablement les possibilités pour les juristes linguistiques de trouver des failles obscures exploitables dans le langage).

0 votes

Une chose qui est implicite est que, d'après ce que j'ai compris, les problèmes ne peuvent survenir que s'il s'agit d'un tableau d'un type avec sizeof=1 (comme char). (Ou existe-t-il un cas particulier pour sizeof=2 également ?).

5voto

YSC Points 3386

Est-il même possible que le résultat de i-j de ne pas être dans la gamme des valeurs représentables de ptrdiff_t ?

Oui, mais c'est peu probable.

En fait, [support.types.layout]/2 ne dit pas grand chose, si ce n'est les règles appropriées concernant la soustraction des pointeurs et la ptrdiff_t sont définis dans [expr.add] . Voyons donc cette section.

[expr.add]/5

Lorsque deux pointeurs sur des éléments du même objet tableau 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 comme suit std::ptrdiff_­t dans le <cstddef> en-tête.

Tout d'abord, notez que le cas où i y j sont des indices d'indices de tableaux différents n'est pas prise en compte. Cela permet de traiter i-j comme P-Q serait où P est un pointeur sur l'élément d'un tableau à l'indice i y Q est un pointeur vers l'élément de le même tableau à l'indice j . En effet, la soustraction de deux pointeurs sur des éléments de tableaux différents est comportement indéfini :

[expr.add]/5

Si les expressions 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 ; sinon, le comportement est indéfini .

En conclusion, avec la notation définie précédemment, i-j y P-Q sont définis pour avoir la même valeur, cette dernière étant de type std::ptrdiff_t . Mais rien n'est dit sur la possibilité pour ce type de détenir une telle valeur. Il est toutefois possible de répondre à cette question à l'aide des éléments suivants std::numeric_limits ; en particulier, on peut détecter si un tableau some_array est trop grand pour std::ptrdiff_t pour contenir toutes les différences d'index :

static_assert(std::numeric_limits<std::ptrdiff_t>::max() > sizeof(some_array)/sizeof(some_array[0]),
    "some_array is too big, subtracting its first and one-past-the-end element indexes "
    "or pointers would lead to undefined behavior as per [expr.add]/5."
);

Maintenant, sur une cible habituelle, cela n'arriverait généralement pas car sizeof(std::ptrdiff_t) == sizeof(void*) ce qui signifie qu'un tableau devrait être stupidement grand pour que les ptrdiff_t pour déborder. Mais il n'y a aucune garantie de cela.

0 votes

En d'autres termes, si i y j sont des pointeurs vers des éléments du même tableau, le résultat de la fonction i-j est toujours dans l'intervalle des valeurs représentables de type std::ptrdiff_t ?

2 votes

Non, support.types.layout ne dit rien de tel. Il dit "... as described in expr.add"" et expr.add décrit un cas où i-j n'est pas représentable l.

4 votes

Un aspect intéressant : En supposant que size_t et ptrdiff_t ont la même taille, comme le premier est non signé, mais le second est signé, il pourrait être utilisé pour définir des tableaux plus grands que ce qu'une différence de pointeur pourrait contenir. Que dit la norme à ce sujet ? Si tout différence doit soit représentable, nous devrions conclure (peut-être sans que cela soit mentionné) que la taille des tableaux ne doit pas dépasser std::numeric_limits<std::ptrdiff_t>::max() peu importe si std::size_t est capable de contenir de telles valeurs ou non...

1voto

xskxzr Points 5160

Je pense que c'est un bug des formulations.

La règle dans [expr.add] est héritée de la même règle pour la soustraction de pointeurs dans la norme C. Dans la norme C, ptrdiff_t n'est pas nécessaire pour contenir la différence entre deux indices dans un objet tableau.

La règle dans [support.types.layout] vient de Numéro de langue principale 1122 . Elle a ajouté des définitions directes pour std::size_t y std::ptrdiff_t qui est censé résoudre le problème de la définition circulaire. Je ne vois pas de raison (du moins pas mentionnée dans un document officiel) de faire de la std::ptrdiff_t maintenir toute différence de deux indices dans un tableau. Je suppose qu'il utilise simplement une définition inappropriée pour résoudre le problème de la définition circulaire.

Comme une autre preuve, [diff.library] ne mentionne aucune différence entre std::ptrdiff_t en C++ et ptrdiff_t dans C. Puisque dans C ptrdiff_t n'a pas cette contrainte, en C++ std::ptrdiff_t ne devrait pas avoir une telle contrainte aussi.

1 votes

Il peut également être utile de noter que la norme C énumère explicitement le comportement non défini causé par la soustraction de pointeurs (si le résultat ne tient pas dans un fichier ptrdiff_t ) dans l'annexe informative J sous "J.2 Comportement non défini".

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