Zanim ludzie zaczną oznaczać to dupkiem, przeczytałem wszystkie poniższe rzeczy, z których żadna nie dostarcza odpowiedzi, której szukam:
- C FAQ: Co jest złego w rzutowaniu wartości zwracanej przez malloc?
- SO: Czy powinienem jawnie rzutować wartość zwracaną przez malloc ()?
- SO: niepotrzebne rzutowanie wskaźnika w C
- SO: Czy mogę rzucić wynik malloc?
Zarówno C FAQ, jak i wiele odpowiedzi na powyższe pytania cytuje tajemniczy błąd, który malloc
może ukryć wartość zwracana przez rzutowanie ; jednak żaden z nich nie podaje konkretnego przykładu takiego błędu w praktyce. Teraz zwróć uwagę, że powiedziałem błąd , a nie ostrzeżenie .
Teraz otrzymałem następujący kod:
#include <string.h>
#include <stdio.h>
// #include <stdlib.h>
int main(int argc, char** argv) {
char * p = /*(char*)*/malloc(10);
strcpy(p, "hello");
printf("%s\n", p);
return 0;
}
Kompilowanie powyższego kodu za pomocą gcc 4.2, z rzutowaniem i bez, daje te same ostrzeżenia, a program działa poprawnie i zapewnia te same wyniki w obu przypadkach.
anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc
hello
Czy zatem ktoś może podać konkretny przykład kodu błędu kompilacji lub wykonania, który może wystąpić z powodu malloc
wartości zwracanej rzutowania , czy jest to tylko miejska legenda?
Edytuj Natknąłem się na dwa dobrze napisane argumenty dotyczące tej kwestii:
- Za rzutowaniem : CERT Advisory: Natychmiast rzutowanie wyniku wywołania funkcji alokacji pamięci na wskaźnik do przydzielonego typu
- Przeciwko przesyłaniu (błąd 404 z 14.02.2012: użyj kopii Internet Archive Wayback Machine z 27.01.2010. {2016-03-18: "Strona nie może zostać zaindeksowana lub wyświetlona z powodu pliku robots.txt."})
void
wskaźników pozwala skompilować kod w C ++; niektórzy mówią, że to funkcja, powiedziałbym, że to błąd;)malloc
wartości zwracanej rzutowania: rzutowanieint*
na arch. 64-bitowe.C
nieC++
(są to dwa różne języki), więc żadna dyskusja (jak w niektórych odpowiedziach) nie ma związku z tym pytaniem.Odpowiedzi:
Nie otrzymasz błędu kompilatora , ale ostrzeżenie kompilatora . Jako źródła zacytowanie głosu (zwłaszcza pierwszy z nich ), użytkownik może uzyskać nieprzewidziany błąd runtime podczas korzystania z obsady bez włącznie
stdlib.h
.Więc błąd po twojej stronie nie polega na obsadzie, ale na zapominaniu o włączeniu
stdlib.h
. Kompilatory mogą założyć, żemalloc
jest to funkcja zwracającaint
, dlatego konwertujevoid*
wskaźnik faktycznie zwracany przezmalloc
naint
a następnie na typ wskaźnika z powodu jawnego rzutowania. Na niektórych platformachint
wskaźniki mogą zajmować różną liczbę bajtów, więc konwersja typów może prowadzić do uszkodzenia danych.Na szczęście współczesne kompilatory ostrzegają o twoim rzeczywistym błędzie. Zobacz dane
gcc
wyjściowe, które dostarczyłeś: Ostrzega, że niejawna deklaracja (int malloc(int)
) jest niekompatybilna z wbudowanąmalloc
. Więcgcc
wydaje się wiedziećmalloc
nawet bezstdlib.h
.Pozostawienie obsady, aby zapobiec temu błędowi, jest w większości tego samego powodu co pisanie
if (0 == my_var)
zamiast
if (my_var == 0)
ponieważ ta ostatnia może prowadzić do poważnego błędu, jeśli ktoś się pomyli
=
i==
, podczas gdy pierwszy z nich doprowadziłby do błędu kompilacji. Osobiście wolę ten drugi styl, ponieważ lepiej odzwierciedla moje zamiary i nie popełniam tego błędu.To samo dotyczy rzutowania wartości zwracanej przez
malloc
: wolę być jawny w programowaniu i generalnie dwukrotnie sprawdzam, czy pliki nagłówkowe są dołączone do wszystkich funkcji, których używam.źródło
stdlib.h
jest już samym błędem, nawet jeśli otrzymujesz tylko ostrzeżenia o „niejawnej deklaracji”.Jeden z dobrych argumentów wyższego poziomu przeciwko rzutowaniu wyniku
malloc
jest często pomijany, chociaż moim zdaniem jest on ważniejszy niż dobrze znane problemy niższego poziomu (takie jak obcięcie wskaźnika, gdy brakuje deklaracji).Dobrą praktyką programistyczną jest pisanie kodu możliwie niezależnego od typu. Oznacza to w szczególności, że nazwy typów powinny być wymieniane w kodzie jak najmniej lub najlepiej nie wspominane w ogóle. Dotyczy to rzutów (unikaj niepotrzebnych rzutów), typów jako argumentów
sizeof
(unikaj używania nazw typów wsizeof
) i ogólnie wszystkich innych odwołań do nazw typów.Nazwy typów należą do deklaracji. O ile to możliwe, nazwy typów powinny być ograniczone do deklaracji i tylko do deklaracji.
Z tego punktu widzenia ten fragment kodu jest zły
int *p; ... p = (int*) malloc(n * sizeof(int));
a to jest o wiele lepsze
int *p; ... p = malloc(n * sizeof *p);
nie tylko dlatego, że "nie
malloc
rzutuje wyniku ", ale raczej dlatego, że jest niezależny od typu (lub niezależny od typu, jeśli wolisz), ponieważ automatycznie dostosowuje się do dowolnegop
zadeklarowanego typu, bez konieczności jakiejkolwiek interwencji ze strony użytkownik.źródło
Zakłada się, że funkcje nieoprototypowane powrócą
int
.Więc rzucasz
int
do wskaźnika. Jeśli wskaźnikiint
na Twojej platformie są szersze niż s, jest to bardzo ryzykowne zachowanie.Plus, oczywiście, że niektórzy ludzie uważają ostrzeżenia za błędy, tj. Kod powinien się kompilować bez nich.
Osobiście uważam, że fakt, że nie trzeba rzutować
void *
na inny typ wskaźnika, jest funkcją w C i uważam, że kod, który jest uszkodzony.źródło
void*
.int
." - Czy masz na myśli, że można zmienić typ zwracanych funkcji nie-prototypowych?#ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif
. Teraz, dlaczego chcesz wywołać malloc w statycznej funkcji wbudowanej, naprawdę nie wiem, ale nagłówki, które działają w obu, są prawie niespotykane.Jeśli zrobisz to podczas kompilacji w trybie 64-bitowym, zwrócony wskaźnik zostanie obcięty do 32-bitów.
EDYCJA: Przepraszam, że jestem zbyt krótki. Oto przykładowy fragment kodu do celów dyskusji.
Załóżmy, że zwrócony wskaźnik sterty jest czymś większym niż to, co można przedstawić w int, powiedzmy 0xAB00000000.
Jeśli malloc nie ma prototypu, aby zwrócić wskaźnik, zwrócona wartość int będzie początkowo znajdować się w jakimś rejestrze ze wszystkimi ustawionymi znaczącymi bitami. Teraz kompilator powie: „OK, jak mogę przekonwertować i int na wskaźnik”. Będzie to albo rozszerzenie znaku, albo rozszerzenie zerowe 32-bitowych najmniej znaczących bitów, o których powiedziano, że malloc „zwraca” przez pominięcie prototypu. Ponieważ int jest podpisany, myślę, że konwersja będzie rozszerzeniem znaku, które w tym przypadku konwertuje wartość na zero. Zwracając wartość 0xABF0000000, otrzymasz niezerowy wskaźnik, który również sprawi trochę radości, gdy spróbujesz go wyłuskać.
źródło
Reguła dotycząca oprogramowania wielokrotnego użytku:
W przypadku pisania funkcji inline, w której użyto malloc (), aby umożliwić jej wielokrotne użycie również dla kodu C ++, należy wykonać jawne rzutowanie typu (np. (Char *)); w przeciwnym razie kompilator będzie narzekał.
źródło
malloc
/free
dla obu jest w stanie lepiej niż stara się wykorzystaćmalloc
w C inew
C ++, zwłaszcza jeśli struktury danych są dzielone między C i C ++ kod i istnieje możliwość, że obiekt może zostać utworzony w kodzie C i wydany w kodzie C ++ lub odwrotnie.Wskaźnik void w C można przypisać do dowolnego wskaźnika bez jawnego rzutowania. Kompilator wyświetli ostrzeżenie, ale może być ponownie użyty w C ++ według rzutowania typu
malloc()
na odpowiedni typ. Bez rzutowania typu out może być również używany w C , ponieważ C nie jest ścisłym sprawdzaniem typu . Ale C ++ służy wyłącznie do sprawdzania typów, więc wymagane jest rzucanie typówmalloc()
w C ++.źródło