Odpowiedzi:
Najprostsza odpowiedź, przy założeniu, że nie przeszkadzają ci kaprysy i różnice w formacie między różnymi platformami, to standardowa %p
notacja.
Norma C99 (ISO / IEC 9899: 1999) mówi w §7.19.6.1 ¶8:
p
Argument powinien być wskaźnikiem dovoid
. Wartość wskaźnika jest konwertowana na ciąg znaków drukujących w sposób zdefiniowany w implementacji.
(W C11 - ISO / IEC 9899: 2011 - informacje podano w §7.21.6.1 ¶8.)
Na niektórych platformach, które będą zawierać wiodące, 0x
a na innych nie, a litery mogą być pisane małymi lub dużymi literami, a standard C nawet nie definiuje, że będzie to wyjście szesnastkowe, chociaż wiem o brak implementacji tam, gdzie jej nie ma.
W pewnym sensie można dyskutować, czy należy jawnie przekonwertować wskaźniki za pomocą (void *)
obsady. Jest to jawne, co zwykle jest dobre (tak właśnie robię), a standard mówi „argument powinien być wskaźnikiem do void
”. Na większości komputerów można uniknąć pominięcia jawnej obsady. Miałoby to jednak znaczenie na komputerze, w którym bitowa reprezentacja char *
adresu dla danej lokalizacji w pamięci różni się od adresu „ wskaźnika innego ” dla tej samej lokalizacji w pamięci. Byłaby to maszyna adresowana słowem, a nie bajtem. Takie maszyny nie są obecnie powszechne (prawdopodobnie niedostępne), ale pierwsza maszyna, nad którą pracowałem po studiach, była taka (ICL Perq).
Jeśli nie jesteś zadowolony z zachowania zdefiniowanego w implementacji %p
, użyj C99 <inttypes.h>
i uintptr_t
zamiast tego:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
Pozwala to na dostrojenie reprezentacji do własnych potrzeb. Wybrałem, aby cyfry szesnastkowe były pisane dużymi literami, aby liczba była równomiernie tej samej wysokości, a więc charakterystyczny spadek na początku 0xA1B2CDEF
pojawia się, a nie tak, 0xa1b2cdef
które spadają w górę i w dół wzdłuż liczby. Twój wybór jednak w bardzo szerokich granicach. (uintptr_t)
Obsada jest jednoznacznie zalecane przez GCC kiedy można go odczytać ciąg formatu w czasie kompilacji. Wydaje mi się, że należy poprosić o obsadę, ale jestem pewien, że są tacy, którzy zignorowaliby ostrzeżenie i unikali go przez większość czasu.
Kerrek pyta w komentarzach:
Jestem trochę zdezorientowany standardowymi promocjami i argumentami variadic. Czy wszystkie wskaźniki są standardowo promowane do unieważnienia *? W przeciwnym razie, gdyby
int*
były powiedzmy dwa bajty, avoid*
były 4 bajty, to oczywistym błędem byłoby odczytanie czterech bajtów z argumentu, non?
Byłem w iluzji, że standard C mówi, że wszystkie wskaźniki obiekt musi być tej samej wielkości, więc void *
i int *
nie mogą mieć różne rozmiary. Jednak to, co uważam za odpowiednią sekcję standardu C99, nie jest tak wyraźne (chociaż nie znam implementacji, w której to, co zasugerowałem, jest prawdą, jest w rzeczywistości fałszywe):
§6.2.5 Typy
26 Wskaźnik do unieważnienia powinien mieć takie same wymagania dotyczące reprezentacji i wyrównania, jak wskaźnik do typu znaku. 39) Podobnie wskaźniki do kwalifikowanych lub niekwalifikowanych wersji kompatybilnych typów powinny mieć takie same wymagania dotyczące reprezentacji i wyrównania. Wszystkie wskaźniki do typów konstrukcji powinny mieć takie same wymagania dotyczące reprezentacji i wyrównania. Wszystkie wskaźniki do typów unii mają takie same wymagania dotyczące reprezentacji i wyrównania. Wskaźniki do innych typów nie muszą mieć takich samych wymagań dotyczących reprezentacji lub wyrównania.
39) Te same wymagania dotyczące reprezentacji i wyrównania mają oznaczać zamienność argumentów do funkcji, zwracanych wartości z funkcji i członków związków.
(C11 mówi dokładnie to samo w rozdziale §6.2.5, ¶28 i przypis 48.)
Tak więc wszystkie wskaźniki do struktur muszą mieć taki sam rozmiar i muszą mieć te same wymagania dotyczące wyrównania, nawet jeśli struktury wskazane przez wskaźniki mogą mieć różne wymagania dotyczące wyrównania. Podobnie w przypadku związków. Wskaźniki znaków i pustki muszą mieć ten sam rozmiar i wymagania dotyczące wyrównania. Wskaźniki wariantów int
(znaczenie unsigned int
i signed int
) muszą mieć takie same wymagania co do wielkości i wyrównania; podobnie dla innych typów. Ale standard C nie mówi tego formalnie sizeof(int *) == sizeof(void *)
. No cóż, SO jest dobry do tego, abyś sprawdził swoje założenia.
Standard C definitywnie nie wymaga, aby wskaźniki funkcji były tego samego rozmiaru co wskaźniki obiektów. Było to konieczne, aby nie zepsuć różnych modeli pamięci w systemach podobnych do DOS. Tam możesz mieć 16-bitowe wskaźniki danych, ale 32-bitowe wskaźniki funkcji lub odwrotnie. To dlatego standard C nie wymaga, aby wskaźniki funkcji mogły być konwertowane na wskaźniki obiektów i odwrotnie.
Na szczęście (dla programistów atakujących POSIX) POSIX wkracza w błąd i nakazuje, aby wskaźniki funkcji i wskaźniki danych miały ten sam rozmiar:
§2.12.3 Typy wskaźników
Wszystkie typy wskaźników funkcji powinny mieć taką samą reprezentację jak wskaźnik typu, który ma zostać unieważniony. Konwersja wskaźnika funkcji na
void *
nie powinna zmieniać reprezentacji.void *
Wartość wynikająca z takiego przekształcenia można przekształcić z powrotem do pierwotnego typu wskaźnik funkcji, za pomocą wyraźnego obsady, bez utraty informacji.Uwaga: Norma ISO C nie wymaga tego, ale jest wymagana do zgodności z POSIX.
Wydaje się więc, że jawne rzuty void *
są zdecydowanie wskazane dla maksymalnej niezawodności w kodzie podczas przekazywania wskaźnika do funkcji variadic, takiej jak printf()
. W systemach POSIX można bezpiecznie umieścić wskaźnik funkcji na pustym wskaźniku w celu wydrukowania. W innych systemach niekoniecznie jest to bezpieczne, a także przekazywanie wskaźników innych niż void *
bez obsady.
dlsym()
funkcji. Pewnego dnia napiszę zmianę ... ale „jeden dzień” to nie „dziś”.
void *
? Hmm, widzę twój komentarz tutaj . Ponieważ potrzebna jest tylko konwersja jednego wata (wskaźnik funkcji do void *
), to działa?
void *
ai wstecz bez utraty informacji. Pragmatycznie jest bardzo mało maszyn, w których rozmiar wskaźnika funkcji nie jest taki sam jak rozmiar wskaźnika obiektu. Nie sądzę, że standard zapewnia metodę drukowania wskaźnika funkcji na komputerach, na których konwersja jest problematyczna.
p
jest specyfikatorem konwersji do drukowania wskaźników. Użyj tego.
int a = 42;
printf("%p\n", (void *) &a);
Pamiętaj, że pominięcie rzutowania jest nieokreślonym zachowaniem, a drukowanie ze p
specyfikatorem konwersji odbywa się w sposób zdefiniowany w implementacji.
Użyj %p
, jako „wskaźnik” i nie używaj niczego innego *. Standard nie gwarantuje, że możesz traktować wskaźnik jak jakikolwiek konkretny typ liczb całkowitych, więc w rzeczywistości uzyskasz niezdefiniowane zachowanie dzięki formatom integralnym. (Na przykład %u
oczekuje unsigned int
, ale co, jeśli void*
ma inny rozmiar lub wymaganie wyrównania niż unsigned int
?)
*) [Patrz Jonathana porządku odpowiedź!] Alternatywnie %p
, to może korzystać z makr wskaźnika od specyficznych <inttypes.h>
, dodane w C99.
Wszystkie wskaźniki obiektów są domyślnie konwertowalne na void*
C, ale aby przekazać wskaźnik jako argument variadic, musisz rzucić go jawnie (ponieważ dowolne wskaźniki obiektów są konwertowalne , ale nie identyczne jak wskaźniki void):
printf("x lives at %p.\n", (void*)&x);
void *
(chociaż dla printf()
ciebie technicznie potrzebujesz jawnego rzutowania, ponieważ jest to funkcja wariadyczna). Wskaźniki funkcji niekoniecznie są konwertowalne void *
.
void *
wskaźnik funkcji i wracały do wskaźnika funkcji bez utraty; na szczęście POSIX wyraźnie tego wymaga (zauważając, że nie jest to część standardu C). Tak więc w praktyce możesz sobie z tym poradzić (konwersja void (*function)(void)
do void *
i powrót do void (*function)(void)
), ale ściśle nie jest to wymagane przez standard C.
%u
!
%u
i %lu
są błędne na wszystkich komputerach , nie na niektórych komputerach. Specyfikacja printf
jest bardzo jasna, że gdy przekazany typ nie odpowiada typowi wymaganemu przez specyfikator formatu, zachowanie jest niezdefiniowane. Nie ma znaczenia, czy rozmiar typów pasuje (który może być prawdziwy, czy fałszywy, w zależności od maszyny); to typy, które muszą pasować i nigdy nie będą.
Jako alternatywę dla innych (bardzo dobrych) odpowiedzi można rzutować na uintptr_t
lub intptr_t
z stdint.h
/ inttypes.h
i używać odpowiednich specyfikatorów konwersji liczb całkowitych. Umożliwiłoby to większą elastyczność w sposobie formatowania wskaźnika, ale ściśle mówiąc, implementacja nie jest wymagana do zapewnienia tych typów czcionek.
#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
czy drukowanie adresu zmiennej przy użyciu %u
specyfikatora formatu jest niezdefiniowanym zachowaniem ? Adres zmiennej w większości przypadków jest dodatni, więc czy mogę użyć %u
zamiast %p
?
%u
jest formatem unsigned int
typu i nie można go używać z argumentem wskaźnika do printf
.
Możesz użyć %x
lub %X
lub %p
; wszystkie są poprawne.
%x
, adres jest podawany małymi literami, na przykład:a3bfbc4
%X
, adres jest podawany wielkimi literami, na przykład:A3BFBC4
Oba są poprawne.
Jeśli używasz %x
lub %X
rozważa sześć pozycji dla adresu, a jeśli używasz, bierze %p
pod uwagę osiem pozycji dla adresu. Na przykład:
void*
? W przeciwnym razie, gdybyint*
były powiedzmy dwa bajty, avoid*
były 4 bajty, to oczywistym błędem byłoby odczytanie czterech bajtów z argumentu, non?