2009-11-06 15 views
7

w c, próbowałem wydrukować adres zmiennej i adres jakiejś funkcji. Mam jeden jest wartością ujemną, drugi jest wartością dodatnią. Moje pytanie brzmi: dlaczego C nie reprezentuje we wszystkich wartościach ujemnych lub wszystkich dodatnich?adres pamięci wartość dodatnia lub ujemna w c?

Oto mój kod:

int foo() { 
    return 0; 
} 

int main() { 

    int a; 
    printf("%d\n",&a); 

    printf("%d\n",foo); 

    return 0; 
} 

Oto wynik:

-1075908992 134513684 

Odpowiedz

29

adresy pamięci nie powinien być interpretowany jako podpisanych liczb całkowitych w taki sposób. Znak liczby zależy od najwyższego zestawu bitów (zakładając dwójkową reprezentację dopełnienia, która jest używana przez większość obecnie używanych systemów), więc adres pamięci powyżej 0x80000000 w systemie 32-bitowym będzie ujemny, a pamięć adres poniżej 0x80000000 będzie dodatni. Nie ma to prawdziwego znaczenia.

Powinieneś drukować adresy pamięci za pomocą modyfikatora %p; alternatywnie niektóre osoby używają %08x do drukowania adresów pamięci (lub %016llx w systemach 64-bitowych). To zawsze będzie drukowane jako liczba całkowita bez znaku w systemie szesnastkowym, co jest znacznie bardziej użyteczne niż liczba dziesiętna ze znakiem.

int a; 
printf("%p\n", &a); 
+2

'% 08x' nie jest przenośny (i faktycznie być pomieszane na platformie LLP64, na przykład) . –

+2

Tak, dlatego powinieneś użyć '% p', ale nadal warto wspomnieć, że wiele osób używa'% 08x' na 32-bitowych komputerach. 99% czasu, jeśli drukujesz wartość wskaźnika, debugujesz coś, dzięki czemu wiesz dokładnie, jakiej architektury używasz. –

6

Należy zdać formatowania palików:

printf("%p\n",foo); 
3

Ani wskaźnik nie ma znaku, ale po przekonwertowaniu na liczbę całkowitą ze znakiem może być dodatni lub ujemny.

Nawiasem mówiąc, przekazanie wskaźnika do printf ze zmienną formatu przeznaczoną do drukowania liczby całkowitej powoduje niezdefiniowane zachowanie. Zawsze należy jawnie rzutować na poprawny typ, aby uzyskać nieokreślone lub zdefiniowane zachowanie implementacji (w zależności od obsady).

+0

Prawe; które mogłyby spowodować prawdziwe problemy w systemie, w którym wskaźniki i int nie były tej samej wielkości (jak większość systemów 64-bitowych). –

4

Drukujesz w systemie dziesiętnym. Prawdopodobnie chcesz wydrukować lokalizację pamięci w systemie szesnastkowym (używając "% p") (większość adresów pamięci jest pokazana w konwencji szesnastkowej zgodnie z konwencją).

0

Użyj następującego kodu:

printf("%u",&a); 
+0

To jest nieprawidłowe. % u jest dla unsigned ints, nie dla wskaźników. – ChrisInEdmonton

+1

Podoba mi się ten pomysł. wymusza adres jako pozytywny. – root

1

Można również użyć printf("%p", &a) również. %p to specyfikator formatu wskaźnika, który wyświetla wartość wskaźnika w sposób zdefiniowany przez implementację. Praktycznie rzecz biorąc, jest prawie taki sam jak% x, ale poradziłby sobie z przypadkiem, w którym wskaźnik jest większy niż int.

+0

Dobra uwaga. Musisz mieć najnowszy kompilator, aby to działało. (Nie śmiej się, często używam dekadowych kompilatorów przygnębiająco często). –

7

Aby być najbardziej zgodnym ze standardem, należy przekazać "% p" w ciągu formatu do printf() i przesłać argument do void*.

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

Cytat z projektu Standard (n1401.pdf)

 
    7.20.6.1 The fprintf function 

8 The conversion specifiers and their meanings are: 

p  The argument shall be a pointer to void. The value of the pointer 
     is converted to a sequence of printing characters, in an 
     implementation-defined manner. 
+2

Sugerowałbym podążanie za twoją radą i zastąpienie '% d' przez'% p' w przykładowym kodzie – Christoph

+0

+1 za wzmiankę o rzutowaniu na ' void * 'jako domyślna promocja argumentów nie wymaga żadnej konwersji wskaźnika, tzn. otrzymasz niezdefiniowane zachowanie na platformach, gdzie wskaźniki różnych typów mają różne reprezentacje – Christoph

0

printf nie jest jakimś magicznym narzędziem interpretacji. Wszystko, co robi, jeśli dasz mu "% d" pokazuje, jaki wzorzec bitowy siedzący w tej zmiennej wygląda jak liczba całkowita.

Co do tego, dlaczego niektóre wskaźniki są negatywne, a niektóre pozytywne, oznacza to, że niektóre mają ustawione wysokie wartości bitowe, a inne nie.To, jakie adresy są dane obiektom, jest tak naprawdę tylko działaniem Twojego systemu operacyjnego (zwykle z pomocą sprzętu). Język programowania nie ma w tym nic wspólnego.

Gdybym był tobą, nie wydrukowałbym adresów w ten sposób. Jeśli naprawdę chcesz na nie spojrzeć, użyj "% x" zamiast "% d". To wydrukuje je na heksach, co znacznie ułatwia ustalenie, które bity są włączone, a które nie. Ogólnie rzecz biorąc, wszystko, na co masz zamiar się martwić, to adresy, które mają wartość 0 lub nie i czy pasują do wartości innego adresu.

3

Wskaźniki nie są liczbami całkowitymi ze znakiem, a są bardziej podobne do liczb całkowitych bez znaku. Jednak drukujesz je tak, jakby były liczbami całkowitymi ze znakiem. W systemie 32-bitowym istnieje kilka rzeczy, które mogą mieć długość 32 bitów: int, , float oraz wskaźniki. Zasadniczo nie można powiedzieć, które z nich są po prostu patrząc na nie, ale wszystkie one oznaczają różne rzeczy. (W trakcie eksperymentu możesz przekazywać różne liczby całkowite i drukować je jako pływające i odwrotnie: ogólnie jest to zła praktyka, ale może pokazać, że różne typy danych oznaczają różne rzeczy.)

Funkcje, które przyjmują liczbę zmienną Argumenty (funkcje "variadic") robią to w skomplikowany sposób. W szczególności nie sprawdzają typów argumentów. Możesz przekazywać dowolne argumenty, które lubisz, do printf() i podobnych funkcji, ale Twoim problemem jest uzgodnienie ich ze specyfikatorem formatu. Oznacza to, że funkcja nie ma żadnego sposobu na uzyskanie prawidłowego typu (tak, jak gdybyś przekazał float do funkcji takiej jak int foo(int)).

Podawanie niewłaściwych typów argumentów prowadzi do niezdefiniowanego zachowania, a szczególnie może prowadzić do problemów w przypadku przekazania argumentów o niewłaściwym rozmiarze. W większości systemów 64-bitowych wskaźniki to 64-bitowe wartości, a int s to 32.

Dlatego należy użyć właściwej rzeczy w ciągu formatującym. printf("%p", &a); wydrukuje adres a jako wskaźnik, który jest tym, czego potrzebujesz. Standard wymaga czegoś takiego jak printf("%p", (void *)&a);, ale w praktyce jest to niepotrzebne na każdym komputerze, z którym możesz się kiedykolwiek zetknąć.

0

Oprócz użycia %p, można również przesłać do uintptr_t, aby uzyskać nieograniczoną liczbę całkowitą.

Zastosowanie PRIuPTR (lub PRIXPTR na hex) Forma <inttypes.h> aby uzyskać poprawny format specyfikatora:

printf("%" PRIuPTR "\n", (uintptr_t)(void *)&foo); 
Powiązane problemy