Lorsque vous travaillez avec des tableaux, des algorithmes standards (dans les deux C et C ++) renvoient souvent des pointeurs vers des éléments. Il est parfois pratique d'avoir l'indice de l'élément, peut-être à l'index dans un autre tableau, et je reçois normalement qu'en soustrayant le début du tableau du pointeur:
int arr[100]; int *addressICareAbout = f(arr, 100); size_t index = addressICareAbout - arr;
3 Réponses :
J'ai trouvé aucune preuve à suggérer autrement. P> blockQuote>
Heureusement, il existe des preuves ici: http://en.cppreference.com/w/ CPP / TYPES / TIZE_T P>
std ::ze_t code> peut stocker la taille maximale d'un objet théoriquement possible de tout type (y compris la matrice). Un type dont la taille ne peut pas être représenté parstd ::ze_t code> est mal formé (car c ++ 14) p> blockQuote>
True, taille_t code> doit être suffisamment grand pour stocker la taille de Art code>. Mais doit ptrdiff_t code> être assez grand pour stocker n'importe quel index?
Pour clarifier, il semble que la soustraction soit déjà non définie si la différence déborde d'un ptrdiff_t code>, quel que soit le type de variable, je comme i> d'attribuer le résultat.
Ah OK, il est concevable que dans un système de mémoire segmenté (ou paginé), vous pourriez avoir des différences d'adresses supérieures à la taille maximale d'un tableau.
Le problème n'est pas "plus grand que la taille maximale d'un tableau". Le problème est que PTRDIFF_T doit gérer des nombres positifs et négatifs, et vous pourriez avoir un tableau avec une taille plus grande que le plus grand PTRDIFF_T (pas plus grand que la plus grande taille possible).
Cela fonctionnerait toujours à cause du débordement arithmétique. par exemple. Dans un système de mémoire de 16 bits, 0xFFFF - 0x0000 donnerait un PTRDIFF de -1 (0xFFFF). Ajouter -1 à 0x0 donnerait une adresse 0xFFFF
Comment cela répond-il à la question? (Astuce: Ça ne le fait pas)
Les types signés débordants sont indéfinis lors de la coulée d'un type non signé sur un type signé qui ne peut pas contenir la valeur est définie sur la mise en œuvre. Je détesterais compter de l'un ou l'autre de ces.
@LightnessRacksinorbit en toute justice, il n'y a aucune question à répondre.
Il est extrêmement improbable que vous auriez jamais deux pointeurs sur le même tableau où la différence ne s'intègre pas dans PTRIFF_T. p>
sur les implémentations 64 bits, PTRDIFF_T est signée 64 bits. Vous auriez donc besoin d'un tableau de 8 milliards de gigaoctets. Sur les implémentations 32 bits, votre espace d'adressage total est généralement limité à 3 Go, 3 1/4 Go Si vous avez de la chance (c'est l'espace d'adressage, pas la RAM, qui compte), vous auriez donc besoin d'un tableau de plus de 2 Go. ne laisse pas beaucoup pour rien d'autre. Et il est tout à fait possible que MALLOC refuse d'allouer une matrice de cette taille en premier lieu. Votre jugement bien sûr. p>
tandis que STD :: Distance présente des avantages, je soupçonne qu'il a le même problème théorique que PTRDIFF_T, car les distances peuvent être positives et négatives, et ce n'est probablement pas un type 64 bits sur une implémentation de 32 bits. p>
Notez que si vous pouvez allouer un réseau de 3 Go sur une implémentation 32 bits et que vous avez deux INT * au premier et le dernier élément de ce tableau, je ne serais pas surpris si la différence de pointeur est calculée de manière incorrecte même Bien que le résultat s'adapte à PTRDIFF_T. p>
La différence serait -1GigaByte, que si elle est ajoutée à la première adresse céderait la seconde (due au débordement arithmétique).
@Richardhodges: Un débordement signé n'a aucune garantie en C ++. La conversion antérieure sur Unsigné pourrait vous y arriver si vous êtes prudent.
Je sais que les compilateurs 32 bits peuvent prendre en charge des entiers 64 bits (comme long long int code>), donc dans ces implémentations, ptrdiff_t code> pourrait être défini comme long LONG LONG INT Code>, et ce serait "juste travailler" (mais vous devez vous assurer d'utiliser pTRIFF_T code> et pas seulement int code>.
"Vous auriez besoin d'un tableau de plus de 2 Go" ... Non, vous auriez besoin d'un tableau avec plus de 2 milliards d'éléments i>. Pour un tableau de INT code> sur un système 32 bits, cela signifie 8 Go, qui ne peut pas s'adapter à l'espace d'adressage. Le problème ne peut potentiellement se produire que pour tailleof (t) == 1 code>.
GCC (et éventuellement d'autres) supposera que les objets n'occupent pas plus de la moitié de l'espace d'adresses de toute façon: GCC.GNU.ORG/ML/GCC/2011-08/MSG00221.HTML
Solution possible: p>
lancer les deux pointeurs sur Solution de contournement vraiment portable (mais moins efficace): p>
Utilisez un alternatif pointeur de base. Si la matrice est supérieure à uintptr_t code>, soustraire et diviser par Tailleof (t) code> vous-même. Ce n'est pas précisément portable, mais il est garanti de ne jamais être un comportement indéfini, et la plupart des systèmes spécifient une conversion entier <-> de pointeur d'une manière qui fait ce travail. P>
2 << 30 code> éléments, vous pouvez alors calculer légalement étape = p1 + (2 << 30) code>, utiliser des opérateurs relationnels pour voir si p2> étape code>, et si oui, calculez le décalage comme (2U << 30) + uintptr_t (distance (étape, p2)) code> Notez que l'appel récursif peut nécessiter une autre étape. . p>
Conversion de uintprt_t code> n'est pas portable. J'ai travaillé sur des systèmes (systèmes de vecteur de crayon) où cela ne fonctionnerait pas. Sur ces systèmes, des pointeurs d'octets sont implémentés sous forme de pointeurs de mots de 64 bits avec un décalage de 3 bits stocké dans les 3 bits à haut ordre; La conversion de pointeur-to-ineger fait juste copie les bits. Je soupçonne que vous êtes plus susceptible de rencontrer un système dans lequel la conversion de uintptr_t code> échouera d'une fois la soustraction de pointeur simplifiée. Et je ne sais pas ce que vous entendez par " garantie de ne jamais être un comportement défini par la mise en œuvre i>".
La standard garantit que vous pouvez convertir un vide * code> sur uintptr_t code> (ce qui pourrait même pas exister) et de retour à vide * code> et le résultat sera comparer égale au pointeur d'origine. Si vous effectuez des arithmétiques sur la valeur uintptr_t code>, la norme garantit rien. Le pointeur résultant est défini par la mise en oeuvre et peut être une représentation de piège, alors il suffit d'évaluer le pointeur a un comportement indéfini.
@Keiththompson: Mais je ne jette pas à un pointeur, sans parler de la derréférance, il n'y a pas de comportement indéfini.
Ah, vous avez raison, l'idée est de calculer un indice entier. Mais l'index peut avoir de sens, et en utilisant i> il doit indiquer dans la matrice pourrait avoir un comportement indéfini.
@KeithThompson: Oui, mais vous pouvez vérifier de manière générale si cela a fonctionné. si (index
Oui
std :: Distance Code> Fait exactement ce que vous décrivez. Par exemple, votre pointeur arithmétique pointeur ne fonctionne que avec contigus i> contenus, il échouerait pourstd :: Liste code>. D'autre part,std :: Distance Code> gère des conteneurs contigus et non contigus, tant qu'ils sont commandés.Votre message est intéressant mais je ne peux pas voir une question. Demandez-vous: «Quel type dois-je utiliser pour stocker des compensations du pointeur?". Si tel est le cas, la réponse est un
ptrdiff_t code>.J'ai 1.5 questions: 1.0) en C ++, peut
std :: Distance Code> être utilisé "en toute sécurité" pour les conteneurs ou dois-je vous inquiéter que la distance peut déborderdifférence_type code>? 0.5) La situation est-elle en C (travaillant avec des pointeurs) vraiment i> aussi mauvais qu'il semble?de en.cpprefreence.com/w/cpp/types/ptrdiff_t Si un tableau est si grand (plus grand que PTRDIFF_max éléments, mais moins que Taille_max octets), que la différence entre deux pointeurs peut ne pas être représentable comme STD :: PTRDIFF_T, le résultat de la soustraction de deux de ces pointeurs est non défini B>. I> Par conséquent, bonne question.
J'ai vu cette référence aussi, et il semblait plausible (bien que j'ai vu un mauvais code de l'échantillon sur ce site avant). Un système permettrait-il que cela soit conforme? Ces systèmes existent-ils même? Existe-t-il une alternative "simple" à la soustraction du pointeur qui fonctionnera dans de tels cas?
Je crois que si la prémisse est vraie, il est impossible pour le compilateur de le rendre conforme (par exemple sur un modèle d'adresse 16 bits, vous ne pouvez pas stocker la possible "gamme de points arithmétiques du pointeur" de -65535 .. + 65535 dans un 16 bits mots).
STD :: Distance (Arr, AdressageAreaBout) Code> sera presque certainement juste êtreRetour AdressageAreaBout - Art; Code>La question liée ne fournit-elle pas la solution de contournement? Insérer
PTRDIFF_MAX CODE> Quelque part dans votre système d'allocation pour éviter que le débordement soit une option. Oui la standard i> ne rend pas la décision surptrdiff_max code> être utile pour cela, mais la mise en œuvre serait effectivement cassée si vous ne pouviez pas compter sur elle pour donner une valeur utile pour donner une valeur utile ici.Pourriez-vous ajouter ceci à votre source,
#if pTRDIFF_max Juste pour voir s'il s'agit d'un problème avec le compilateur / cible spécifié?