La norme C garantit qu'il size_t
s'agit d'un type pouvant contenir n'importe quel index de tableau. Cela signifie que, logiquement, size_t
devrait pouvoir contenir n'importe quel type de pointeur. J'ai lu sur certains sites que j'ai trouvés sur Google que cela est légal et / ou devrait toujours fonctionner:
void *v = malloc(10);
size_t s = (size_t) v;
Ainsi, en C99, la norme a introduit les types intptr_t
et uintptr_t
, qui sont des types signés et non signés garantis pour pouvoir contenir des pointeurs:
uintptr_t p = (size_t) v;
Alors, quelle est la différence entre utiliser size_t
et uintptr_t
? Les deux ne sont pas signés, et les deux devraient pouvoir contenir n'importe quel type de pointeur, de sorte qu'ils semblent fonctionnellement identiques. Y a-t-il une vraie raison impérieuse d'utiliser uintptr_t
(ou mieux encore, a void *
) plutôt que a size_t
, autre que la clarté? Dans une structure opaque, où le champ ne sera géré que par des fonctions internes, y a-t-il une raison de ne pas le faire?
De même, ptrdiff_t
a-t-il été un type signé capable de contenir des différences de pointeurs, et donc capable de contenir la plupart des pointeurs, alors en quoi est-il distinct intptr_t
?
Tous ces types ne servent-ils pas fondamentalement des versions trivialement différentes de la même fonction? Sinon, pourquoi? Qu'est-ce que je ne peux pas faire avec l'un d'eux que je ne peux pas faire avec un autre? Si oui, pourquoi C99 a-t-il ajouté deux types essentiellement superflus au langage?
Je suis prêt à ignorer les pointeurs de fonction, car ils ne s'appliquent pas au problème actuel, mais n'hésitez pas à les mentionner, car j'ai une suspicion sournoise, ils seront au cœur de la "bonne" réponse.
size_t
etuintptr_t
mais qu'en est-ilptrdiff_t
etintptr_t
- les deux ne pourraient-ils pas stocker la même plage de valeurs sur presque n'importe quelle plate-forme? Pourquoi avoir des types entiers de taille de pointeur signés et non signés, en particulier si celaptrdiff_t
sert déjà l'objectif d'un type entier de taille de pointeur signé.size_t
être d'au moins 16 bits, maisptrdiff_t
d'au moins 17 bits (ce qui signifie en pratique qu'elle sera probablement d'au moins 32 bits).SIZE_MAX
qu'il ne devrait pas être 2 ** 64. Cela utilise l'adressage plat, souvenez-vous; aucune segmentation n'est nécessaire pour avoir un décalage entreSIZE_MAX
et la plage d'un pointeur de données.Concernant votre déclaration:
Il s'agit en fait d'une erreur (une idée fausse résultant d'un raisonnement incorrect) (a) . Vous pensez peut-être que ce dernier découle du premier, mais ce n'est pas vraiment le cas.
Les pointeurs et les index de tableaux ne sont pas la même chose. Il est tout à fait plausible d'envisager une implémentation conforme qui limite les tableaux à 65536 éléments mais permet aux pointeurs d'adresser n'importe quelle valeur dans un espace d'adressage massif de 128 bits.
C99 indique que la limite supérieure d'une
size_t
variable est définie parSIZE_MAX
et cela peut être aussi bas que 65535 (voir C99 TR3, 7.18.3, inchangé en C11). Les pointeurs seraient assez limités s'ils étaient limités à cette gamme dans les systèmes modernes.En pratique, vous constaterez probablement que votre hypothèse est vraie, mais ce n'est pas parce que la norme le garantit. Parce que cela ne le garantit pas.
(a) Ce n'est pas une forme d'attaque personnelle, soit dit en passant, expliquant simplement pourquoi vos déclarations sont erronées dans le contexte de la pensée critique. Par exemple, le raisonnement suivant n'est pas non plus valide:
La gentillesse ou non des chiots n'a aucune incidence ici, tout ce que je dis, c'est que les deux faits ne mènent pas à la conclusion, car les deux premières phrases permettent l'existence de choses mignonnes qui ne sont pas des chiots.
Ceci est similaire à votre première déclaration qui n'impose pas nécessairement la seconde.
la source
ptrdiff_t
vs.intptr_t
).Je laisserai toutes les autres réponses s'affirmer concernant le raisonnement avec les limitations de segment, les architectures exotiques, etc.
La simple différence de noms n'est-elle pas une raison suffisante pour utiliser le bon type pour la bonne chose?
Si vous stockez une taille, utilisez
size_t
. Si vous stockez un pointeur, utilisezintptr_t
. Une personne lisant votre code saura instantanément que "aha, c'est la taille de quelque chose, probablement en octets", et "oh, voici une valeur de pointeur stockée sous forme d'entier, pour une raison quelconque".Sinon, vous pourriez simplement utiliser
unsigned long
(ou, en ces temps modernes iciunsigned long long
), pour tout. La taille n'est pas tout, les noms de type ont une signification qui est utile car elle aide à décrire le programme.la source
size_t
champ.void*
,intptr_t
etuintptr_t
sont garantis pour pouvoir représenter n'importe quel pointeur vers des données.Il est possible que la taille du plus grand tableau soit inférieure à celle d'un pointeur. Pensez aux architectures segmentées - les pointeurs peuvent être de 32 bits, mais un seul segment ne peut traiter que 64 Ko (par exemple l'ancienne architecture 8086 en mode réel).
Bien que ceux-ci ne soient plus couramment utilisés sur les ordinateurs de bureau, la norme C est destinée à prendre en charge même les petites architectures spécialisées. Il existe encore des systèmes embarqués en cours de développement avec des CPU 8 ou 16 bits par exemple.
la source
size_t
également pouvoir gérer cela? Ou les tableaux dynamiques dans certains segments éloignés seraient-ils encore limités à l'indexation dans leur segment?str
fonctions et Borland même pour lesmem
fonctions (memset
,memcpy
,memmove
). Cela signifiait que vous pouviez écraser une partie de la mémoire lorsque le décalage débordait, ce qui était amusant à déboguer sur notre plate-forme intégrée.J'imagine (et cela vaut pour tous les noms de types) qu'il transmet mieux vos intentions dans le code.
Par exemple, même si
unsigned short
etwchar_t
sont de la même taille sous Windows (je pense), l'utilisationwchar_t
au lieu deunsigned short
montre l'intention que vous l'utiliserez pour stocker un caractère large, plutôt que juste un certain nombre arbitraire.la source
wchar_t
est beaucoup plus grande qu'ununsigned short
donc utiliser l'un pour l'autre serait erroné et créerait un problème de portabilité sérieux (et moderne), tandis que les problèmes de portabilité entresize_t
etuintptr_t
semblent se situer dans les contrées lointaines de 1980-quelque chose (coup de couteau au hasard dans le noir à la date, là)size_t
etuintptr_t
ont toujours des utilisations implicites dans leurs noms.En regardant à la fois en arrière et en avant, et en rappelant que diverses architectures bizarres étaient dispersées dans le paysage, je suis presque sûr qu'elles essayaient d'envelopper tous les systèmes existants et de prévoir également tous les futurs systèmes possibles.
Tellement sûr, la façon dont les choses se sont arrangées, nous avons jusqu'à présent eu besoin de moins de types.
Mais même dans LP64, un paradigme assez courant, nous avions besoin de size_t et ssize_t pour l'interface d'appel système. On peut imaginer un système hérité ou futur plus contraint, où l'utilisation d'un type 64 bits complet coûte cher et peut-être voudra-t-il exécuter des opérations d'E / S de plus de 4 Go mais qui ont toujours des pointeurs 64 bits.
Je pense que vous devez vous demander: ce qui aurait pu être développé, ce qui pourrait arriver à l'avenir. (Peut-être des pointeurs à l'échelle du système distribué 128 bits sur Internet, mais pas plus de 64 bits dans un appel système, ou peut-être même une limite 32 bits "héritée". :-) Image que les systèmes hérités pourraient obtenir de nouveaux compilateurs C .. .
Regardez également ce qui existait à l'époque. Outre les modèles de mémoire en mode réel zillion 286, que diriez-vous des ordinateurs centraux de pointeur de mot / bit 18 bits CDC? Et la série Cray? Peu importe ILP64, LP64, LLP64 normal. (J'ai toujours pensé que Microsoft était prétentieux avec LLP64, il aurait dû être P64.) Je peux certainement imaginer un comité essayant de couvrir toutes les bases ...
la source
Cela implique que intptr_t doit toujours remplacer size_t et vice versa.
la source