Am aruncat rezultatul malloc?

În această întrebare, cineva a sugerat într-un comentariu că ar trebui să nu distribuție rezultatul malloc, adică

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

mai degrabă decât:

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

De ce ar fi cazul?

Comentarii la întrebare (2)
Soluția

Nu; ce don't distribuție rezultatul, deoarece:

  • Este inutil, ca void * este în mod automat și în condiții de siguranță promovat la orice alt tip pointer în acest caz.
  • Se adaugă dezordine la cod, mulaje nu sunt foarte ușor de citit (mai ales dacă este de tip pointer este lung).
  • Asta te face să te repeți, care este, în general, de rău.
  • Se poate ascunde o eroare dacă ați uitat să includ<stdlib.h>. Acest lucru poate cauza accidente (sau, mai rău, nu ** provoca un accident până mult mai târziu în unele total diferite, o parte din cod). Ia în considerare ce se întâmplă dacă indicii și întregi sunt de dimensiuni diferite; atunci&#39;re ascunde un avertisment prin turnare și-ar putea pierde bucăți de întors adresa. Notă: ca de C99 funcțiile latente sunt plecat din C, iar acest punct nu mai este relevant, deoarece nu&#39;s nici presupunerea automată că nedeclarate funcții reveniint`.

Ca o clarificare, rețineți că am spus "nu't exprimate", nu "nu't nevoie de a arunca". În opinia mea, a's un eșec pentru a include exprimate, chiar dacă ai dreptate. Pur și simplu nu există beneficii pentru a face asta, dar o grămadă de riscuri potențiale, inclusiv exprimate indică faptul că don't știu despre riscurile.

Rețineți, de asemenea, ca comentatori subliniază, că cele de mai sus vorbește despre drept C, nu C++. Eu cred foarte ferm în C și C++ ca limbi separate.

Pentru a adăuga mai mult, codul inutil repetă tip de informații (int) care pot provoca erori. L's mai bine de referință indicatorul utilizat pentru a stoca valoarea de returnare, pentru "de blocare" cele două împreună:

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

Acest lucru, de asemenea, se mută la "lungime" în față pentru o vizibilitate sporită, și picături disponibilizați paranteze cu sizeof; ei *sunt necesare doar atunci când* * * argument este un nume de tip. Mulți oameni par să nu știe (sau ignora) acest lucru, ceea ce face codul mult mai detaliată. Amintiți-vă:sizeof` nu este o funcție! :)


În timp ce se deplasează "lungime" în față poate crește vizibilitatea în unele cazuri rare, unul ar trebui să acorde atenție, de asemenea că, în cazul general, ar trebui să fie mai bine să scrie expresia ca:

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

Deoarece păstrarea sizeof în primul rând, în acest caz, asigură multiplicarea se face cu cel puțin size_t matematica.

Compara: malloc(sizeof *sită * lungime * latime) vs. malloc(lungime * lățime * sizeof *sită) cea de-a doua poate revărsa pe lungime * latime când "lățime" și "lungime" sunt mai mici tipuri de size_t.

Comentarii (32)

În C, nu't nevoie pentru a arunca valoarea de returnare a malloc. La pointer la void returnat de malloc` este automat convertit la tipul corect. Cu toate acestea, dacă doriți codul dvs. pentru a compila cu un compilator C++, o distributie este nevoie. O alternativă de preferat în rândul comunității este de a utiliza următoarele opțiuni:

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

care, în plus, te elibereaza de la a fi nevoie să vă faceți griji cu privire la schimbarea partea dreaptă a expresiei dacă schimbați tipul de sită`.

Mulajele sunt rele, așa cum s-a subliniat. Special indicatorul de mulaje.

Comentarii (8)

Ai nu distributie, deoarece:

  • Face codul mai portabil între C și C++, și ASTFEL experiența arată, o mare mulți programatori pretind că sunt scris în C, atunci când acestea sunt într-adevăr scris în C++ (sau C, plus locală compiler extensii).
  • Nu reușesc să fac atât de poate ascunde o eroare: nota tot ATÂT de exemple de confuz când să scrie tip * "versus" tip **.
  • Ideea asta nu te împiedică să observe că nu a reușit să #include un caz antet fișier lipsește pădurea de copaci. L's la fel cu a spune "nu't vă faceți griji despre faptul că nu a reușit să cere compilatorului să se plângă de a nu vedea prototipuri ... asta plictisitor stdlib.h este cel mai important lucru de reținut!"
  • Forțează o extra cognitive cross-check. Se pune la (presupusa) tipul dorit chiar lângă aritmetică te're face pentru prime de mărime variabilă. Pun pariu că ai putea face un ASTFEL de studiu care arată că malloc() bug-uri sunt prins mult mai repede atunci când există's-o aruncat. Ca cu afirmații, adnotări, care dezvăluie intenția de scădere bug-uri.
  • Repeți într-un mod care aparatul poate verifica este de multe ori o mare **** idee. În fapt, ca's de ce o afirmație este, și de această utilizare de distributie este o afirmație. Afirmațiile sunt încă cele mai generală tehnică pe care o avem pentru a obține codul corect, deoarece Turing a venit cu ideea de atât de mulți ani în urmă.
Comentarii (21)

Ca și alte precizat, nu este necesar pentru C, dar pentru C++. Dacă credeți că aveți de gând pentru a compila cod C cu un compilator C++, pentru care motive, puteți folosi un macro în schimb, cum ar fi:

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

În acest fel puteți scrie încă într-un mod foarte compact:

int *sieve = NEW(int, 1);

și acesta va compila pentru C și C++.

Comentarii (15)

De Wikipedia:

Avantaje pentru turnare

  • Inclusiv exprimate pot permite un program C sau funcția de a compila ca C++.
  • distributie permite pre-1989 versiuni de malloc care inițial a revenit un char *.
  • Turnare poate ajuta dezvoltator identifica inconsecvențele în tipul de dimensionare ar trebui să destinatie tip pointer schimbare, în special în cazul în care indicatorul este declarat departe de malloc() apel (deși compilatoare moderne și analizoare statice pot avertiza cu privire la un astfel de comportament, fără a necesita exprimate).

Dezavantaje la turnare

  • Sub ANSI C standard, exprimate este de prisos.
  • Adăugarea de distributie poate masca eșecul de a include antet stdlib.h, în care prototipul pentru malloc este găsit. În absența unui prototip pentru malloc, standardul cere ca C compiler presupunem malloc returnează un int. Dacă nu există nici o distributie, o avertizare este emis atunci când acest întreg este atribuit pointer; cu toate acestea, cu exprimate, acest avertisment nu este produs, ascunde un bug. Pe anumite arhitecturi și modele de date (cum ar fi LP64 pe sistemele pe 64 de biți, în cazul în care lung și indicii sunt pe 64-bit și int este de 32-bit), această eroare poate de fapt duce la comportament nedefinit, ca și, implicit, a declarat malloc returnează o valoare pe 32 de biți întrucât, de fapt, funcții definite de returnează un 64-bit de valoare. În funcție de asteptare convenții și memorie layout-ul, acest lucru poate duce la stack smashing. Această problemă este mai puțin probabil pentru a trece neobservat în compilatoare moderne, cum se produce uniform avertismente că o funcție nedeclarată a fost folosit, deci, un avertisment va încă mai apar. De exemplu, CCG's, comportamentul implicit este de a arăta un avertizare pe care scrie "incompatibil implicit declarație de built-in funcția" indiferent dacă distribuția este prezent sau nu.
  • în Cazul în care tip de pointer este schimbat la declarația sa, se poate de asemenea, nevoie pentru a schimba toate liniile unde malloc este numit și exprimate.

Deși malloc fără de turnare este metoda preferată și de cele mai multe programatori cu experiență alege-l, ar trebui să utilizați oricare îți place să ai cunoștință de probleme.

eu.e: Dacă aveți nevoie pentru a compila program C ca C++ (Deși este o limbă separată) trebuie să arunce urmare a folosi malloc.

Comentarii (4)

În C puteți converti implicit un gol pointer la orice alt tip de pointer, deci o distributie nu este necesar. Folosind o poate sugera la observator casual că există un motiv de ce unul este necesar, care pot fi înșelătoare.

Comentarii (0)

Nu't exprimate rezultatul malloc, deoarece acest lucru adaugă inutil dezordine la cod.

Cel mai frecvent motiv pentru care oamenii arunca urmare a malloc este, deoarece acestea sunt nu sunteți sigur cum limbajul C lucrări. Ca's un semn de avertizare: dacă nu't știu cum o anumită limbă mecanism funcționează, atunci nu't_ să ia o ghici. Uite-l sau pune pe Stack Overflow.

Unele comentarii:

  • Un gol pointer poate fi convertit la/de la orice alt tip pointer fără explicit exprimate (C11 6.3.2.3 și 6.5.16.1).

  • C++ va toate acestea, nu permit o implicite exprimate între void* și un alt tip pointer. Deci, în C++, exprimate ar fi fost corectă. Dar dacă ai un program în C++, ar trebui să folosiți " noi " și nu malloc(). Și niciodată nu ar trebui să compila cod C, folosind un compilator C++.

Dacă aveți nevoie pentru a sprijini atât C și C++ cu același cod sursă, utilizați compiler switch-uri pentru a marca diferențele. Nu încercați să sătura atât în limba standarde cu același cod, deoarece acestea nu sunt compatibile.

  • Dacă un compilator C nu poate găsi o funcție pentru că ai uitat să includă antet, veți obține un compilator/linker error despre asta. Deci, dacă ați uitat să includ<stdlib.h> asta's nu-i nimic, ai castigat't fi capabil de a construi dvs. de program.

  • Pe vechi compilatoare care să urmeze o versiune a standardului, care este mai mult de 25 de ani, uitând să includă <stdlib.h> ar duce la un comportament periculos. Pentru că în acel vechi standard, funcțiile fără o vizibilă prototip implicit convertit tipul de returnare a int. Turnare rezultatul de la malloc în mod explicit ar ascunde acest bug.

Dar care este de fapt o non-problema. Nu't cu ajutorul a 25 de ani de calculator, deci de ce ai folosi-o 25 de ani compiler?

Comentarii (3)

În C veți obține o conversie implicită de void* pentru orice alte (date) pointer.

Comentarii (2)

Turnare valoarea returnat de malloc()` nu este necesar acum, dar am'd dori să adăugați un punct care se pare că nimeni nu a subliniat:

În zilele vechi, care este, înainte de ANSI C oferă void * ca tip generic de indicii, char * este de tip pentru o astfel de utilizare. În acest caz, distribuția poate închide compilatorul avertismente.

Referință: C FAQ

Comentarii (3)

Nu este obligatoriu să arunce rezultatele malloc, când se întoarce void* , și `void* poate fi indicat pentru orice tip de date.

Comentarii (0)

Doar adăugând experiența mea, studiind ingineria calculatoarelor văd că doi sau trei profesori care l-am văzut scris în C întotdeauna exprimate malloc, cu toate acestea am cerut (cu o imensă CV-ul și înțelegerea de C) mi-a spus că este absolut inutile, dar folosit doar pentru a fi absolut specifice, și de a primi studenți în mentalitatea de a fi absolut specifice. În esență, turnare nu se va schimba ceva în modul în care aceasta funcționează, face exact ceea ce spune, alocă memorie, și turnare nu are efect, veți obține aceeași memorie, și chiar dacă ai arunca-l la altceva din greșeală (și cumva se sustrage compiler) C va accesa în același mod.

Edit: Turnare are un anumit punct. Atunci când utilizați notație matrice, codul generat trebuie să știe cât de multe locuri de memorie trebuie să avans pentru a ajunge la începutul următorului element, acest lucru este realizat prin turnare. În acest fel știi că pentru un dublu te duci 8 octeți față, în timp ce pentru un int te duci 4, și așa mai departe. Astfel, nu are nici un efect dacă utilizați indicatorul de notație, în notație matrice devine necesar.

Comentarii (3)

Aceasta este ceea ce GNU C Library Referință manualul spune:

puteți stoca rezultatul malloc în orice variabilă pointer fără o cast, deoarece ISO C convertește automat de tip void *` la alta tip de pointer, atunci când este necesar. Dar distributia este necesar în contexte altele decât operatori de atribuire sau dacă ai putea dori codul pentru a rula în tradiționale C.

Și într-adevăr ISO C11 standard (p347) spune așa:

indicatorul returnat în cazul în care alocarea reușește corespunzător aliniat astfel încât care poate fi atribuită unui pointer la orice tip de obiect cu un fundamentale aliniere cerință și apoi utilizate pentru a accesa astfel de obiect sau o serie de astfel de obiecte in spatiul alocat (până la spațiul este în mod explicit dezafectat)

Comentarii (0)

Un pointer void este un obiect generic pointer și C sprijină conversia implicită la un vid de tip pointer la alte tipuri, astfel încât nu este nevoie de explicit typecasting-o.

Cu toate acestea, dacă doriți același lucru cod perfect compatibil pe un C++ platforma, care nu are suport de conversie implicit, aveți nevoie pentru a face typecasting, deci, totul depinde de gradul de utilizare.

Comentarii (3)

Întors tip este void*, care pot fi exprimate dorit tipul de date pointer, în scopul de a fi dereferenceable.

Comentarii (1)

În limbajul C, un gol pointer pot fi atribuite la orice pointer, care este de ce tu ar trebui să nu utilizați un tip de distributie. Dacă vrei "tip în condiții de siguranță" alocarea, eu pot recomanda următoarele funcții macro, pe care am folosi întotdeauna în C proiecte:

#include 
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

Cu acestea, în loc puteți spune pur și simplu

NEW_ARRAY(sieve, length);

Pentru non-matrice dinamice, cea de-a treia trebuie să aibă funcția macro este

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

ceea ce face matrice bucle mai sigur și mai convenabil:

int i, a[100];

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

Depinde de limbajul de programare și compilatoare. Dacă utilizați malloc în C nu este nevoie să tastați-l arunce, ca acesta va automat de tip cast, cu toate Acestea, dacă dumneavoastră folosind C++, atunci ar trebui să-tip cast, pentru că malloc va returna o nul* tip.

Comentarii (1)

Oamenii folosit pentru a CCG și Zăngănit sunt rasfatati. L's nu toate așa de bine acolo.

Am fost destul de îngrozit de-a lungul anilor de uluitor în vârstă de compilatoare I'am fost necesar pentru a utiliza. Adesea, companiile și managerii adopta un ultra-conservator abordare a schimba compilatoare și nici nu va test dacă un nou compilator ( cu mai bine de respectarea standardelor și optimizarea codului ) va lucra în sistemul lor. În realitatea practică de lucru pentru dezvoltatori este că atunci când te're codificare aveți nevoie pentru a acoperi bazele și, din păcate, turnare mallocs este un obicei bun, dacă nu poți controla ceea ce compiler poate fi aplicat la cod.

Aș sugera, de asemenea, că multe organizații se aplică un standard de codificare a lor și că ar trebui să fie metoda de oameni să urmeze, dacă este definit. În absența explicită de orientare am tendința de a merge pentru cele mai susceptibile de a compila peste tot, mai degrabă decât servilă aderarea la un standard.

Argumentul pe care-l's nu este necesar sub standardele actuale este destul de valabil. Dar acest argument omite aspectele practice din lumea reală. Noi nu cod într-o lume condusă exclusiv de către standard al zilei, dar de cele practice a ceea ce îmi place să numesc "managementul local's câmp realitate". Și că's îndoit și răsucit de mai mult spațiu-timp a fost vreodată. :-)

YMMV.

Eu tind să cred că de turnare malloc ca o operație defensivă. Nu destul, nu perfect, dar, în general, în condiții de siguranță. ( Sincer, daca'nu am inclus stdlib.h atunci've cale mai multe probleme decât turnare malloc ! ).

Comentarii (0)

Nu't exprimate rezultatul malloc().

În general, *don't fontă sau din `void `**.

Tipic pentru un motiv pentru a nu face așa este că eșecul de a #includ <stdlib.h> ar putea trece neobservat. Asta e't o problemă pentru o lungă perioadă de timp acum ca C99 făcut implicit funcția de declarații ilegal, deci, dacă dumneavoastră compiler este conform cu cel puțin C99, veți obține un diagnostic mesaj.

Dar nu's a mult mai puternic motiv să nu introducă inutile pointer mulaje:

În C, o indicatorul de distributie este aproape întotdeauna o eroare. Acest lucru este pentru că de următoarea regulă (§6.5 p7 în N1570, cel mai recent proiect pentru C11):

Un obiect trebuie să aibă valoare stocată accesate numai de către un lvalue expresie care are una dintre următoarele tipuri: — un tip compatibil cu efective tip de obiect, — o calificat versiune de un tip compatibil cu efective tip de obiect, — un tip care este semnat sau nesemnat de tip corespunzătoare eficient tip de obiect, — un tip care este semnat sau nesemnat de tip corespunzătoare pentru a calificat versiune a eficient tip de obiect, — un agregat sau o uniune de tip, care include unul dintre tipurile de mai sus printre membri (incluzând, recursiv, un membru al unei subaggregate sau conținute uniunii), sau — un tip de caracter.

Acest lucru este, de asemenea, cunoscut sub numele de stricte aliasing regula. Deci următorul cod este comportament nedefinit:

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

Și, uneori, în mod surprinzător, următorul text este la fel de bine:

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;

Uneori, te nu aveți nevoie pentru a arunca indicii, dar, având în vedere stricte aliasing regula, trebuie să fie foarte atent cu ea. Deci, orice apariție a unui pointer exprimate în cod este un loc de trebuie să verificați pentru valabilitatea acestuia. Prin urmare, n-ai scrie o inutilă pointer exprimate.

tl;dr

Într-un cuvânt: Pentru că, în C, orice apariția de o indicatorul de distributie ar trebui să ridice un steag rosu pentru cod care necesită o atenție specială, niciodată nu ar trebui să scrie inutil indicatorul de mulaje.


Side note:

  • Există cazuri în care chiar ai nevoie o distributie a void *, de exemplu, dacă doriți să imprimați un pointer:

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

Distributia este necesară aici, pentru că printf() este o variadic funcție, deci implicit conversii don't de lucru.

  • În C++, situația este diferită. Turnare indicatorul de tipuri este oarecum comun (și corect) atunci când se ocupă cu obiecte din clasele derivate. Prin urmare, este logic că în C++, conversia la și de la void * e nu implicite. C++ are un întreg set de diferite arome de turnare.
Comentarii (6)

Mi-am pus în ghips pur și simplu pentru a arăta dezaprobarea față de urât gaură în sistemul de tip, care permite de cod, cum ar fi următorul fragment pentru a compila fără diagnosticare, chiar dacă nu mulaje sunt folosite pentru a aduce despre cele rele de conversie:

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

Îmi doresc să am't există (și nu't în C++), și așa am făcut. Ea reprezintă gustul meu, și programarea mea politică. Am'm nu numai turnare un pointer, dar eficient, turnare un buletin de vot, și să alunge demonii din prostie. Dacă am putea't de fapt alunga prostia, atunci cel puțin să-mi exprim dorința de a face deci cu un gest de protest.

În fapt, o bună practică este să-și încheie malloc (și prietenii), cu funcții care returnează unsigned char *, și, practic, să nu folosim niciodată void * în cod. Dacă aveți nevoie de un generic pointer-la-orice-obiect, utilizați o char * " sau " unsigned char *, și aruncă în ambele direcții. Cel relaxare, care pot fi îngăduite, probabil, este folosind funcții, cum ar fi `memset " și " memcpy fără mulaje.

Pe tema de turnare și C++ compatibilitate, daca scrii cod, astfel încât acesta compilează ca ambele C și C++ (caz în care te au a aruncat valoarea de returnare a malloc atunci când atribuirea altceva decât void *), puteți face un lucru foarte util pentru tine: puteți utiliza macro-uri pentru turnare care traduce în C++ stil aruncă atunci când compilarea ca C++, dar se reduce la un C cast atunci când compilarea ca C:

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

Dacă respectați aceste macro-uri, atunci un simplu grep căutare de codul de bază pentru acești identificatori vă va arăta în cazul în care toate modelele sunt, astfel încât să puteți evalua dacă vreuna dintre ele sunt incorecte.

Apoi, mergând mai departe, dacă în mod regulat compila codul cu C++, se va asigura utilizarea corespunzătoare a aruncat. De exemplu, dacă utilizați strip_qual doar pentru a elimina o const sau volatile, dar schimbări de program în așa fel încât o conversie de tip este acum implicat, vei primi un diagnostic, și va trebui să utilizați o combinație de mulaje pentru a obține dorit de conversie.

Pentru a vă ajuta să adere la aceste macro-uri, GNU C++ (nu C!) compilatorul are o caracteristică frumos: un opțional de diagnosticare, care este produs în toate cazurile de C stil de mulaje.

-Wold-stil-cast (C++ si Objective-C++ numai)
Avertizează dacă un stil vechi (C-style) exprimate la un non-void tip este folosit
într-un program C++. Noul stil de mulaje (dynamic_cast,
static_cast, reinterpret_cast, și const_cast) sunt mai puțin vulnerabile
la efecte nedorite și mult mai ușor pentru a căuta.

Dacă codul C compilează ca C++, puteți folosi acest -Wold-stil-cast opțiune pentru a găsi toate aparițiile (tip) de turnare a sintaxei care poate aparea in cod, și urmări aceste diagnostice înlocuindu-l cu o alegere potrivită dintre cele de mai sus macro-uri (sau o combinație, dacă este necesar).

Acest tratament de conversii este cel mai mare independent justificare tehnică pentru a lucra într-o "Curat C": combinate C și C++ dialect, care, la rândul său, din punct de vedere tehnic justifică turnare valoarea de returnare a malloc.

Comentarii (3)

Eu prefer sa fac distributia, dar nu manual. Preferata mea este folosind g_new " și " g_new0 macro-uri de volubil. Dacă glib nu este utilizat, aș adăuga similare macro-uri. Aceste macro-uri reduce duplicarea de cod, fără a compromite tip de siguranță. Dacă aveți tipul greșit, v-ar lua o implicite exprimate între non-void indicatori, care ar provoca o avertizare (eroare în C++). Dacă uitați să includă antet care definește g_new " și "g_new0, vei primi o eroare.g_new" și " g_new0ia atât de aceleași argumente, spre deosebire demalloccare are mai puține argumente decâtcalloc. Trebuie doar să adăugați0` pentru a obține zero-inițializat de memorie. Codul poate fi compilat cu un compilator C++ fără modificări.

Comentarii (0)