Warum/wann `intptr_t` für die Typumwandlung in C verwenden?

Lesezeit: 7 Minuten

Benutzeravatar von william_grisaitis
william_grisaitis

Ich habe eine Frage zur Verwendung intptr_t vs. long int. Ich habe beobachtet, dass sich das Inkrementieren von Speicheradressen (z. B. über manuelle Zeigerarithmetik) je nach Datentyp unterscheidet. Zum Beispiel fügt das Erhöhen eines char-Zeigers 1 zur Speicheradresse hinzu, während das Erhöhen eines int-Zeigers 4, 8 für ein Double, 16 für ein langes Double usw. hinzufügt.

Anfangs habe ich so etwas gemacht:

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:\n\n" );
printf( "pChar:  %d\n", (int)pChar );
printf( "pFloat:    %d\n", (int)pFloat );

was gut kompiliert und ausgeführt wurde, aber XCode gab mir Warnungen für meine Typumwandlung: “Vom Zeiger in eine Ganzzahl unterschiedlicher Größe umwandeln.”

Nach einigem Googeln und Bingen (ist letzteres schon ein Wort?) sah ich, dass einige Leute die Verwendung empfahlen intptr_t:

#include <stdint.h>

printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

was die Fehler tatsächlich behebt. Also dachte ich, von jetzt an sollte ich verwenden intptr_t für Typumwandlungszeiger … Aber dann, nach einigem Herumzappeln, fand ich heraus, dass ich das Problem lösen konnte, indem ich einfach ersetzte int mit long int:

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

Meine Frage ist also, warum intptr_t nützlich, und wann sollte es verwendet werden? Es scheint in diesem Fall überflüssig. Klar, die Speicheradressen für myChar und myFloat waren einfach zu groß, um in einen zu passen int… also typisieren sie sie long ints hat das Problem gelöst.

Liegt es daran, dass Speicheradressen manchmal zu groß sind? long int auch? Jetzt, wo ich darüber nachdenke, denke ich, dass dies möglich ist, wenn Sie > 4 GB RAM haben. In diesem Fall könnten Speicheradressen 2 ^ 32 – 1 überschreiten (Maximalwert für vorzeichenlose lange Ints …), aber C wurde lange davor erstellt war vorstellbar, oder? Oder waren sie so vorausschauend?

Vielen Dank!

  • Ja, Binging ist ein Wort, das ursprünglich bedeutete, sich einer Aktivität, insbesondere dem Essen, bis zum Exzess hinzugeben.

    – Ack

    4. Juli 2014 um 17:13 Uhr

  • Verwenden intptr_t mit den printf-Funktionen ist nicht portierbar, es gibt keinen Formatbezeichner dafür. Wandeln Sie den Zeiger stattdessen in einen void-Zeiger um und verwenden Sie die %p Formatbezeichner.

    – osven

    19. November 2017 um 13:52 Uhr

intptr_t ist eine neue Erfindung, die geschaffen wurde, nachdem 64-Bit- und sogar 128-Bit-Speicheradressen vorgestellt wurden.

Wenn du je müssen einen Zeiger in einen Integer-Typ umwandeln, stets verwenden intptr_t. Alles andere verursacht unnötige Probleme für Personen, die Ihren Code in Zukunft portieren müssen.

Es hat lange gedauert, alle Fehler damit in Programmen wie Mozilla/Firefox auszubügeln, wenn Leute es auf 64-Bit-Linux kompilieren wollten.

  • Es wäre besser zu verwenden uintptr_t. Signierte Typen sind chaotisch und machen mit Zeigern sicherlich keinen Sinn …

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    13. Juni 2011 um 5:10 Uhr

  • Guter Anruf. Alles, worüber ich geschrieben sehe uintptr_t ist im Kontext von c++ und der inttypes.h Bibliothek. sieht aus als wäre es drin stdint.h, allerdings auch. jede Differenz?

    – william_grisaitis

    13. Juni 2011 um 5:30 Uhr

  • Apropos stets verwenden intptr_t Diese Antwort behauptet, dass sie nicht allgemein verfügbar ist stackoverflow.com/a/9492910/1073672 . Was also tun?

    – Benutzer10607

    23. Dezember 2014 um 7:11 Uhr


  • @ user10607: Es ist ab C99 verfügbar. Das ist mehr als ein Jahrzehnt. Es sei denn, Sie verwenden einen alten Compiler wie TurboC oder eine sehr exotische Plattform ohne die Absicht, einen Zeiger zu halten Wille verfügbar sein 😉

    – MestreLion

    17. Februar 2015 um 6:13 Uhr

  • @MestreLion das stimmt nicht, es ist optional in C99

    – osven

    19. November 2017 um 13:46 Uhr

Hier ist die Sache: Auf einigen Plattformen int hat die richtige Größe, aber bei anderen long hat die richtige Größe. Woher wissen Sie, welches Sie verwenden sollten? Du nicht. Man könnte Recht haben, aber der Standard gibt keine Garantie dafür, welches es wäre (wenn es eines von beiden ist). Der Standard bietet also einen Typ, der die richtige Größe hat, unabhängig davon, auf welcher Plattform Sie sich befinden. Wo man vorher schreiben musste:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

Jetzt schreibst du einfach:

#include <stdint.h>

Und es deckt so viele weitere Fälle ab. Stellen Sie sich vor, Sie spezialisieren das obige Snippet auf jede einzelne Plattform Ihr Code läuft weiter.

  • Hm, danke für die Klarstellung. Was wäre, wenn Sie gerade verwendet haben long die ganze Zeit? Würde nicht long auch mit kürzeren Adressen arbeiten? Funktioniert die zusätzliche Speicherzuweisung für longs vs ints die Leistung erheblich beeinträchtigen?

    – william_grisaitis

    13. Juni 2011 um 4:22 Uhr


  • @caravaggisto – Nein. long ist nur garantiert 32 Bit und funktioniert möglicherweise nicht auf einer 64-Bit-Plattform. Könntest du immer gebrauchen long long, aber das existiert nicht vor C99. Ich bezweifle, dass die Zuordnung viel ausmacht. Es ist eher ein Problem der Korrektheit.

    – Chris Lutz

    13. Juni 2011 um 4:27 Uhr

  • Ich wäre sehr skeptisch gegenüber jedem C-Buch, das Ihnen beibringt, zwischen Zeigern und ganzen Zahlen umzuwandeln. Eigentlich weisen fast alle Casts auf Bugs hin; Sie sind zumindest ein Code-Geruch. Pointer/Integer-Casts sind viel schlimmer.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    13. Juni 2011 um 5:12 Uhr

  • @R..: Häufig hat eine API, die eine Rückruffunktion bereitstellt, oder eine Messaging-API wie die Windows-Nachrichtenpumpe ein oder zwei ganzzahlige Datenfelder, die weitergegeben werden können. Ihr Programm muss möglicherweise einen Zeiger in a einfügen DWORD oder ein int denn das ist alles, was in der API verfügbar ist.

    – Zan Luchs

    5. September 2012 um 15:41 Uhr

  • @ZanLynx WinAPI ist eine schreckliche API, die Mitte der 80er Jahre für schrecklich kaputte Pre-Standard-C-Compiler entwickelt wurde. Es hat sich seit der Win16-Ära nicht viel geändert. Es gibt einen Grund, warum MS versucht, es zu töten.

    – Jonathan Baldwin

    10. März 2014 um 22:28 Uhr

Zuerst, intptr_t ist nur für Datenzeiger (keine Funktionen) und ist nicht garantiert vorhanden.

Dann, nein, Sie sollten es nicht zum Drucken verwenden. Das %p ist dafür. Sie müssen nur Ihren Zeiger auf werfen (void*) und los gehts.

Es ist auch nicht gut für die Arithmetik / den Zugriff auf einzelne Bytes. Cast zu (unsigned char*) stattdessen.

intptr_t ist wirklich für die seltenen Fälle, in denen Sie Zeiger als ganze Zahlen interpretieren müssen (was sie wirklich nicht sind). Tun Sie das nicht, wenn Sie nicht müssen.

  • Danke. Was sind denn Zeiger, wenn nicht (binäre) ganze Zahlen? Vielen Dank für Ihre Zeit.

    – william_grisaitis

    14. Juni 2011 um 16:57 Uhr

  • @caravaggisto, das hängt stark von der Architektur ab. Einige Architekturen haben einen segmentierten Speicher, sodass eine Adresse aus zwei Teilen besteht. Nur eine einfache Integer-Addition ergibt beispielsweise nicht immer eine gültige Adresse. Arithmetik an unsigned char reicht aus, vorausgesetzt, Sie haben einen Chunk zugewiesen, der groß genug ist.

    – Jens Gustedt

    14. Juni 2011 um 18:17 Uhr

  • intptr_t oder uintptr_t könnte für bestimmte Szenarien nützlich sein. Zum Beispiel bauen Sie Ihre eigene Version von malloc()B. ein spezialisierter dynamischer Speicherzuordner, vielleicht für ein eingebettetes System oder ein anderes Computersystem, wo ein spezialisierter Speicherzuordner benötigt wird. Angenommen, Sie behalten den Überblick über freigegebene Blöcke in einer expliziten frei verknüpften Liste. Sie könnten ein intptr_t verwenden, um Zeiger auf die nächsten und vorherigen Blöcke in der verknüpften Liste zu speichern. Dies wäre nicht Teil der Nutzlast selbst, sondern das vorherige Wort davor in einer “Kopfzeile” oder “Fußzeile” des Blocks.

    – Galaxie

    4. Juli 2018 um 22:42 Uhr

Sie könnten Ihr Leben einfacher machen, indem Sie die verwenden p Konvertierungsbezeichner:

printf("%p\n", (void *)foo);

Auch die portable Möglichkeit, eine Variable vom Typ zu drucken (u)intptr_t ist die zu verwenden PRI*PTR Makros aus inttypes.h; Folgendes entspricht der Verwendung p auf meiner Plattform (32-Bit):

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

Die wirft zu void * sind für volle Portabilität notwendig, können aber auf Plattformen mit einheitlichen Zeigerdarstellungen weggelassen werden.

1411630cookie-checkWarum/wann `intptr_t` für die Typumwandlung in C verwenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy