Ce returnează malloc(0)? [duplicat] (Programare, C, Linux, Pointeri, Malloc, Realocare)

manav m-n a intrebat.

Ce face malloc(0) întoarce?

Răspunsul ar fi același pentru realloc(malloc(0),0)?

#include<stdio.h>
#include<malloc.h>
int main()
{
        printf("%p
", malloc(0));
        printf("%p
", realloc(malloc(0), 0));
        return 0;
}

Ieșire din Linux GCC:

[email protected]:~$ gcc -Wall mal.c
[email protected]:~$ ./a.out
0x9363008
(nil)
[email protected]:~$

Ieșirea se schimbă de fiecare dată pentru malloc(0). Este acesta un răspuns standard? Și de ce ar fi cineva interesat să obțină un astfel de pointer, în afară de cercetarea academică?

EDIT:

Dacă malloc(0) returnează un pointer fictiv, atunci cum funcționează următoarele:

int main()
{
    void *ptr = malloc(0);
    printf("%p
", realloc(ptr, 1024));
    return 0;
}

EDIT:

Următorul cod scoate „posibil” pentru fiecare iterație. De ce nu ar trebui să eșueze?

#include<stdio.h>
#include<malloc.h>
int main()
{

        int i;
        void *ptr;
        printf("Testing using BRUTE FORCE
");
        for (i=0; i<65000; i++)
        {
                ptr = malloc(0);
                if (ptr == realloc(ptr, 1024))
                        printf("Iteration %d: possible
", i);
                else
                {
                        printf("Failed for iteration %d
", i);
                        break;
                }
        }
        return 0;
}

Comentarii

  • @tommieb75: ajută să fi răspuns la întrebarea inițială. Acestea fiind spuse, această întrebare are două părți, iar a doua parte este mai interesantă și nu este o dublură. Vezi răspunsul meu la această întrebare pentru detalii. –  > Por Alok Singhal.
9 răspunsuri
Alok Singhal

Alții au răspuns cum malloc(0) funcționează. Voi răspunde la una dintre întrebările pe care le-ați pus și care nu a fost încă răspuns (cred). Întrebarea se referă la realloc(malloc(0), 0):

Ce face malloc(0) întoarce? Răspunsul ar fi același pentru realloc(malloc(0),0)?

Standardul spune următoarele despre realloc(ptr, size):

  • dacă ptr este NULL, , se comportă ca malloc(size),
  • altfel (ptr nu este NULL), se dezalocă vechiul pointer de obiect la de către ptr și returnează un pointer către un nou buffer alocat. Dar dacă size este 0, C89 spune că efectul este echivalent cu free(ptr). Interesant este că nu găsesc această afirmație în C99 draft (n1256 sau n1336). În C89, singura valoare sensibilă de returnat în acest caz ar fi NULL.

Așadar, există două cazuri:

  • malloc(0) returnează NULL pe o implementare. Apoi, se poate obține realloc() este echivalent cu realloc(NULL, 0). Aceasta este echivalentă cu malloc(0) de mai sus (și care este NULL în acest caz).
  • malloc(0) returnează non-NULL. Atunci, apelul este echivalent cu free(malloc(0)). În acest caz, malloc(0) și realloc(malloc(0), 0) sunt nu sunt echivalente.

Rețineți că există un caz interesant aici: în cel de-al doilea caz, când malloc(0) returnează non-NULL în caz de reușită, se poate totuși să returneze NULL pentru a indica un eșec. Acest lucru va avea ca rezultat un apel de tipul: realloc(NULL, 0), , care ar fi echivalent cu malloc(0), , care poate sau nu să returneze NULL.

Nu sunt sigur dacă omisiunea din C99 este o omisiune sau dacă înseamnă că în C99, realloc(ptr, 0) pentru non-NULL ptr nu este echivalent cu free(ptr). Tocmai am încercat acest lucru cu gcc -std=c99, , iar rezultatul de mai sus este echivalent cu free(ptr).

Editați: Cred că am înțeles care este confuzia ta:

Să ne uităm la un fragment din codul dvs. de exemplu:

ptr = malloc(0);
if (ptr == realloc(ptr, 1024))

Cele de mai sus nu este același lucru cu malloc(0) == realloc(malloc(0), 1024). În cel de-al doilea, se adaugă malloc() este efectuat de două ori, în timp ce în primul caz, treceți un pointer alocat anterior către realloc().

Să analizăm mai întâi primul cod. Presupunând că malloc(0) nu returnează NULL la succes, ptr are o valoare validă. Când se face realloc(ptr, 1024), , realloc() practic vă oferă un nou buffer care are dimensiunea 1024, iar valoarea ptr devine invalidă. O implementare conformă poate returna aceeași adresă ca și cea care se află deja în ptr. Astfel, aplicația dvs. if condiție poate returna adevărat. (Rețineți, totuși, că dacă vă uitați la valoarea lui ptr după realloc(ptr, 1024) poate fi un comportament nedefinit).

Acum, întrebarea pe care o puneți: malloc(0) == realloc(malloc(0), 1024). În acest caz, să presupunem că ambele condiții malloc(0) de pe LHS și RHS returnează non-NULL. Atunci, este garantat că sunt diferite. De asemenea, valoarea de returnare din malloc() de pe LHS nu a fost free()d încă, astfel încât orice altă malloc(), , calloc(), , sau realloc() poate să nu returneze această valoare. Acest lucru înseamnă că dacă ați scris condiția dvs. ca:

if (malloc(0) == realloc(malloc(0), 1024)
    puts("possible");

nu veți vedea possible la ieșire (cu excepția cazului în care ambele malloc() și realloc() eșuează și returnează NULL).

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    void *p1;
    void *p2;

    p1 = malloc(0);
    p2 = realloc(p1, 1024);
    if (p1 == p2)
        puts("possible, OK");

    /* Ignore the memory leaks */
    if (malloc(0) == realloc(malloc(0), 1024))
        puts("shouldn't happen, something is wrong");
    return 0;
}

Pe OS X, codul meu nu a ieșit nimic atunci când l-am rulat. Pe Linux, acesta tipărește possible, OK.

Comentarii

  • @Alok: Bună treabă. Am verificat condiția pe Linux if (malloc(0) == realloc(malloc(0), 1024). Eșuează de fiecare dată! –  > Por manav m-n.
  • Când spui „eșuează”, vrei să spui că programul de mai sus tipărește „nu ar trebui să se întâmple, ceva nu este în regulă”? –  > Por Alok Singhal.
  • „Standardul spune următoarele despre realloc(ptr, size): dacă ptr este NULL, se comportă ca și malloc(size), în caz contrar (ptr nu este NULL), dezalocă vechiul pointer de obiect la care se referă ptr și returnează un pointer la un nou buffer alocat.” Cred că ar trebui să menționez că realloc efectuează doar un free(ptr) dacă zona de memorie a trebuit să fie mutată, iar dacă id nu, nu se efectuează nicio dezalocare, ci doar o expansiune. Cel puțin așa am înțeles din man-pagina. „Dacă zona indicată a fost mutată, se face un free(ptr).” –  > Por Victor Zamanian.
  • Atât în C99, cât și în C11, secțiunea J.3 comportament definit de implementare conține: -Dacă funcțiile calloc, malloc și realloc returnează un pointer nul sau un pointer către un obiect alocat atunci când dimensiunea solicitată este zero (7.22.3). ceea ce înseamnă că realloc(ptr,0) este permis să se returneze un pointer valid (dar care nu poate fi dereglementat), cum ar fi malloc(0). –  > Por Medinoc.
Prasoon Saurav

malloc(0) este Implementarea definită în ceea ce privește C99.

De la C99 [Secțiunea 7.20.3]

Ordinea și contiguitatea spațiului de stocare alocat prin apeluri succesive la funcția funcțiile calloc, malloc și realloc este nespecificată.. Pointerul returnat în cazul în care alocarea reușește este aliniat în mod corespunzător, astfel încât să poată fi atribuit unui pointer la orice tip de obiect și apoi utilizat pentru a accesa un astfel de obiect sau o matrice de astfel de obiecte în spațiul alocat (până când spațiul este dezalocat în mod explicit). Durata de viață a unui obiect alocat se extinde de la alocare până la dezalocare. Fiecare alocare de acest tip generează un pointer către un obiect disjuns de orice alt obiect. Pointerul returnat indică începutul (cea mai mică adresă de octeți) al spațiului alocat. În cazul în care spațiul nu poate fi alocat, se returnează un pointer nul. În cazul în care dimensiunea spațiului solicitat este zero, comportamentul este definit de implementare.: fie se returnează un pointer nul, fie se comportă ca și cum dimensiunea ar fi o valoare diferită de zero, cu excepția faptului că pointerul returnat nu trebuie utilizat pentru a accesa un obiect.

Comentarii

  • PJ Plauger (membru al Comitetului de standardizare C), într-unul din articolele sale, a declarat că au existat mari discuții pe această temă și că, în cele din urmă, s-au speriat și au lăsat acest lucru la latitudinea implementării. – anon
  • @Manav: ar putea fi folosit ca un tag-pointer: este un pointer care este cu siguranță distinct de orice alt pointer valid și care nu este NULL. –  > Por Joachim Sauer.
  • returnarea unui pointer care nu este nul în cazul lui malloc(0) este coerentă și ajută la distingerea acestuia de cazul de eroare de returnare a lui NULL, care poate fi returnat de la orice argument pentru malloc, de exemplu malloc(4). –  > Por Alex Brown.
  • @BlueRaja: asta este treaba lor, să argumenteze avantajele și dezavantajele diferitelor propuneri. În acest caz, returnarea întotdeauna a unui pointer nul este convenabilă pentru unii oameni, în timp ce efectuarea unei alocări de dimensiunea 0 este convenabilă pentru alții. Comitetul este ar trebui să să le ia în considerare pe amândouă. Din nefericire, compromisul la care au ajuns ajută doar codul specific platformei și este inutil pentru oricine încearcă să codifice portabil. –  > Por Steve Jessop.
  • @TMN: pentru că „Fiecare alocare de acest tip trebuie să producă un pointer către un obiect care să nu fie legat de niciun alt obiect.” –  > Por Bill.
anon

În C89, malloc(0) depinde de implementare – nu știu dacă C99 a rezolvat acest lucru sau nu. În C++, folosind:

char * p = new char[0];

este bine definită – se obține un pointer valid, non-null. Desigur, nu puteți utiliza pointerul pentru a accesa ceea ce indică fără a invoca un comportament nedefinit.

În ceea ce privește motivul pentru care acest lucru există, este convenabil pentru unii algoritmi și înseamnă că nu trebuie să vă umpleți codul cu teste pentru valorile zero.

Comentarii

  • @Neil: malloc(0) este același lucru în C99. –  > Por Alok Singhal.
  • @Alok: asta este pur și simplu fals. C99 permite malloc(0) să returneze 0, iar implementările bune fac acest lucru. –  > Por R.. GitHub NU MAI AJUTAȚI ICE.
  • @R. Cred că ideea este că C99 nu necesită să returneze 0 sau non-0 pentru malloc(0). –  > Por Alok Singhal.
Kornel Kisielewicz

Standard C99

În cazul în care spațiul nu poate fi alocat, se returnează un indicator nul. În cazul în care dimensiunea spațiului solicitat este zero, comportamentul este definit de implementare: fie se returnează un pointer nul, fie se comportă ca și cum dimensiunea ar fi o valoare diferită de zero, cu excepția faptului că pointerul returnat nu poate fi utilizat pentru a accesa un obiect.

Desfășurare

Adresa comp.lang.c FAQ are următoarele să spună:

Standardul ANSI/ISO spune că poate face oricare dintre aceste două lucruri; comportamentul este definit de implementare (a se vedea întrebarea 11.33). Codul portabil trebuie fie să aibă grijă să nu apeleze malloc(0), fie să fie pregătit pentru posibilitatea unei returnări nule.

Așadar, este probabil cel mai bine să se evite utilizarea lui malloc(0).

Comentarii

  • Dar același pointer returnat de malloc(0), dacă nu este NULL, poate fi utilizat de realloc() pentru a indica o locație de memorie validă. Cum ar fi realloc(malloc(0), 1024); –  > Por manav m-n.
  • @Manav: De fapt, funcționează și cu NULL, realloc(NULL, 1024) este la fel ca malloc(1024) –  > Por Hasturkun.
  • Pot vreodată malloc(0) și realloc(malloc(0), 1024) să returneze aceiași pointeri ??? if (malloc(0) == realloc(malloc(0), 1024) printf(„posibil”); – –  > Por manav m-n.
Patrick Schlüter

Un aspect despre care nimeni nu a ținut să vorbească încă, în primul tău program este că realloc cu lungimea 0 este același lucru cu free.

din pagina de manual Solaris:

The realloc() modifică dimensiunea blocului indicat de ptr la size octeți și returnează un pointer la blocul (eventual mutat). Conținutul va rămâne neschimbat până la cea mai mică dintre dimensiunile nouă și veche. Dacă ptr este NULL, , realloc() se comportă ca malloc() pentru dimensiunea specificată. Dacă size este 0 și ptr nu este un pointer nul, spațiul indicat este pus la dispoziție pentru alocare ulterioară de către aplicație, deși nu este returnat sistemului. Memoria este returnată sistemului numai la terminarea aplicației.

Dacă nu se știe acest lucru, poate fi o sursă de surprize neplăcute (mi s-a întâmplat mie).

Comentarii

  • Ciudat, am menționat acest lucru în întrebarea duplicată aici… stackoverflow.com/questions/2022335/whats-the-point-in-malloc0/… -.  > Por t0mm13b.
Christoph

A se vedea C99, secțiunea 7.20.3:

În cazul în care dimensiunea spațiului solicitat este zero, comportamentul este definit de implementare: fie se returnează un pointer nul, fie se comportă ca și cum dimensiunea ar fi fost o valoare diferită de zero, cu excepția faptului că pointerul returnat nu poate fi utilizat pentru a accesa un obiect.

Acest lucru este valabil pentru toate cele trei funcții de alocare (adică calloc(), , malloc() și realloc()).

Patrick

Cred că depinde.Am verificat sursele Visual Studio 2005 și am văzut acest lucru în funcția _heap_alloc:

if (size == 0)
    size = 1;

Cred că, în multe cazuri, este posibil să doriți un pointer valid, chiar și atunci când cereți zero octeți.Acest lucru se datorează faptului că acest comportament consecvent facilitează verificarea pointerilor, deoarece: dacă aveți un pointer non-NULL este în regulă; dacă aveți un pointer NULL, probabil că aveți o problemă.De aceea, cred că majoritatea implementărilor vor returna un pointer valid, chiar și atunci când cer zero octeți.

Comentarii

  • Am văzut adesea o ajustare generală de genul size = (size+WORD_LEN)%WORD_LEN; Acest lucru menține blocurile din heap aliniate la cuvânt, ceea ce îmbunătățește adesea timpul de acces (în special pentru transferurile de blocuri). –  > Por TMN.
  • Blocurile din heap vor fi mai mult decât aliniate la cuvânt în majoritatea sistemelor; de obicei, toate blocurile vor fi aliniate pe un multiplu de 8 octeți; multe vor fi aliniate pe un multiplu de 16 octeți. Valoarea de retur trebuie să fie suficient de bine aliniată pentru orice utilizare. Pentru multe sisteme, acest lucru înseamnă că, dacă este utilizată pentru un dublu, trebuie să fie aliniată pe un multiplu de 8 octeți; pentru unele sisteme, un dublu lung poate fi de 16 octeți și așa mai departe. Și chiar dacă cipurile Intel pot gestiona date nealiniate, există o penalizare de performanță în acest sens, pe care malloc() et al ar trebui să o evite – și o fac. –  > Por Jonathan Leffler.
user9876

Dacă malloc(0) returnează un pointer fictiv, atunci cum funcționează următoarele:

void *ptr = malloc(0);

printf("%p
", realloc(ptr, 1024));

Nu știu la ce vă referiți prin „pointer fals”. Dacă malloc(0) returnează non-NULL, atunci ptr este un pointer valid către un bloc de memorie de dimensiune zero. Adresa malloc implementare salvează această informație într-un mod specific implementării. realloc cunoaște modalitatea (specifică implementării) de a afla că ptr indică un bloc de memorie de dimensiune zero.

(Cum malloc/realloc/free face acest lucru este specific implementării. O posibilitate este de a aloca cu 4 octeți mai mult decât se solicită și de a stoca dimensiunea chiar înainte de blocul de memorie. În acest caz, ((int *)ptr)[-1] ar da dimensiunea blocului de memorie, care este 0. Nu ar trebui să faceți niciodată acest lucru din codul dumneavoastră, este doar pentru a fi folosit de către realloc și free).

Comentarii

  • @user9876: „Dacă malloc(0) returnează non-NULL, atunci ptr este un pointer valid către un bloc de memorie de dimensiune zero.”…. Cum alocați un bloc de memorie valid de dimensiune „zero”, așa cum ați spus în postarea dumneavoastră? –  > Por manav m-n.
  • Cum procedează aplicația dvs. de utilizator? Chemați malloc(0) (dacă sunteți pe un sistem în care acesta returnează non-NULL). Cum implementează sistemul acest lucru? Este ca și cum ai întreba cum poți avea un fișier pe disc cu dimensiunea zero. Este în continuare un fișier valid pe disc, cu toate metadatele obișnuite (intrări în directoare etc.), doar că are o dimensiune de zero. –  > Por user9876.