Czy rzucam wynik malloc?


2406

W tej kwestii , ktoś sugerowane w komentarzu , że powinienem nie rzucać wynik malloc, czyli

int *sieve = malloc(sizeof(int) * length);

zamiast:

int *sieve = (int *) malloc(sizeof(int) * length);

Dlaczego miałoby tak być?


221
Ponadto łatwiej jest napisać sito = malloc (rozmiar * sito * długość);
William Pursell


3
Odpowiedzi tutaj to pole minowe jednostronności. Odpowiedź brzmi „to zależy”. Nie jest konieczne, aby program działał. Jednak niektóre standardy kodowania tego wymagają ... Na przykład zobacz CERT C Coding Standard
Dr. Person Person II

O dziwo, wszyscy zgadzają się, że nie rzucasz NULL. (prawdopodobnie dlatego wprowadzono C ++ nullptr: C ++ nie zezwala na żadne niejawne rzutowania wskaźnika)
Sapphire_Brick

Możesz, ale nie musisz. Ale musisz to zrobić w C ++.
Bez ciała

Odpowiedzi:


2215

Nie ; Państwo nie rzucać wynik, ponieważ:

  • Jest to niepotrzebne, ponieważ void *w tym przypadku jest automatycznie i bezpiecznie promowane na dowolny inny typ wskaźnika.
  • Dodaje bałaganu do kodu, rzutowania nie są bardzo łatwe do odczytania (szczególnie jeśli typ wskaźnika jest długi).
  • Powoduje, że powtarzasz się, co jest ogólnie złe.
  • Może ukryć błąd, jeśli zapomniałeś dołączyć <stdlib.h>. Może to powodować awarie (lub, co gorsza, nie powodować awarii aż do później w jakiejś zupełnie innej części kodu). Zastanów się, co się stanie, jeśli wskaźniki i liczby całkowite będą różnej wielkości; wtedy ukrywasz ostrzeżenie, przesyłając, i możesz stracić fragmenty zwracanego adresu. Uwaga: od C99 funkcje niejawne zniknęły z C i ten punkt nie jest już istotny, ponieważ nie ma automatycznego założenia, że ​​funkcje niezadeklarowane zwracają int.

Jako wyjaśnienie zauważ, że powiedziałem „nie rzucasz”, a nie „nie musisz rzucać”. Moim zdaniem nie można dołączyć obsady, nawet jeśli masz rację. Po prostu nie ma z tego żadnych korzyści, ale wiązka potencjalnych zagrożeń, w tym obsada wskazuje, że nie wiesz o ryzyku.

Zauważ też, jak zauważają komentatorzy, że powyższe mówi o prostym C, a nie C ++. Bardzo mocno wierzę w C i C ++ jako osobne języki.

Aby dodać dalej, kod niepotrzebnie powtarza informacje o typie ( int), które mogą powodować błędy. Lepiej jest usunąć odwołanie do wskaźnika używanego do przechowywania wartości zwracanej, aby „zablokować” oba razem:

int *sieve = malloc(length * sizeof *sieve);

To również przesuwa lengthprzód do przodu w celu zwiększenia widoczności i upuszcza zbędne nawiasy sizeof; one są potrzebne tylko wtedy, gdy argument jest nazwą typu. Wydaje się, że wiele osób nie wie (lub ignoruje) to, co sprawia, że ​​ich kod jest bardziej szczegółowy. Pamiętaj: sizeofnie jest funkcją! :)


Podczas gdy przejście lengthdo przodu może zwiększyć widoczność w niektórych rzadkich przypadkach, należy również zwrócić uwagę, że w ogólnym przypadku lepiej jest napisać wyrażenie jako:

int *sieve = malloc(sizeof *sieve * length);

Ponieważ zachowanie sizeofpierwszego, w tym przypadku zapewnia, że ​​mnożenie odbywa się co najmniej z size_tmatematyki.

Porównaj: malloc(sizeof *sieve * length * width)vs. malloc(length * width * sizeof *sieve)drugi może przepełnić, length * widthkiedy widthi lengthsą mniejsze niż size_t.


21
Proszę rozważyć zaktualizowanie odpowiedzi. Obsada nie jest już niebezpieczna, a powtarzanie się niekoniecznie jest czymś złym (redundancja może pomóc w wykrywaniu błędów).
n. „zaimki” m.

11
Kompilatory się zmieniły. Aktualny kompilator ostrzeże Cię o brakującej deklaracji malloc.
n. „zaimki” m.

55
@nm Ok. Myślę, że źle jest założyć, że każdy, kto tu czyta, ma określony kompilator. Odkąd C11 odeszła cała koncepcja „funkcji niejawnej”, nie wiedziałem o tym. Nadal nie widzę sensu w dodawaniu bezcelowej obsady. Czy robisz to również int x = (int) 12;po to, aby wszystko było jasne?
zrelaksuj się

22
@nm, jeśli jawne przesłanie wskaźnika pustki „pomogło” rozwiązać błąd, bardziej prawdopodobne jest napotkanie nieokreślonego zachowania, co oznaczałoby, że program ma znacznie gorszy, nieodkryty błąd, na który jeszcze się nie natknąłeś. I pewnego dnia, w mroźny zimowy wieczór, wrócisz do domu z pracy i zastaniesz stronę GitHub zalaną raportami problemów narzekającymi na demony wylatujące z nosa użytkowników
Braden Best,

12
@unwind Nawet ja się z tobą zgadzam, (int)12nie jest porównywalny. 12 jestint , obsada robi po prostu nic. Retval od malloc()Is void *, nie typ wskaźnik do lanego. (Jeśli nie jest void *. Więc analogia do (int)12tego, o (void*)malloc(…)czym nikt nie dyskutuje.)
Amin Negm-Awad

376

W C nie musisz rzutować wartości zwracanej na malloc. Wskaźnik zwrócony przez void mallocjest automatycznie konwertowany na poprawny typ. Jeśli jednak chcesz, aby Twój kod skompilował się przy użyciu kompilatora C ++, niezbędny jest rzut. Preferowaną alternatywą wśród społeczności jest użycie następujących opcji:

int *sieve = malloc(sizeof *sieve * length);

co dodatkowo uwalnia cię od konieczności martwienia się o zmianę prawej strony wyrażenia, jeśli zmienisz typ sieve.

Obsady są złe, jak zauważyli ludzie. Szczególnie rzutki.


71
@MAKZ Twierdziłbym, że malloc(length * sizeof *sieve)to wygląda sizeofna zmienną - więc myślę, że malloc(length * sizeof(*sieve))jest bardziej czytelna.
Michael Anderson

21
I malloc(length * (sizeof *sieve))jeszcze bardziej czytelny. MOIM ZDANIEM.
Toby Speight,

18
@ Na ()marginesie problem Michaela Andersona , zauważ, że twój sugerowany styl zmienił kolejność., Zastanów się, kiedy obliczana jest liczba elementów length*width, zachowując sizeofpierwszy w tym przypadku, upewniając się, że mnożenie odbywa się co najmniej z size_tmatematyki. Porównaj malloc(sizeof( *ptr) * length * width)vs. malloc(length * width * sizeof (*ptr))- 2. może przepełnić, length*widthkiedy width,lengthsą mniejsze typy, które size_t.
chux - Przywróć Monikę

3
@chux nie jest oczywiste, ale odpowiedź została zredagowana, aby mój komentarz był mniej trafny - oryginalna sugestia brzmiałamalloc(sizeof *sieve * length)
Michael Anderson

12
C nie jest C ++. Udawanie, że są, ostatecznie doprowadzi do zamieszania i smutku. Jeśli używasz C ++, rzutowanie w stylu C jest również złe (chyba że używasz bardzo starego kompilatora C ++). I static_cast>()(lub reinterpret_cast<>()) nie jest kompatybilny z żadnym dialektem C.
David C.

349

Państwo nie rzucać, ponieważ:

  • To sprawia, że ​​twój kod jest bardziej przenośny między C i C ++, a jak pokazuje doświadczenie SO, wielu programistów twierdzi, że pisze w C, gdy tak naprawdę pisze w C ++ (lub C plus lokalne rozszerzenia kompilatora).
  • Niezastosowanie się do tego może ukryć błąd : zwróć uwagę na wszystkie SO przykłady mylącego kiedy pisać w type *porównaniu do type **.
  • Pomysł, że nie pozwala ci zauważyć, że nie udało #includesię znaleźć odpowiedniego pliku nagłówkowego, pomija las drzew . To tak samo, jak powiedzenie „nie przejmuj się faktem, że nie poprosiłeś kompilatora, aby narzekał na brak prototypów - to nieznośne stdlib.h jest PRAWDZIWĄ ważną rzeczą do zapamiętania!”
  • Wymusza dodatkową kontrolę poznawczą . Umieszcza (rzekomy) pożądany typ tuż obok arytmetyki, którą wykonujesz dla surowego rozmiaru tej zmiennej. Założę się, że możesz zrobić badanie SO, które pokazuje, że malloc()błędy są wykrywane znacznie szybciej, gdy jest obsada. Podobnie jak w przypadku stwierdzeń, adnotacje ujawniające zamiar zmniejszają liczbę błędów.
  • Powtarzanie się w sposób, który może sprawdzić maszyna, jest często świetnym pomysłem. W rzeczywistości takie jest twierdzenie, a to użycie rzutowania jest twierdzeniem. Asercje są wciąż najbardziej ogólną techniką poprawiania kodu, ponieważ Turing wpadł na ten pomysł wiele lat temu.

39
@ulidtko Jeśli nie wiesz, możesz napisać kod, który kompiluje zarówno jako C, jak i C ++. W rzeczywistości większość plików nagłówkowych jest taka i często zawierają kod (makra i funkcje wbudowane). Posiadanie pliku .c/ .cppdo kompilacji, ponieważ oba są bardzo przydatne, ale jednym z przypadków jest dodanie obsługi C ++ throwpo kompilacji za pomocą kompilatora C ++ (ale return -1;po kompilacji za pomocą kompilatora C lub cokolwiek innego).
hyde

37
Gdyby ktoś miał wywołania malloc wbudowane w nagłówku, nie byłbym pod wrażeniem, #ifdef __cplusplus i extern „C” {} są do tego zadania, nie dodając dodatkowych rzutów.
paulm

15
Cóż, punkt 1 jest nieistotny, ponieważ C! = C ++, pozostałe punkty są również trywialne, jeśli użyjesz zmiennej w swoim mallocwywołaniu: char **foo = malloc(3*sizeof(*foo));jeśli całkiem w pełni dowód: 3 wskaźniki do wskaźników char. następnie zapętlić i zrobić foo[i] = calloc(101, sizeof(*(foo[i])));. Przydziel tablicę 101 znaków, starannie zainicjowanych zerami. Nie potrzeba obsady. zmień deklarację na unsigned charinny typ, jeśli o to chodzi, i nadal jesteś dobry
Elias Van Ootegem,

34
Kiedy myślałem, że go dostałem, nadchodzi! Fantastyczna odpowiedź. To pierwszy raz w StackOverflow, że daje +1 dwie przeciwne odpowiedzi! +1 Nie, nie rzucasz, i +1 Tak, rzucasz! LOL. Jesteście wspaniali. A dla mnie i moich uczniów postanowiłem: rzucam. Błędy, które popełniają studenci, są łatwiejsze do zauważenia podczas przesyłania.
Dr Beco

15
@Leushenko: Powtarzanie się w sposób, którego nie można potwierdzić maszynowo ani lokalnie, jest złe. Powtarzanie się w sposób, który można zweryfikować za pomocą takich środków, jest mniej złe. Biorąc pod uwagę struct Zebra *p; ... p=malloc(sizeof struct Zebra);, malloc nie może uniknąć powielania informacji o typie p, ale ani kompilator, ani kontrola kodu lokalnego nie wykryłyby żadnego problemu, gdyby jeden typ się zmienił, a drugi nie. Aby zmienić kod p=(struct Zebra*)malloc(sizeof struct Zebra);i kompilator skrzek jeśli typ obsada nie pasuje p, a lokalna kontrola ujawni ...
Supercat

170

Jak powiedzieli inni, nie jest potrzebny w C, ale w C ++. Jeśli uważasz, że zamierzasz skompilować kod C za pomocą kompilatora C ++, z jakichkolwiek powodów możesz użyć makra, na przykład:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

W ten sposób możesz nadal pisać w bardzo kompaktowy sposób:

int *sieve = NEW(int, 1);

i skompiluje się dla C i C ++.


17
Skoro i tak używasz makra, dlaczego nie użyjesz go neww definicji C ++?
Hosam Aly

63
Ponieważ nie ma powodu, aby to robić. Dotyczy to głównie programów C skompilowanych przy użyciu kompilatora C ++. Jeśli zamierzasz używać „nowego”, jedyne, co dostajesz, to problemy. Potrzebujesz również makra za darmo. I potrzebujesz makra, aby zwolnić tablicę, różnicowania, które nie istnieje w C.
quinmars

8
Nie wspominając o tym, że to nie ty zwalniasz pamięć, ale może używasz biblioteki C, itp. Wiele możliwych problemów bez żadnego zysku.
quinmars,

86
@Hosam: Tak, zdecydowanie tak jest. Jeśli używasz new, musisz używać, deletea jeśli używasz malloc(), musisz free(). Nigdy ich nie mieszaj.
Graeme Perrow,

17
Jeśli ktoś podejmie takie podejście, wywołanie makra NEWjest prawdopodobnie złym pomysłem, ponieważ zasób nigdy nie jest zwracany za pomocą delete(lub DELETE), więc miksujesz słownictwo. Zamiast tego nazywanie go MALLOC, a raczej CALLOCw tym przypadku, miałoby większy sens.
mah

139

Z Wikipedii :

Zalety odlewania

  • Dołączenie rzutowania może umożliwić kompilację programu lub funkcji C jako C ++.

  • Obsada pozwala na wersje Malloc sprzed 1989 roku, które pierwotnie zwróciły char *.

  • Przesyłanie może pomóc deweloperowi w identyfikowaniu niespójności przy zmianie typu w przypadku zmiany typu wskaźnika docelowego, szczególnie jeśli wskaźnik jest zadeklarowany z dala od wywołania malloc () (chociaż nowoczesne kompilatory i analizatory statyczne mogą ostrzegać przed takim zachowaniem bez konieczności rzutowania).

Wady castingu

  • Zgodnie ze standardem ANSI C obsada jest zbędna.

  • Dodanie obsady może maskować brak dołączenia nagłówka stdlib.h, w którym znajduje się prototyp malloc. W przypadku braku prototypu dla malloc, standard wymaga, aby kompilator C zakładał, że malloc zwraca int. Jeśli nie ma rzutowania, generowane jest ostrzeżenie, gdy ta liczba całkowita jest przypisana do wskaźnika; jednak w przypadku obsady to ostrzeżenie nie jest generowane, ukrywając błąd. W niektórych architekturach i modelach danych (takich jak LP64 w systemach 64-bitowych, gdzie długie i wskaźniki są 64-bitowe, a int 32-bitowe), ten błąd może faktycznie powodować niezdefiniowane zachowanie, ponieważ niejawnie zadeklarowany malloc zwraca 32- wartość bitu, podczas gdy faktycznie zdefiniowana funkcja zwraca wartość 64-bitową. W zależności od konwencji wywoływania i układu pamięci może to spowodować zniszczenie stosu. Ten problem nie pozostanie niezauważony w nowoczesnych kompilatorach, ponieważ jednolicie generują ostrzeżenia, że ​​użyto niezadeklarowanej funkcji, więc ostrzeżenie będzie nadal wyświetlane. Na przykład domyślne zachowanie GCC polega na pokazywaniu ostrzeżenia o treści „niekompatybilna niejawna deklaracja wbudowanej funkcji” niezależnie od tego, czy rzutowanie jest obecne, czy nie.

  • Jeśli typ wskaźnika zostanie zmieniony w deklaracji, można również zmienić wszystkie linie, w których wywoływany jest malloc i rzutować.

Chociaż metoda malloc bez rzutowania jest preferowaną metodą i wybierają ją najbardziej doświadczeni programiści , powinieneś użyć dowolnej z tych opcji, aby mieć świadomość problemów.

tzn .: jeśli musisz skompilować program C jako C ++ (chociaż jest to osobny język), musisz rzutować wynik użycia malloc.


1
Co oznacza „ Przesyłaniemalloc()może pomóc deweloperowi w identyfikowaniu niespójności przy zmianie typu, jeśli powinien zmienić się typ docelowego wskaźnika, szczególnie jeśli wskaźnik jest zadeklarowany z dala od wywołania ”? Czy możesz podać przykład?
Spikatrix

3
@CoolGuy: Zobacz wcześniejszy komentarz do innej odpowiedzi . Pamiętaj jednak, że p = malloc(sizeof(*p) * count)idiom automatycznie rozpoznaje zmiany typu, więc nie musisz otrzymywać ostrzeżeń i nic nie zmieniać. Nie jest to więc prawdziwa zaleta w porównaniu z najlepszą alternatywą dla nie rzucania.
Peter Cordes

7
Oto poprawna odpowiedź: są plusy i minusy i sprowadza się to do gustu (chyba że kod musi się skompilować jako C ++ - obsada jest obowiązkowa).
Peter - przywróć Monikę

3
Punkt 3 jest dyskusyjny, ponieważ jeśli typ wskaźnika zmieni się w deklaracji, należy sprawdzić każde wystąpienie malloc, realokacji i swobodnego rozwiązania tego typu. Casting zmusi cię do tego.
Michaël Roy,

104

W C można niejawnie przekonwertować voidwskaźnik na dowolny inny wskaźnik, więc rzutowanie nie jest konieczne. Użycie jednego może zasugerować przypadkowemu obserwatorowi, że istnieje jakiś powód, dla którego jest on potrzebny, co może wprowadzać w błąd.


100

Nie rzutujesz wyniku malloc, ponieważ powoduje to niepotrzebny bałagan w kodzie.

Najczęstszym powodem, dla którego ludzie rzucają wynik malloc, jest to, że nie są pewni, jak działa język C. To znak ostrzegawczy: jeśli nie wiesz, jak działa dany mechanizm językowy, nie zgaduj. Sprawdź to lub zapytaj o Przepełnienie stosu.

Niektóre komentarze:

  • Wskaźnik pustki można przekonwertować na / z dowolnego innego typu wskaźnika bez wyraźnego rzutowania (C11 6.3.2.3 i 6.5.16.1).

  • C ++ nie zezwala jednak na niejawne rzutowanie między void*innym typem wskaźnika. Tak więc w C ++ obsada byłaby poprawna. Ale jeśli programujesz w C ++, powinieneś używać, newa nie malloc (). I nigdy nie powinieneś kompilować kodu C za pomocą kompilatora C ++.

    Jeśli potrzebujesz obsługiwać zarówno C, jak i C ++ przy użyciu tego samego kodu źródłowego, użyj przełączników kompilatora, aby zaznaczyć różnice. Nie próbuj dopasowywać obu standardów językowych za pomocą tego samego kodu, ponieważ nie są one kompatybilne.

  • Jeśli kompilator C nie może znaleźć funkcji, ponieważ zapomniałeś dołączyć nagłówek, pojawi się błąd kompilatora / linkera. Więc jeśli zapomniałeś dołączyć <stdlib.h>to nie jest biggie, nie będziesz w stanie zbudować swojego programu.

  • W starożytnych kompilatorach zgodnych z wersją standardu, która ma ponad 25 lat, zapomnienie o dołączeniu <stdlib.h>spowodowałoby niebezpieczne zachowanie. Ponieważ w tym starożytnym standardzie funkcje bez widocznego prototypu domyślnie przekształcały typ zwracany na int. Bezpośrednie przesłanie wyniku z malloc ukryłoby ten błąd.

    Ale to naprawdę nie jest problem. Nie używasz 25-letniego komputera, więc dlaczego miałbyś używać 25-letniego kompilatora?


9
„bezsensowny bałagan” to lekceważąca hiperbola, która wykracza poza wszelkie możliwości przekonania każdego, kto jeszcze się z tobą nie zgadza. Obsada z pewnością nie jest bezcelowa; Odpowiedzi Rona Burka i Kaza przemawiają za castingiem, z którym bardzo się zgadzam. To, czy obawy są ważniejsze niż te, o których wspominasz, jest rozsądnym pytaniem. Dla mnie twoje obawy wydają się stosunkowo niewielkie w porównaniu do ich.
Don Hatch

„Wskaźnik void może zostać przekonwertowany na / z dowolnego innego typu wskaźnika bez jawnego rzutowania” nie jest obsługiwany przez 6.3.2.3. Być może myślisz o „wskaźniku do dowolnego typu obiektu”? „wskaźnik pustki” i „wskaźnik funkcji” nie są tak łatwo przekształcalne.
chux - Przywróć Monikę

Rzeczywiście odniesienie było niepełne. Istotną częścią „niejawności” jest zasada prostego przypisania 6.5.16.1. „jeden operand jest wskaźnikiem do typu obiektu, a drugi jest wskaźnikiem do kwalifikowanej lub niekwalifikowanej wersji void”. Dodałem to odniesienie do odpowiedzi w celu uzyskania kompletności.
Lundin,

91

W C otrzymujesz niejawną konwersję z void *dowolnego innego wskaźnika (danych).


6
@Jens: OK, może bardziej odpowiednie sformułowanie to „niejawna konwersja”. Podobnie jak użycie zmiennej całkowej w wyrażeniu zmiennoprzecinkowym.
EFraim

@EraFraim To faktycznie spowodowałoby obsadę, a tym samym ukrytą.
Mad Physicist,

71

Rzucanie wartości zwracanej przez malloc()nie jest teraz konieczne, ale chciałbym dodać jeden punkt, który wydaje się, że nikt nie zauważył:

W dawnych czasach, to znaczy, zanim ANSI C zapewnia void *ogólny typ wskaźników, char *jest typem takiego użycia. W takim przypadku rzutowanie może wyłączyć ostrzeżenia kompilatora.

Odniesienie: C FAQ


2
Zamykanie ostrzeżeń kompilatora to zły pomysł.
Albert van der Horst

8
@AlbertvanderHorst Nie, jeśli robisz to, rozwiązując dokładny problem, ostrzeżenie jest po to, aby cię ostrzec.
Dan Bechard

@Dan. Jeśli rozwiązanie tego konkretnego problemu oznacza przepisanie podprogramu w celu zwrócenia nowoczesnych typów ANSI C zamiast char *, zgadzam się. Nie nazwałbym tego zamykaniem kompilatora. Nie poddawaj się menedżerom, którzy twierdzą, że nie ma ostrzeżeń kompilatora, zamiast używać ich przy każdej ponownej kompilacji w celu znalezienia możliwych problemów. Groetjes Albert
Albert van der Horst

53

Dodając moje doświadczenie, studiując inżynierię komputerową, widzę, że dwaj lub trzej profesorowie, których pisałem w C, zawsze rzucali malloc, jednak ten, o który prosiłem (z ogromnym CV i zrozumieniem C) powiedział mi, że jest to absolutnie niepotrzebne, ale były tylko absolutnie specyficzne i pozwalały uczniom wejść w mentalność absolutnej specyficzności. Zasadniczo rzutowanie nie zmieni niczego w sposobie działania, robi dokładnie to, co mówi, przydziela pamięć, a rzutowanie nie wpływa na to, dostajesz tę samą pamięć, a nawet jeśli przypadkowo rzucisz ją na coś innego (i jakoś unikniesz kompilatora błędy) C uzyska do niego dostęp w ten sam sposób.

Edycja: Casting ma pewien punkt. Kiedy używasz notacji tablicowej, wygenerowany kod musi wiedzieć, ile miejsc pamięci musi przejść, aby osiągnąć początek następnego elementu, jest to osiągane poprzez rzutowanie. W ten sposób wiesz, że dla podwójnej przewijasz 8 bajtów do przodu, podczas gdy dla int masz 4, i tak dalej. Dlatego nie ma to żadnego efektu, jeśli używasz notacji wskaźnikowej, w notacji tablicowej staje się to konieczne.


3
Poza tym, jak już wspomniano, rzutowanie może ukrywać błędy i utrudniać analizę kodu dla kompilatora lub analizatora statycznego.
Lundin

2
„Zasadniczo casting niczego nie zmieni w działaniu”. Przesyłanie na pasujący typ nie powinno niczego zmieniać, ale czy typ var powinien się zmienić, a rzutowanie nie pasuje, czy mogą pojawić się problemy? IWO, obsada i typ var powinny być zsynchronizowane - dwa razy więcej prac konserwacyjnych.
chux - Przywróć Monikę

Rozumiem, dlaczego profesorowie wolą casting. Przesyłanie może być przydatne z edukacyjnego punktu widzenia, gdy przekazuje informacje instruktorowi, a kod ucznia nie musi być utrzymywany - jego kod wyrzucania. Jednak z punktu widzenia kodowania, wzajemnej oceny i konserwacjip = malloc(sizeof *p * n); jest to takie proste i lepsze.
chux - Przywróć Monikę

53

Rzucanie wyników nie jest obowiązkowe malloc, ponieważ zwraca void*, a void*można wskazać dowolny typ danych.


34

Wskaźnik pustki jest ogólnym wskaźnikiem obiektu, a C obsługuje niejawną konwersję z typu wskaźnika pustki na inne typy, więc nie ma potrzeby jawnego rzutowania go.

Jeśli jednak chcesz, aby ten sam kod działał idealnie kompatybilny na platformie C ++, która nie obsługuje niejawnej konwersji, musisz wykonać rzutowanie czcionek, więc wszystko zależy od użyteczności.


2
Kompilacja pojedynczego źródła jako C i C ++ nie jest normalnym przypadkiem użycia (w przeciwieństwie do, powiedzmy, użycia pliku nagłówkowego zawierającego deklaracje do połączenia kodu C i C ++). Używanie malloci przyjaciół w C ++ jest dobrym znakiem ostrzegawczym, że zasługuje na szczególną uwagę (lub ponowne napisanie w C).
Toby Speight,

1
„Wskaźnik pustki to ogólny wskaźnik” -> „Wskaźnik pustki to ogólny wskaźnik obiektowy ”. Rozmiary wskaźników funkcji mogą przekraczać void *, dlatego nie void *są wystarczające, aby dobrze przechowywać wskaźnik funkcji.
chux - Przywróć Monikę

moja intencja tej linii była taka sama, ale i tak dziękuję @chux za sugestie.
Staraj się

33

Tak mówi Podręcznik GNU C Library Reference :

Możesz zapisać wynik mallocw dowolnej zmiennej wskaźnika bez rzutowania, ponieważ ISO C automatycznie konwertuje typ void *na inny typ wskaźnika, gdy jest to konieczne. Ale rzutowanie jest konieczne w kontekstach innych niż operatory przypisania lub jeśli chcesz, aby Twój kod działał w tradycyjnym C.

I rzeczywiście norma ISO C11 (p347) mówi:

Wskaźnik zwrócony, jeśli alokacja się powiedzie, jest odpowiednio wyrównany, dzięki czemu można go przypisać do dowolnego typu obiektu z podstawowymi wymaganiami dotyczącymi wyrównania, a następnie użyć do uzyskania dostępu do takiego obiektu lub tablicy takich obiektów w przydzielonej przestrzeni (do czasu spacja jest jawnie zwolniona)


31

Zwrócony typ jest nieważny *, który można rzutować na pożądany typ wskaźnika danych, aby być dereferencyjnym.


1
void* można rzutować na żądany typ, ale nie ma takiej potrzeby, ponieważ zostanie on automatycznie przekonwertowany. Dlatego obsada nie jest konieczna, a wręcz niepożądana z powodów wymienionych w odpowiedziach na najwyższe noty.
Toby Speight,

ale tylko wtedy, gdy musisz wyrejestrować ją „w locie”, jeśli zamiast tego utworzysz zmienną, zostanie ona automatycznie i automatycznie przekonwertowana na efektywny typ zmiennej, bez rzutowania (w C).
Ferrarezi

28

W języku C wskaźnik pustki można przypisać do dowolnego wskaźnika, dlatego nie należy używać rzutowania typu. Jeśli chcesz przydzielić „bezpieczny typ”, mogę polecić następujące funkcje makr, których zawsze używam w moich projektach C:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

Po ich wprowadzeniu możesz po prostu powiedzieć

NEW_ARRAY(sieve, length);

W przypadku tablic niedynamicznych trzecim niezbędnym makrem funkcji jest

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

co sprawia, że ​​pętle macierzy są bezpieczniejsze i wygodniejsze:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

„wskaźnik pustki można przypisać do dowolnego wskaźnika obiektu ” Wskaźniki funkcji to kolejny problem, choć nie malloc()jeden.
chux - Przywróć Monikę

Przypisanie wskaźnika void*do / od wskaźnika funkcji może spowodować utratę informacji, więc „wskaźnik pustki można przypisać do dowolnego wskaźnika”, w takich przypadkach jest problemem. Przypisanie wskaźnika void*od malloc() do dowolnego obiektu nie jest jednak problemem.
chux - Przywróć Monikę

doKomentarz pętla związane z makrami z udziałem jeszcze pętlę, która zastanawia się od tytułowego pytania. Usuwam ten komentarz. Zrobię to później.
chux - Przywróć Monikę

27

To zależy od języka programowania i kompilatora. Jeśli używasz mallocw C, nie ma potrzeby wpisywania rzutowania, ponieważ będzie on automatycznie wpisywać rzutowanie. Jeśli jednak używasz C ++, powinieneś wpisać cast, ponieważ malloczwróci void*typ.


1
Funkcja malloc zwraca również wskaźnik pustki w C, ale reguły języka są inne niż C ++.
August Karlstrom,

16

Ludzie przyzwyczajeni do GCC i Clanga są rozpieszczani. Tam nie jest tak dobrze.

Przez lata byłem przerażony niesamowicie starzejącymi się kompilatorami, których musiałem używać. Często firmy i menedżerowie przyjmują bardzo konserwatywne podejście do zmiany kompilatorów i nawet nie testują czy nowy kompilator (z lepszą zgodnością standardów i optymalizacją kodu) będzie działał w ich systemie. Praktyczna rzeczywistość dla pracujących programistów polega na tym, że kiedy piszesz kod, musisz zakryć swoje bazy i, niestety, rzucanie mallocami jest dobrym nawykiem, jeśli nie możesz kontrolować, który kompilator może zostać zastosowany do twojego kodu.

Sugerowałbym również, że wiele organizacji stosuje własne standardy kodowania i że to powinna być metoda, którą ludzie zastosują, jeśli zostanie ona zdefiniowana. Wobec braku wyraźnych wskazówek staram się raczej gromadzić wszędzie, niż niewolnicze przestrzeganie standardu.

Argument, że zgodnie z obecnymi standardami nie jest konieczny, jest całkiem aktualny. Ale ten argument pomija praktyczne aspekty prawdziwego świata. Nie kodujemy w świecie rządzonym wyłącznie standardami dnia, ale praktycznością tego, co lubię nazywać „rzeczywistością lokalnego zarządzania”. I to jest wygięte i pokręcone bardziej niż kiedykolwiek czasoprzestrzeń. :-)

YMMV.

Często myślę o rzucaniu malloca jako operacji obronnej. Nie ładna, nie idealna, ale ogólnie bezpieczna. (Szczerze mówiąc, jeśli nie podałeś stdlib.h, masz o wiele więcej problemów niż rzucanie malloc!).


15

Włączyłem rzutowanie po prostu, aby pokazać dezaprobatę dla brzydkiej dziury w systemie tekstowym, co pozwala kompilować kod, taki jak poniższy fragment kodu, bez diagnostyki, mimo że żadne rzutowania nie są używane do spowodowania złej konwersji:

double d;
void *p = &d;
int *q = p;

Chciałbym, żeby to nie istniało (i nie ma w C ++), więc rzuciłem. Reprezentuje mój gust i moją politykę programistyczną. Nie tylko rzucam wskaźnik, ale skutecznie, rzucam kartę do głosowania i wyrzucam demony głupoty . Jeśli nie mogę tak naprawdę wyrzucić głupoty , to przynajmniej pozwólcie mi wyrazić życzenie gestem protestu.

W rzeczywistości dobrą praktyką jest owijanie malloc(i znajomych) funkcjami, które zwracają unsigned char *, i zasadniczo nigdy nie należy ich używać void *w kodzie. Jeśli potrzebujesz ogólnego wskaźnika do dowolnego obiektu, użyj a char *lub unsigned char *i rzutuj w obu kierunkach. Jedną z relaksacji, na którą można sobie pozwolić, może być używanie funkcji takich jak memseti memcpybez rzutów.

Na temat odlewów i kompatybilności C ++, jeśli napisać kod tak, że kompiluje jako ++ (w takim przypadku zarówno C i C muszą rzutować wartości zwracanej mallocpodczas przypisywania go do czegoś innego niż void *), można zrobić bardzo pomocny rzecz dla siebie: możesz używać makr do rzutowania, które tłumaczą rzutowania w stylu C ++ podczas kompilacji jako C ++, ale redukują się do rzutowania C podczas kompilacji jako C:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

Jeśli zastosujesz się do tych makr, proste grepprzeszukanie bazy kodu dla tych identyfikatorów pokaże Ci, gdzie znajdują się wszystkie Twoje rzutowania, dzięki czemu możesz sprawdzić, czy któryś z nich jest niepoprawny.

Następnie, jeśli będziesz regularnie kompilować kod w C ++, wymusi to użycie odpowiedniej obsady. Na przykład, jeśli używasz strip_qualtylko w celu usunięcia constlub volatile, ale zmiany programu w taki sposób, że konwersja typu jest obecnie zaangażowany, dostaniesz diagnostyczny i trzeba będzie użyć kombinacji odlewów, aby uzyskać żądaną konwersję.

Aby pomóc Ci w przestrzeganiu tych makr, kompilator GNU C ++ (nie C!) Ma piękną funkcję: opcjonalną diagnostykę, która jest generowana dla wszystkich wystąpień rzutowań w stylu C.

     -Old-style-cast (tylko C ++ i Objective-C ++)
         Ostrzegaj, jeśli zostanie użyty rzut w starym stylu (w stylu C) na typ nieważny
         w programie C ++. Odlewy w nowym stylu (dynamic_cast,
         static_cast, reinterpret_cast i const_cast) są mniej wrażliwe
         do niezamierzonych efektów i o wiele łatwiejsze do wyszukiwania.

Jeśli Twój kod C kompiluje się jako C ++, możesz użyć tej -Wold-style-castopcji, aby znaleźć wszystkie wystąpienia (type)składni rzutowania, które mogą wkraść się do kodu, i śledzić tę diagnostykę, zastępując go odpowiednim wyborem spośród powyższych makr (lub połączenie, jeśli to konieczne).

Takie traktowanie konwersji jest największym pojedynczym samodzielnym technicznym uzasadnieniem pracy w „Clean C”: połączonym dialekcie C i C ++, co z kolei technicznie uzasadnia rzutowanie wartości zwracanej malloc.


Jak zauważyli inni, zwykle polecam nie mieszać kodu C i C ++. Jeśli jednak masz ku temu dobry powód, makra mogą być przydatne.
Phil1970,

@ Phil1970 Wszystko jest napisane w jednym spójnym dialekcie, który akurat jest przenośny dla kompilatorów C i C ++ i wykorzystuje niektóre możliwości C ++. Musi być skompilowany w całości jako C ++, lub inaczej w C.
Kaz

To znaczy, co próbowałem powiedzieć w poprzednim komentarzu, to to, że nie ma mieszania C i C ++. Chodzi o to, aby cały kod był skompilowany jako C lub cały skompilowany jako C ++.
Kaz

15

Najlepszą rzeczą do zrobienia przy programowaniu w C, gdy tylko jest to możliwe:

  1. Spraw, aby Twój program kompilował się za pomocą kompilatora C z włączonymi wszystkimi ostrzeżeniami -Walli napraw wszystkie błędy i ostrzeżenia
  2. Upewnij się, że nie ma zadeklarowanych zmiennych jako auto
  3. Następnie skompiluj go za pomocą kompilatora C ++ za pomocą -Walli -std=c++11. Napraw wszystkie błędy i ostrzeżenia.
  4. Teraz ponownie skompiluj używając kompilatora C. Twój program powinien teraz kompilować się bez ostrzeżenia i zawierać mniej błędów.

Ta procedura pozwala wykorzystać ścisłe sprawdzanie typu C ++, zmniejszając w ten sposób liczbę błędów. W szczególności ta procedura zmusza Cię do włączenia stdlib.hlub otrzymasz

malloc nie został zadeklarowany w tym zakresie

a także zmusza cię do rzucenia wyniku malloclub dostaniesz

nieprawidłowa konwersja z void*naT*

lub jaki jest Twój typ docelowy.

Jedyne zalety pisania w C zamiast C ++, jakie mogę znaleźć, to

  1. C ma dobrze określony ABI
  2. C ++ może generować więcej kodu [wyjątki, RTTI, szablony, polimorfizm środowiska wykonawczego ]

Zauważ, że drugie minusy powinny w idealnym przypadku zniknąć, gdy użyjesz podzbioru wspólnego dla C razem ze statyczną cechą polimorficzną.

Dla tych, którzy uznają surowe reguły C ++ za niewygodne, możemy użyć funkcji C ++ 11 z wnioskowanym typem

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

18
Użyj kompilatora C dla kodu C. Użyj kompilatora C ++ dla kodu C ++. Nie, jeśli, nie ma ale. Przepisanie kodu C w C ++ to zupełnie inna sprawa i może - lub nie musi - być warte czasu i ryzyka.
Toby Speight,

2
Chciałbym dodać do porady @TobySpeight: Jeśli potrzebujesz użyć kodu C w projekcie C ++, zwykle możesz skompilować kod C jako C (np. gcc -c c_code.c), Kod C ++ jako C ++ (np. g++ -c cpp_code.cpp), A następnie połączyć je ze sobą (np. gcc c_code.o cpp_code.olub odwrotnie w zależności od zależności projektu). Teraz nie powinno być powodu, aby pozbawiać się jakichkolwiek przyjemnych cech któregokolwiek języka ...
autystyczny

1
@ user877329 Jest to bardziej rozsądna alternatywa dla starannego dodawania rzutowań do kodu, które zmniejszają czytelność kodu, tylko ze względu na jego „kompatybilność z C ++”.
autystyczny

1
Prawdopodobnie główną zaletą w tym kontekście jest to, że C pozwala pisać p = malloc(sizeof(*p));, co nie wymaga zmiany w pierwszej kolejności, jeśli pzmieni się nazwa innego typu. Proponowaną „zaletą” rzutowania jest to, że pojawia się błąd kompilacji, jeśli pjest to zły typ, ale jeszcze lepiej, jeśli po prostu działa.
Peter Cordes

1
Chciałbym wspomnieć, że pisanie w C może być konieczne, gdy atakujemy platformy pozbawione odpowiednich kompilatorów C ++. Wyjątki i szablony to funkcje, które zwykle pomagają c ++ generować mniejszy i / lub bardziej wydajny kod, podczas gdy polimorfizm środowiska wykonawczego w C ++ jest w większości równoważny C.
user7860670

15

Nie, nie rzucasz wyniku malloc().

Ogólnie rzecz biorąc, nie przesyłasz do ani zvoid * .

Typowym powodem, dla którego się tego nie robi, jest brak #include <stdlib.h>niezauważenia. Już od dawna nie jest to problemem, ponieważ C99 sprawił, że niejawne deklaracje funkcji są nielegalne, więc jeśli twój kompilator jest zgodny co najmniej z C99, otrzymasz komunikat diagnostyczny.

Ale istnieje znacznie silniejszy powód, aby nie wprowadzać niepotrzebnych rzutów wskaźnika:

W C rzutowanie wskaźnika prawie zawsze jest błędem . Wynika to z następującej zasady ( § 6.5 p7 w N1570, najnowszy projekt dla C11):

Obiekt ma swoją zapisana wartość dostępne tylko lwartością wyrażenia, które ma jeden z następujących typów:
- typ zgodny z efektywnym typem obiektu,
- wykwalifikowana wersja typu zgodnego z efektywnym typem obiektu,
- typ, który jest typem podpisanym lub niepodpisanym odpowiadającym efektywnemu typowi obiektu,
- typ, który jest typem podpisanym lub niepodpisanym odpowiadającym kwalifikowanej wersji efektywnego typu obiektu,
- typ agregacyjny lub łączny, który obejmuje jeden wyżej wymienionych typów wśród jej członków (w tym rekurencyjnie członek subagregatu lub zawartego związku), lub
- typ postaci.

Jest to również znane jako ścisła zasada aliasingu . Zatem poniższy kod jest niezdefiniowanym zachowaniem :

long x = 5;
double *p = (double *)&x;
double y = *p;

I, czasem zaskakujące, są również następujące kwestie:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

Czasami nie trzeba wskaźniki obsady, ale biorąc pod uwagę ścisłe reguły aliasingu , trzeba być bardzo ostrożnym z nim. Tak więc każde wystąpienie wskaźnika rzuconego w kodzie jest miejscem, w którym należy dokładnie sprawdzić jego poprawność . Dlatego nigdy nie piszesz niepotrzebnego rzutowania wskaźnika.

tl; dr

W skrócie: Ponieważ w C każde wystąpienie rzutowania wskaźnika powinno podnieść czerwoną flagę dla kodu wymagającego specjalnej uwagi, nigdy nie należy pisać niepotrzebnych rzutowań wskaźnika.


Notatki dodatkowe:

  • Są przypadki, w których naprawdę potrzebujesz rzutowania void *, np. Jeśli chcesz wydrukować wskaźnik:

    int x = 5;
    printf("%p\n", (void *)&x);

    Obsada jest tutaj niezbędna, ponieważ printf()jest funkcją wariadyczną, więc niejawne konwersje nie działają.

  • W C ++ sytuacja jest inna. Rzutowanie typów wskaźników jest dość powszechne (i poprawne) w przypadku obiektów klas pochodnych. Dlatego ma sens, że w C ++ konwersja do iz nievoid * jest niejawna. C ++ ma cały zestaw różnych smaków castingu.


1
W swoich przykładach unikasz nieważności *. istnieje różnica między rzutowaniem z double * na int * i vice versa. malloc zwraca pointel wyrównany do największego standardowego typu, więc nie ma łamanych reguł aliasingu, nawet jeśli ktoś rzutuje z tego wyrównanego wskaźnika na inny typ.
P__J__

Aliasing nie ma nic wspólnego z wyrównaniem i do końca twojego komentarza - najwyraźniej nie rozumiesz.

@PeterJ: na wszelki wypadek chodzi o to, aby uniknąć niepotrzebnego rzucania wskaźnika, aby nie wyglądał jak fragment kodu, na który należy zwrócić szczególną uwagę.

Problem ścisłego aliasingu tak naprawdę nie ma nic wspólnego z pustymi wskaźnikami. Aby uzyskać błędy spowodowane ścisłym naruszeniem aliasingu, musisz cofnąć odwołanie do wskazanych danych. A ponieważ nie można odwoływać się do wskaźnika pustki, takie błędy z definicji nie są związane ze wskaźnikiem pustki, ale z czymś innym.
Lundin,

Przeciwnie, musisz wprowadzić zasadę, aby zakazać wszystkich rzutów wskaźnikiem. Ale w jaki sposób napisałbyś takie rzeczy jak procedury serializacji i programowanie związane ze sprzętem? Rzeczy, które są siłą C. Takie obsady są w porządku, jeśli wiesz, co robisz.
Lundin,

15

Wolę robić obsadę, ale nie ręcznie. Moim ulubionym jest używanie g_newi g_new0makra z glib. Jeśli glib nie jest używany, dodałbym podobne makra. Te makra zmniejszają powielanie kodu bez uszczerbku dla bezpieczeństwa typu. Jeśli pomylisz się w typie, otrzymasz niejawne rzutowanie między wskaźnikami nie będącymi nieważnymi, co spowoduje ostrzeżenie (błąd w C ++). Jeśli zapomnisz dołączyć nagłówek, który definiuje g_newi g_new0, otrzymasz błąd. g_newi g_new0oba przyjmują te same argumenty, w przeciwieństwie do malloctego , że bierze mniej argumentów niż calloc. Wystarczy dodać, 0aby uzyskać pamięć inicjowaną zerem. Kod można skompilować za pomocą kompilatora C ++ bez zmian.


12

Przesyłanie dotyczy tylko C ++, a nie C. Jeśli używasz kompilatora C ++, lepiej zmień go na kompilator C.


9

Koncepcja wskaźnika pustki polega na tym, że można go rzutować na dowolny typ danych, dlatego malloc zwraca wartość pustą. Musisz także pamiętać o automatycznym rzutowaniu czcionek. Rzucanie wskaźnika nie jest więc obowiązkowe, ale musisz to zrobić. Pomaga w utrzymaniu kodu w czystości i pomaga w debugowaniu


11
To nie jest obowiązkowe - choć musisz to zrobić ” - myślę, że jest w tym sprzeczność!
Toby Speight,

5
Myślę, że powinieneś przeczytać ten post komuś i sprawdzić, czy rozumie, co próbujesz powiedzieć. Następnie przepisz, wyjaśniając, co chcesz powiedzieć. Naprawdę nie rozumiem twojej odpowiedzi.
Bill Woodger,

9

Wskaźnik pustki jest wskaźnikiem ogólnym, a C obsługuje niejawną konwersję z typu wskaźnika pustki na inne typy, więc nie ma potrzeby jawnego jego rzutowania.

Jeśli jednak chcesz, aby ten sam kod działał idealnie kompatybilny na platformie C ++, która nie obsługuje niejawnej konwersji, musisz wykonać rzutowanie czcionek, więc wszystko zależy od użyteczności.


9
  1. Jak już wspomniano, nie jest to konieczne dla C, ale dla C ++.

  2. Dołączenie rzutowania może umożliwić kompilację programu lub funkcji C jako C ++.

  3. W C nie jest to konieczne, ponieważ void * jest automatycznie i bezpiecznie promowany do dowolnego innego typu wskaźnika.

  4. Jeśli jednak rzucisz, może ukryć błąd, jeśli zapomnisz dołączyć plik stdlib.h . Może to powodować awarie (lub, co gorsza, nie powodować awarii aż do później w jakiejś zupełnie innej części kodu).

    Ponieważ stdlib.h zawiera prototyp dla malloc, został znaleziony. W przypadku braku prototypu dla malloc, standard wymaga, aby kompilator C zakładał, że malloc zwraca int. Jeśli nie ma rzutowania, generowane jest ostrzeżenie, gdy ta liczba całkowita jest przypisana do wskaźnika; jednak w przypadku obsady to ostrzeżenie nie jest generowane, ukrywając błąd.


7

Rzucanie malloc jest niepotrzebne w C, ale obowiązkowe w C ++.

Przesyłanie w C jest zbędne, ponieważ:

  • void * jest automatycznie i bezpiecznie promowany do dowolnego innego typu wskaźnika w przypadku C.
  • Może ukryć błąd, jeśli zapomniałeś dołączyć <stdlib.h>. Może to powodować awarie.
  • Jeśli wskaźniki i liczby całkowite są różnej wielkości, to ukrywasz ostrzeżenie przesyłając i możesz stracić bity twojego zwróconego adresu.
  • Jeśli typ wskaźnika zmieni się w deklaracji, może być konieczna zmiana wszystkich wierszy, w których mallocjest wywoływany i rzutowany.

Z drugiej strony casting może zwiększyć przenośność twojego programu. tzn. pozwala kompilacji programu lub funkcji C jako C ++.


0

Dla mnie, weźmy do domu i wniosek jest taki, że castowanie mallocw C nie jest absolutnie konieczne, ale jeśli jednak rzucisz, nie będzie to miało wpływu, mallocponieważ mallocnadal przydzieli ci żądane miejsce w pamięci. Innym sposobem na zabranie do domu jest powód lub jeden z powodów, dla których ludzie rzucają casting, a to pozwala im skompilować ten sam program w C lub C ++.

Mogą być inne powody, ale inne, prawie na pewno, wpędzą cię w poważne kłopoty prędzej czy później.


0

Możesz, ale nie musisz przesyłać w C. Musisz przesyłać, jeśli ten kod jest skompilowany jako C ++.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.