2011-02-11 15 views
5

Brakuje mi tu coś oczywistego, ale pod uwagę następujące,Konwersja int int *, a następnie z powrotem do int

int k = 10; 
int *p = &k; 
int i = (int)p; 

powyżej produkuje,

warning: cast from pointer to integer of different size 

i następne

int k = 10; 
int *p = &k; 
int i = p; 

powoduje,

warning: initialization makes integer from pointer without a cast 

trochę googlowania prowadzić mnie,

Converting a pointer into an integer

która doradza przy użyciu uintptr_t,

int k = 10; 
int *p = &k; 
int i = (uintptr_t)p; 

powyżej działa bez błędów żadnych ostrzeżeń. Moje pytanie brzmi: int jest int, a p jest wskaźnikiem int wskazującym k, dlaczego dostaję błąd dereferencji p?

+0

Jaka jest wartość "i"? –

+0

Nie dereferencji 'p' w dowolnym miejscu w żadnym z przykładów. Czy możesz wyjaśnić swoje pytanie? – nmichaels

Odpowiedz

9
int k = 10; 
int *p = &k; 
int i = *p; 

Zwróć uwagę na * przed p na ostatnim wierszu. Usuń zaznaczenie wskaźnika, aby uzyskać punkt int, który wskazuje.

EDYCJA: Nie jestem bardzo znany z uintptr_t, ale uruchomiłem kod int i = (uintptr_t) p; Rezultatem zapisanym w i jest adres pamięci k, a nie 10. Obsada mówi kompilatorowi, że naprawdę chcesz zamienić adres na liczbę całkowitą (więc nie ma żadnego ostrzeżenia, ponieważ nalegasz na kompilator, który znasz co robisz), ale nie usuwa dereferencji wskaźnika.

+0

dziękuję za wyjaśnienia, ale dlaczego trzeci przykład nie usuwa żadnych błędów? –

+1

@Hamza Yerlikaya: Po pierwsze, żaden z twoich przykładów nie powodował żadnych * błędów * (nawet jeśli powinny one mieć). Wyprodukowali * ostrzeżenia *. Po drugie, ostrzeżenie nie wymaga wyjaśnień: rozmiar 'int' różni się od rozmiaru' int * '. Rozmiar 'uintptr_t' jest taki sam jak rozmiar' int * ', a więc nie ma w tym przypadku żadnego ostrzeżenia. – AnT

3

Otrzymujesz błąd, ponieważ w rzeczywistości nie usunąłeś adresu p, właśnie go rzuciłeś (zdecydowanie zmieniłeś jego typ) z int*. Aby dereference, trzeba użyć operatora *, tak:

int i = *p; 
2

Nie jesteś dereferencji wskaźnika, więc przypisujemy adres wskaźnik do int.

int i = *p; 

To, czego najprawdopodobniej chciałeś.

1

Grasz z int do odlewania wskaźnika, a to nie ma gwarancji, że dwaj nawet używają tej samej ilości pamięci. Właściwie to nie miała gwarancji, że użyje tego samego rozmiaru, kiedy ludzie to zrobili, ale na większości platform po prostu i tak zadziałało.

Ponieważ wyraźnie rzuca się w tym ostatnim przykładzie, kompilator zakłada, że ​​nie był to błąd (lub dlaczego został wpisany?). To jest przyczyna różnicy w wynikach kompilatora.

Należy pamiętać, że jeśli nie chce przechowywać adres pamięci K do I, a następnie w odpowiedni sposób, aby uzyskać wartość k jest w byłbym

int i = *p; 
0

to jak powinno wyglądać

int k = 10; 
int *p = &k; 
int i = *p; 

tutaj jest, jak myślę o tym

k = 10

p = jakiś adres

& k = jakiś adres

* p = 10

1

ustawiania i równa adres k (czego p trzyma). A wartość adresu jest typu bez znaku. Tak więc podczas rzucania wskaźnika, narzeka, ponieważ bierzesz casting i wartość bez znaku do podpisu.

Również wskaźniki są różnych rozmiarów typów w oparciu o platformę, więc wskaźniki 32-bitowe są mniejsze niż 64. Tak więc, jeśli wskaźnik jest 8-bajtowy niepodpisany, to odrzuca się go do 4-bajtowego podpisu.

0

uintptr_t nie jest przeznaczony do wykonywania rzutów pośrednich na int, ale używane jako takie. Jeśli jest zdefiniowany w systemie (a twój wydaje się, że to jest), to jest to liczba całkowita bez znaku, w której nie tracisz informacji podczas ładowania wartości wskaźnika. Więc jeśli musisz, użyj uintprt_t całkowicie zamiast int. Ale to powinno zdarzać się rzadko, aw większości przypadków mogę się założyć, to tylko oznaka złego projektu.

W każdym razie int to zły pomysł od samego początku. Jeśli cokolwiek, wartość wskaźnika jest po prostu pozycją w wirtualnej przestrzeni pamięci i postrzeganie jej jako wartości podpisanej nie ma dla mnie żadnego sensu.