Der C-Standard garantiert, dass dieser size_t
Typ jeden Array-Index enthalten kann. Dies bedeutet, dass logischerweise size_t
jeder Zeigertyp enthalten sein sollte. Ich habe auf einigen Websites, die ich auf den Googles gefunden habe, gelesen, dass dies legal ist und / oder immer funktionieren sollte:
void *v = malloc(10);
size_t s = (size_t) v;
In C99 führte der Standard die Typen intptr_t
und uintptr_t
ein, die signierte und nicht signierte Typen sind, die garantiert Zeiger enthalten können:
uintptr_t p = (size_t) v;
Was ist der Unterschied zwischen size_t
und uintptr_t
? Beide sind nicht signiert und sollten in der Lage sein, jeden Zeigertyp zu halten, sodass sie funktional identisch zu sein scheinen. Gibt es einen wirklich zwingenden Grund, a uintptr_t
(oder besser noch void *
) a anstelle von a zu verwenden size_t
, außer Klarheit? Gibt es in einer undurchsichtigen Struktur, in der das Feld nur von internen Funktionen behandelt wird, einen Grund, dies nicht zu tun?
Aus dem gleichen Grund war ptrdiff_t
es ein vorzeichenbehafteter Typ, der Zeigerunterschiede aufnehmen kann und daher fast jeden Zeiger aufnehmen kann. Wie unterscheidet er sich also von intptr_t
?
Sind nicht alle diese Typen grundsätzlich trivial unterschiedliche Versionen derselben Funktion? Wenn nicht, warum? Was kann ich mit einem von ihnen nicht machen, was ich mit einem anderen nicht machen kann? Wenn ja, warum hat C99 der Sprache zwei im Wesentlichen überflüssige Typen hinzugefügt?
Ich bin bereit, Funktionszeiger zu ignorieren, da sie nicht für das aktuelle Problem gelten, aber ich kann sie gerne erwähnen, da ich den Verdacht habe, dass sie für die "richtige" Antwort von zentraler Bedeutung sind.
size_t
und,uintptr_t
aber was ist mitptrdiff_t
undintptr_t
- könnten nicht beide den gleichen Wertebereich auf fast jeder Plattform speichern? Warum haben sowohl vorzeichenbehaftete als auch vorzeichenlose Ganzzahltypen in Zeigergröße, insbesondere wenn sieptrdiff_t
bereits den Zweck eines Ganzzahlentyps in Vorzeichengröße mit Vorzeichen erfüllen?size_t
mindestens 16 Bit, aberptrdiff_t
mindestens 17 Bit betragen (was in der Praxis bedeutet, dass es wahrscheinlich mindestens 32 Bit sein wird).SIZE_MAX
dass er nicht 2 ** 64 sein sollte. Dies verwendet eine flache Adressierung, wohlgemerkt; Es ist keine Segmentierung erforderlich, um eine Nichtübereinstimmung zwischenSIZE_MAX
und dem Bereich eines Datenzeigers zu haben.Zu Ihrer Aussage:
Dies ist tatsächlich ein Irrtum (ein Missverständnis, das sich aus falschen Überlegungen ergibt) (a) . Sie können denken Letzteres aus Ersterem folgt, aber das ist eigentlich nicht der Fall.
Zeiger und Array-Indizes sind es nicht dasselbe. Es ist durchaus plausibel, sich eine konforme Implementierung vorzustellen, die Arrays auf 65536 Elemente beschränkt, es Zeigern jedoch ermöglicht, jeden Wert in einem massiven 128-Bit-Adressraum zu adressieren.
C99 gibt an, dass die Obergrenze einer
size_t
Variablen durch definiert istSIZE_MAX
und diese bis zu 65535 betragen kann (siehe C99 TR3, 7.18.3, unverändert in C11). Zeiger wären ziemlich begrenzt, wenn sie in modernen Systemen auf diesen Bereich beschränkt wären.In der Praxis werden Sie wahrscheinlich feststellen, dass Ihre Annahme zutrifft, aber das liegt nicht daran, dass der Standard dies garantiert. Weil es das eigentlich nicht garantiert.
(a) Dies ist übrigens keine Form von persönlichem Angriff, sondern nur die Begründung, warum Ihre Aussagen im Kontext des kritischen Denkens falsch sind. Zum Beispiel ist die folgende Argumentation ebenfalls ungültig:
Die Niedlichkeit oder das Gegenteil von Welpen spielt hier keine Rolle. Ich sage nur, dass die beiden Tatsachen nicht zu einer Schlussfolgerung führen, da die ersten beiden Sätze die Existenz von niedlichen Dingen zulassen, die es nicht sind Welpen sind.
Dies ähnelt Ihrer ersten Aussage, die nicht unbedingt die zweite vorschreibt.
quelle
ptrdiff_t
vs.-intptr_t
Teil) wiederholen .Ich werde alle anderen Antworten in Bezug auf die Argumentation mit Segmentbeschränkungen, exotischen Architekturen usw. für sich stehen lassen.
Ist der einfache Unterschied in den Namen nicht Grund genug, den richtigen Typ für das Richtige zu verwenden?
Wenn Sie eine Größe speichern, verwenden Sie
size_t
. Wenn Sie einen Zeiger speichern, verwenden Sieintptr_t
. Eine Person, die Ihren Code liest, wird sofort wissen, dass "aha, dies ist eine Größe von etwas, wahrscheinlich in Bytes" und "oh, hier ist ein Zeigerwert, der aus irgendeinem Grund als Ganzzahl gespeichert wird".Ansonsten könnte man einfach
unsigned long
(oder in diesen hier modernen Zeitenunsigned long long
) für alles verwenden. Größe ist nicht alles, Typnamen haben eine Bedeutung, die nützlich ist, da sie zur Beschreibung des Programms beitragen.quelle
size_t
Feld gespeichert wird .void*
,intptr_t
unduintptr_t
sind garantiert alle Zeiger auf Daten darstellen zu können.Möglicherweise ist die Größe des größten Arrays kleiner als ein Zeiger. Denken Sie an segmentierte Architekturen - Zeiger können 32-Bit sein, aber ein einzelnes Segment kann möglicherweise nur 64 KB adressieren (zum Beispiel die alte 8086-Architektur im Real-Modus).
Während diese auf Desktop-Computern nicht mehr häufig verwendet werden, soll der C-Standard auch kleine, spezialisierte Architekturen unterstützen. Es gibt immer noch eingebettete Systeme, die beispielsweise mit 8- oder 16-Bit-CPUs entwickelt werden.
quelle
size_t
auch damit umgehen können? Oder würden sich dynamische Arrays in einem weit entfernten Segment immer noch auf die Indizierung innerhalb ihres Segments beschränken?str
Funktionen und Borland auch für diemem
Funktionen (memset
,memcpy
,memmove
). Dies bedeutete, dass Sie einen Teil des Speichers überschreiben konnten, wenn der Offset überlief. Das Debuggen auf unserer eingebetteten Plattform hat Spaß gemacht.Ich würde mir vorstellen (und das gilt für alle Typnamen), dass es Ihre Absichten im Code besser vermittelt.
Zum Beispiel, obwohl
unsigned short
undwchar_t
unter Windows die gleiche Größe haben (glaube ich), zeigt die Verwendung vonwchar_t
anstelle vonunsigned short
die Absicht, dass Sie damit ein breites Zeichen und nicht nur eine beliebige Zahl speichern.quelle
wchar_t
ist es viel größer als einunsigned short
solches, so dass die Verwendung eines Systems für das andere fehlerhaft wäre und ein ernstes (und modernes) Portabilitätsproblem schafft, während das Portabilitätsproblem zwischensize_t
unduintptr_t
in den fernen Ländern zu liegen scheint von 1980-etwas (zufälliger Stich in die Dunkelheit am Datum, dort)size_t
und habenuintptr_t
immer noch Verwendungen in ihren Namen impliziert.Wenn ich sowohl vorwärts als auch rückwärts schaue und mich daran erinnere, dass verschiedene seltsame Architekturen über die Landschaft verstreut waren, bin ich mir ziemlich sicher, dass sie versucht haben, alle vorhandenen Systeme zu verpacken und auch alle möglichen zukünftigen Systeme bereitzustellen.
So sicher, wie sich die Dinge eingestellt haben, haben wir bisher nicht so viele Typen gebraucht.
Aber selbst in LP64, einem weit verbreiteten Paradigma, benötigten wir size_t und ssize_t für die Systemaufrufschnittstelle. Man kann sich ein eingeschränkteres Legacy- oder zukünftiges System vorstellen, bei dem die Verwendung eines vollständigen 64-Bit-Typs teuer ist und sie möglicherweise auf E / A-Operationen mit mehr als 4 GB zugreifen möchten, aber immer noch 64-Bit-Zeiger haben.
Ich denke, man muss sich fragen: Was könnte entwickelt worden sein, was könnte in Zukunft kommen. (Vielleicht internetweite 128-Bit-Zeiger für verteilte Systeme, aber nicht mehr als 64 Bit in einem Systemaufruf oder vielleicht sogar ein "Legacy" -32-Bit-Limit. :-) Stellen Sie sich vor, Legacy-Systeme könnten neue C-Compiler erhalten. .
Schauen Sie sich auch an, was es damals gab. Wie wäre es neben den zillion 286 Real-Mode-Speichermodellen mit den CDC-60-Bit-Wort- / 18-Bit-Zeiger-Mainframes? Wie wäre es mit der Cray-Serie? Egal, normales ILP64, LP64, LLP64. (Ich dachte immer, Microsoft sei mit LLP64 vorgetäuscht, es hätte P64 sein sollen.) Ich kann mir durchaus vorstellen, dass ein Komitee versucht, alle Grundlagen abzudecken ...
quelle
Dies bedeutet, dass intptr_t immer size_t ersetzen muss und umgekehrt.
quelle