Natknąłem się na następującą zagadkę C:
P: Dlaczego następujący program segfaulta na IA-64, ale działa dobrze na IA-32?
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
Wiem, że rozmiar int
na komputerze 64-bitowym może nie być taki sam jak rozmiar wskaźnika ( int
może wynosić 32 bity, a wskaźnik może mieć 64 bity). Ale nie jestem pewien, jak to się ma do powyższego programu. Jakieś pomysły?
c
pointers
segmentation-fault
użytkownik7
źródło
źródło
stdlib.h
nie uwzględnienie?#include stdlib.h
(dla malloc)#include <stdlib.h>
, jest doskonale znaleziony, ale nie o to chodzi.sizeof(int) == sizeof(int*)
, jeśli na przykład wskaźniki zostaną zwrócone, chociaż rejestr inny niżint
s w używanej konwencji wywoływania.malloc()
. GCC mówi:warning: incompatible implicit declaration of built-in function 'malloc'
też.Odpowiedzi:
Rzut
int*
maskuje fakt, że bez właściwego#include
przyjmuje się, że zwracany typmalloc
toint
. IA-64 tak sięsizeof(int) < sizeof(int*)
składa, że ten problem jest oczywisty.(Należy również zauważyć, że z powodu nieokreślonego zachowania może nadal zawieść nawet na platformie, na której
sizeof(int)==sizeof(int*)
jest prawdziwe, na przykład jeśli konwencja wywoływania używa innych rejestrów do zwracania wskaźników niż liczby całkowite)W sekcji często zadawanych pytań dotyczących comp.lang.c znajduje się wpis omawiający, dlaczego odlewanie wyniku zwrotnego
malloc
nigdy nie jest potrzebne i jest potencjalnie złe .źródło
new
w C ++ i zawsze kompilować C za pomocą kompilatora C, a nie kompilatora C ++.int
, że jakikolwiek typ zwrotu jest nieznanymalloc
)void*
. Ale kod wywołujący uważa, że funkcja zwracaint
(ponieważ zdecydowałeś nie mówić inaczej), więc próbuje odczytać wartość zwracaną zgodnie z konwencją wywoływania funkcjiint
. Stądp
nie nie muszą wskazywać na przydzielonej pamięci. Tak się złożyło, że zadziałało dla IA32, ponieważint
i avoid*
są tego samego rozmiaru i zwrócone w ten sam sposób. Na IA64 otrzymujesz złą wartość.Najprawdopodobniej dlatego, że nie dołączasz pliku nagłówkowego dla
malloc
i, chociaż kompilator normalnie ostrzegłby Cię o tym, fakt, że jawnie rzutujesz wartość zwracaną, oznacza, że mówisz mu, że wiesz, co robisz.Oznacza to, że kompilator oczekuje
int
zwrócenia kodu, zmalloc
którego następnie rzutuje na wskaźnik. Jeśli mają różne rozmiary, spowoduje to smutek.Dlatego nigdy nie rzutujesz
malloc
powrotu w C.void*
Zwracany przez niego element zostanie niejawnie przekonwertowany na wskaźnik odpowiedniego typu (chyba że nie dołączyłeś nagłówka, w którym to przypadku prawdopodobnie ostrzegłby cię o potencjalnie niebezpiecznym int- do konwersji wskaźnika).źródło
void *
można go niejawnie przekonwertować na dowolny inny typ wskaźnika.int *p = malloc(sizeof(int))
działa, jeśli właściwy prototyp znajduje się w zakresie i kończy się niepowodzeniem, jeśli nie jest (ponieważ zakłada się, że wynik jestint
). Z rzutowaniem oba skompilowałyby się, a to drugie spowodowałoby błędy, kiedysizeof(int) != sizeof(void *)
.stdlib.h
, kompilator nie będzie wiedział,malloc
ani jego typu zwracanego. Więc po prostu przyjmujeint
jako domyślne.Dlatego nigdy nie kompilujesz bez ostrzeżeń o brakujących prototypach.
Rzutowanie jest potrzebne do zapewnienia zgodności z C ++. Nie ma powodu (czytaj: nie ma powodu), aby go pominąć.
Zgodność z C ++ nie zawsze jest potrzebna, aw kilku przypadkach nie jest możliwa, ale w większości przypadków jest to bardzo łatwe do osiągnięcia.
źródło