Po co odrzucać wartość zwrotną free?

82

Czytam książkę ( Programowanie z wątkami POSIX autorstwa Butenhof, 1997), która używa C, i natrafiłem na następujący wiersz:

(void)free(data);

Tutaj datajest tylko wskaźnikiem do przydzielonej struktury,

data = malloc(sizeof(my_struct_t));

Dlaczego jest wynikiem freejest wrzucony do void?

Z mojego rozumienia C wydaje się to nie mieć sensu z dwóch powodów:

  • Funkcja bezpłatna już powraca void
  • Kod nie używa wartości zwracanej (nawet nie jest przypisany do zmiennej)

Książka została napisana w 1997 roku. Czy jest to jakaś spuścizna?

Autor wspomina, że ​​przykłady uruchomiono na Digital Unix 4.0d, ale wciąż nie wyobrażam sobie powodu, aby kiedykolwiek rzucić wynik funkcji, jeśli nie zamierzasz użyć tego wyniku.

Adam Johnston
źródło
Kilka możliwych wyjaśnień można znaleźć tutaj: stackoverflow.com/questions/689677/…
Timbo
3
Z ciekawości, jaka jest data publikacji twojej książki w języku C? (Która to książka?) Jeśli jest to około 1995 roku, może być jakieś uzasadnienie - standardowe kompilatory C nie były wcześniej wszechobecne. Jeśli zostanie opublikowany później i nadal zawiera obsadę (i bez wyjaśnienia dlaczego), martw się o inne złe nawyki, których cię uczy. Zdobądź nowszą książkę!
Jonathan Leffler,
10
Wygląda jak programowanie z wątkami POSIX
Eugene Sh.
3
@JathanathanLeffler, jak wspomniano w moim oryginalnym poście, książka została opublikowana w 1997 roku i używała UNIX 4.0d. Książka to „Programowanie z wątkami POSIX” Davida R. Butenhofa. Do tej pory był bardzo pouczający i został napisany przez jednego z oryginalnych współpracowników standardu wątków POSIX.
Adam Johnston,
6
Używałem swojej kopii w ciągu ostatniego tygodnia - tak, nadal jest przydatna. Został napisany na granicy „wszechobecnego standardu C” (powiedziałem „około 1995 r.”). „UNIX 4.0d” brzmi jak Digital UNIX - tam działał Butenhof, a wstęp mówi o tym. Traktuj obsadę free()jako osobliwość w książce, której nie musisz naśladować. Dawno temu było to na wpół trafne, ale już nie ma znaczenia.
Jonathan Leffler,

Odpowiedzi:

100

Jeśli mówimy o freefunkcji standardowej, to jej prototyp to

void free(void *ptr);

Dlatego obsada jest całkowicie bezużyteczna.
Teraz trochę spekulacji.

Autor mógł zapomnieć o dołączeniu stdlib.hnagłówka deklarującego ten prototyp, więc kompilator przyjmuje jego typ zwracany jako int. Teraz podczas analizy statycznej tego kodu kompilator ostrzegał o niewykorzystanej wartości zwracanej tego, co uważa za voidniefunkcjonalne. Takie ostrzeżenia są zwykle wyciszane przez dodanie obsady void.

Eugene Sh.
źródło
50
Pamiętaj jednak, że jeśli rzut został wprowadzony z podanego powodu, użycie go do wyciszenia ostrzeżenia jest nieprawidłowe . W tym przypadku kompilatorowi przypisuje się inny typ freeniż w rzeczywistości, w wyniku czego wywołanie ma nieokreślone zachowanie (zakładając semantykę C90, gdzie wywołanie funkcji niezadeklarowanej nie we wszystkich przypadkach wykazuje UB). W praktyce, jest to prawdopodobne, że spowoduje bona fide złe zachowanie na niektórych systemach. Prawidłowym rozwiązaniem jest podanie poprawnej deklaracji dla funkcji.
John Bollinger
11
W szczególności przykłady w „Programowaniu z wątkami POSIX” wielokrotnie nie zawierają odpowiednich standardowych nagłówków. Być może to była jakaś zła praktyka autora, mogli używać niestandardowej konfiguracji kompilatora, która domyślnie obejmowała wszystkie standardowe biblioteki lib.
Lundin
74

Byłoby to spuścizna!

Zanim free()pojawiłby się standard C, funkcja byłaby (domyślnie) typu int- ponieważ nie było jeszcze wiarygodnego typu, voidaby mogła ona zwrócić. Nie zwrócono żadnej wartości.

Kiedy kod został po raz pierwszy zmodyfikowany do pracy ze standardowymi kompilatorami języka C, prawdopodobnie nie zawierał <stdlib.h>(ponieważ nie istniał przed standardem). Stary kod pisałby extern char *malloc();(być może bez extern) dla funkcji alokacji (podobnie dla calloc()i realloc()) i nie musiał deklarować free(). A kod rzuciłby następnie wartość zwracaną na poprawny typ - ponieważ było to konieczne w co najmniej niektórych systemach (w tym w tym, którego nauczyłem się C).

Jakiś czas później (void)dodano obsadę, aby poinformować kompilator (lub, co bardziej prawdopodobne lint), że „zwracana wartość z free()jest celowo ignorowana”, aby uniknąć reklamacji. Ale lepiej byłoby dodać <stdlib.h>i pozwolić jej deklaracji extern void free(void *vp);powiedzieć lintkompilatorowi, że nie ma żadnej wartości do zignorowania.

JFTR: W połowie lat 80-tych ICL Perq był pierwotnie oparty na architekturze zorientowanej na słowa, a char *adres lokalizacji pamięci był zupełnie inny niż wskaźnik „any_else” w tej samej lokalizacji. Najważniejsze było char *malloc()jakoś zadeklarować ; kluczowe było rzutowanie z niego wyniku na dowolny inny typ wskaźnika. Obsada faktycznie zmieniła liczbę używaną przez procesor. (Wielką radość sprawiło również uaktualnienie pamięci głównej w naszych systemach z 1 MiB do 2 MiB - ponieważ jądro używało około 3/4 MiB, oznaczało to, że programy użytkownika mogły używać 1 1/4 MiB przed stronicowaniem itp.)

Jonathan Leffler
źródło
9
Właśnie otworzyłem kopię K&R, 1. edycja, która zawiera implementację free()na p. 177, który domyślnie zwraca int.
ex nihilo
9
Oczywiście - voidzostał dodany do niektórych systemów (być może Unix System III) przed wydaniem standardu, ale nie było to częścią C, kiedy napisano K&R 1st Edn (1978). Funkcja, która nie zwróciła wartości, została zadeklarowana bez typu zwracanego (co oznaczało, że została zwrócona int) i dopóki nie użyłeś wartości, która nie została zwrócona, nie było problemu. Norma C90 musiała traktować ten rodzaj kodu jako prawidłowy - nie udałoby mu się to normalnie, gdyby nie był. Ale C99 usunął zasady „niejawnej int” i „niejawnej deklaracji funkcji”. Nie cały kod na świecie nadrobił zaległości.
Jonathan Leffler,
5
Op twierdzi, że książka została napisana w 1997 roku. To, o czym tu mówisz, to bardzo wczesny pre-standardowy „K&R C” i wydaje się mało prawdopodobne, aby ktokolwiek napisał o tym książkę. Jedyną taką książką, która istniała według mojej wiedzy, była rzeczywiście pierwsza edycja K&R.
Lundin
Częstą (choć donkiszotyczną) praktyką było nie włączanie nagłówków, jeśli myślałeś, że możesz uciec od dorozumianej deklaracji, ponieważ ludzie sądzili, że skróci to czas kompilacji.
Spencer
Czy ktoś używa (void)obsady printf()?
Luis Colorado,
11

Ta obsada nie jest potrzebna. Prawdopodobnie nie byłby w tym czasie, ponieważ C został znormalizowany w postaci C89.

Gdyby tak było, wynikałoby to z domniemanej deklaracji . Zazwyczaj oznaczało to, że osoba pisząca kod zapomniała #include <stdlib.h>i użyto analizatora statycznego. To nie jest najlepsze obejście i o wiele lepszym pomysłem byłoby właśnie #include <stdlib.h>zamiast tego. Oto kilka sformułowań z C89 na temat niejawnej deklaracji:

Jeśli wyrażenie poprzedzające listę argumentów w nawiasach w wywołaniu funkcji składa się wyłącznie z identyfikatora, a jeśli dla tego identyfikatora nie jest widoczna deklaracja, identyfikator jest domyślnie deklarowany dokładnie tak, jakby w najbardziej wewnętrznym bloku zawierającym wywołanie funkcji deklaracja

extern int identifier();

pojawiło się.

Ale to dziwne, ponieważ nie rzutują mallocżadnego z nich malloci freeznajdują się w tym samym pliku nagłówkowym.

Możliwe jest również, że jest to tylko błąd lub jakiś sposób poinformowania czytelnika, że freenie zwraca żadnego wyniku.

SS Anne
źródło
5
To, że język został ustandaryzowany, nie oznacza, że ​​wszyscy natychmiast aktualizują łańcuch narzędzi i kod, aby się z nim dostosować. Jest prawdopodobne, że w starym stylu „K&R” C utknęło przez kolejne 8 lat. Mimo to zgadzam się, że to dziwne, że narzędzie do analizy statycznej wymagałoby rzutowania na, freeale nie na malloc.
dan04
4
@ dan04 Zwykle używasz wyniku malloc;) Nie jestem fanem pisania rzeczy takich jak (void) printf (...), aby zatrzymać wyrzucanie ostrzeżeń przez kompilator, ale „musi się kompilować bez żadnych ostrzeżeń, nawet te głupie” to coś, co zdarza się w wielu projektach.
richardb