Correcte formaatspecificatie om aanwijzer of adres af te drukken?

Welke formaatspecificatie moet ik gebruiken om het adres van een variabele af te drukken? Ik ben in de war tussen de onderstaande kavel.

%u – geheel getal zonder teken

%x – hexadecimale waarde

%p – ongeldige aanwijzer

Wat is het optimale formaat om een adres af te drukken?


Antwoord 1, autoriteit 100%

Het eenvoudigste antwoord, ervan uitgaande dat u de grillen en variaties in formaat tussen verschillende platforms niet erg vindt, is de standaard %p-notatie.

De C99-standaard (ISO/IEC 9899:1999) zegt in §7.19.6.1 ¶8:

pHet argument moet een verwijzing zijn naar void. De waarde van de aanwijzer is
geconverteerd naar een reeks afdruktekens, in een door de implementatie gedefinieerde
manier.

(In C11 — ISO/IEC 9899:2011 — staat de informatie in §7.21.6.1 ¶8.)

Op sommige platforms zal dat een leidende 0xbevatten en op andere niet, en de letters kunnen in kleine letters of hoofdletters zijn, en de C-standaard niet eens definieer dat het een hexadecimale uitvoer zal zijn, hoewel ik geen implementatie ken waar dit niet het geval is.

Het is enigszins open om te debatteren of u de wijzers uitdrukkelijk moet converteren met een (void *)cast. Het is expliciet, wat meestal goed is (dus het is wat ik doe), en de norm zegt: ‘Het argument is een aanwijzer op void‘. Op de meeste machines zou je wegkomen met het weglaten van een expliciete cast. Het zou echter uitmaken op een machine waar de bitvertegenwoordiging van een char *adres voor een bepaalde geheugenlocatie verschilt van het adres van ‘-aanwijzer voor hetzelfde geheugen plaats. Dit zou een woord-geadresseerd zijn, in plaats van byte-geadresseerd, machine. Dergelijke machines zijn niet gebruikelijk (waarschijnlijk niet beschikbaar) tegenwoordig, maar de eerste machine die ik heb gewerkt aan de universiteit was een dergelijke (ICL PERQ).

Als u niet blij bent met het implementatie-gedefinieerde gedrag van %p, gebruik dan C99 <inttypes.h>en uintptr_tIn plaats daarvan:

printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);

Hiermee kunt u de representatie voor uzelf afstemmen. Ik koos ervoor om de hex-cijfers in hoofdletters te hebben, zodat het nummer uniform dezelfde hoogte is en de karakteristieke dip aan het begin van 0xA1B2CDEFverschijnt, niet zoals 0xA1B2CDEFdaalt ook op en neer langs het aantal. Je keuze, binnen zeer brede limieten. De (uintptr_t)CAST is ondubbelzinnig aanbevolen door GCC wanneer het de indelingsnoer op compileertijd kan lezen. Ik denk dat het correct is om de cast aan te vragen, hoewel ik zeker weet dat er sommigen zijn die de waarschuwing zouden negeren en er het grootste deel van de tijd mee kunnen verwijderen.


Kerrekt vraagt ​​in de opmerkingen:

Ik ben een beetje in de war over standaardpromoties en variadische argumenten. Worden alle pointers standaard gepromoveerd tot ongeldig*? Anders, als int*bijvoorbeeld twee bytes was, en void*4 bytes, dan zou het duidelijk een fout zijn om vier bytes van het argument te lezen, niet ?

Ik had de illusie dat de C-standaard zegt dat alle objectpointers dezelfde grootte moeten hebben, dus void *en int *kunnen niet van verschillende grootte zijn. Ik denk echter dat het relevante gedeelte van de C99-standaard niet zo nadrukkelijk is (hoewel ik geen implementatie ken waarbij wat ik suggereerde waar is, eigenlijk onwaar is):

§6.2.5 Soorten

¶26 Een pointer to void moet dezelfde representatie- en uitlijningsvereisten hebben als een pointer naar een tekentype.39)Evenzo moeten pointers naar gekwalificeerde of niet-gekwalificeerde versies van compatibele typen dezelfde representatie hebben en uitlijningsvereisten. Alle verwijzingen naar structuurtypen moeten dezelfde vereisten voor weergave en uitlijning hebben als elkaar. Alle verwijzingen naar verbindingstypen moeten dezelfde vereisten voor weergave en uitlijning hebben als elkaar. Aanwijzers naar andere typen hoeven niet dezelfde vereisten voor weergave of uitlijning te hebben.

39)Dezelfde vereisten voor representatie en uitlijning zijn bedoeld om uitwisselbaarheid te impliceren als argumenten voor functies, retourwaarden van functies en leden van vakbonden.

(C11 zegt precies hetzelfde in de secties §6.2.5, ¶28 en voetnoot 48.)

Dus alle aanwijzers naar structuren moeten even groot zijn en dezelfde uitlijningsvereisten hebben, ook al hebben de structuren waarnaar de aanwijzers wijzen mogelijk verschillende uitlijningsvereisten. Zo ook voor vakbonden. Tekenaanwijzers en lege aanwijzers moeten dezelfde vereisten voor grootte en uitlijning hebben. Verwijzingen naar variaties op int(wat betekent unsigned inten signed int) moeten dezelfde grootte en uitlijningsvereisten hebben; hetzelfde voor andere typen. Maar de C-standaard zegt formeel niet dat sizeof(int *) == sizeof(void *). Nou ja, SO is goed om je aannames te laten inspecteren.

De C-standaard vereist absoluut niet dat functiewijzers dezelfde grootte hebben als objectwijzers. Dat was nodig om de verschillende geheugenmodellen op DOS-achtige systemen niet te breken. Daar zou je 16-bits datapointers kunnen hebben, maar 32-bits functiepointers, of omgekeerd. Dit is de reden waarom de C-standaard niet vereist dat functiewijzers kunnen worden geconverteerd naar objectwijzers en vice versa.

Gelukkig (voor programmeurs die zich op POSIX richten), stapt POSIX in de bres en schrijft het voor dat functiewijzers en gegevenswijzers even groot zijn:

§2.12.3 Aanwijzertypen

Alle typen functieaanwijzers moeten dezelfde weergave hebben als de typeaanwijzer om te annuleren. Conversie van een functieaanwijzer naar void *zal de weergave niet veranderen. Een void *-waarde die het resultaat is van een dergelijke conversie, kan worden teruggeconverteerd naar het oorspronkelijke type functieaanwijzer, met behulp van een expliciete cast, zonder verlies van informatie.

Opmerking:
De ISO C-standaard vereist dit niet, maar het is vereist voor POSIX-conformiteit.

Het lijkt er dus op dat expliciete casts naar void *sterk worden aanbevolen voor maximale betrouwbaarheid in de code bij het doorgeven van een aanwijzer naar een variadische functie zoals printf(). Op POSIX-systemen is het veilig om een functieaanwijzer naar een lege aanwijzer te casten om af te drukken. Op andere systemen is het niet per se veilig om dat te doen, en het is ook niet per se veilig om andere pointers dan void *door te geven zonder cast.


Antwoord 2, autoriteit 22%

pis de conversiespecificatie om pointers af te drukken. Gebruik dit.

int a = 42;
printf("%p\n", (void *) &a);

Onthoud dat het weglaten van de cast ongedefinieerd gedrag is en dat het afdrukken met pconversiespecificatie op een door de implementatie gedefinieerde manier wordt gedaan.


Antwoord 3, autoriteit 12%

Gebruik %p, voor “pointer”, en gebruik niets anders*. Je bent niet gegarandeerd door de standaard dat je een aanwijzer als een bepaald type geheel getal mag behandelen, dus je zou eigenlijk ongedefinieerd gedrag krijgen met de integrale formaten. (Bijvoorbeeld, %uverwacht een unsigned int, maar wat als void*een andere grootte of uitlijnvereiste heeft dan unsigned int?)

*) [Zie het goede antwoord van Jonathan!] Als alternatief voor %pkunt u kuntaanwijzerspecifieke macro’s gebruiken uit <inttypes.h>, toegevoegd in C99.

Alle objectaanwijzers zijn impliciet converteerbaar naar void*in C, maar om de aanwijzer als een variadisch argument door te geven, moet u deze expliciet casten (aangezien willekeurige objectaanwijzers slechts zijn) converteerbaar, maar niet identiekvoor ongeldige aanwijzers):

printf("x lives at %p.\n", (void*)&x);

Antwoord 4, autoriteit 3%

Als alternatief voor de andere (zeer goede) antwoorden, kun je casten naar uintptr_tof intptr_t(van stdint.h/inttypes.h) en gebruik de overeenkomstige conversiespecificaties voor gehele getallen. Dit zou meer flexibiliteit mogelijk maken in de opmaak van de aanwijzer, maar strikt genomen is er geen implementatie vereist om deze typedefs te bieden.


Antwoord 5

U kunt %Xof %Xof %pgebruiken; ze zijn allemaal correct.

  • Als u %Xgebruikt, wordt het adres in kleine letters weergegeven, bijvoorbeeld: A3BFBC4
  • Als u %Xgebruikt, wordt het adres als hoofdletter weergegeven, bijvoorbeeld: A3BFBC4

Beide zijn correct.

Als u %Xof %Xgebruikt, overweegt het zes posities voor het adres, en als u %pgebruikt, overweegt het acht posities voor het adres. Bijvoorbeeld:

Other episodes