Variabele instellen op NULL na gratis

In mijn bedrijf is er een codeerregel die zegt dat, na het vrijmaken van geheugen, de variabele moet worden teruggezet op NULL. Bijvoorbeeld …

void some_func () 
{
    int *nPtr;
    nPtr = malloc (100);
    free (nPtr);
    nPtr = NULL;
    return;
}

Ik heb het gevoel dat, in gevallen zoals de hierboven getoonde code, het instellen op NULLgeen enkele betekenis heeft. Of mis ik iets?

Als er in dergelijke gevallen geen zin is, ga ik het opnemen met het “kwaliteitsteam” om deze codeerregel te verwijderen. Graag advies.


Antwoord 1, autoriteit 100%

Het instellen van ongebruikte aanwijzers op NULL is een defensieve stijl die bescherming biedt tegen bungelende aanwijzerbugs. Als een bungelende aanwijzer wordt gebruikt nadat deze is vrijgemaakt, kunt u willekeurig geheugen lezen of overschrijven. Als een null-pointer wordt gebruikt, krijg je op de meeste systemen een onmiddellijke crash, waardoor je meteen weet wat de fout is.

Voor lokale variabelen kan het een beetje zinloos zijn als het “duidelijk” is dat de aanwijzer niet meer wordt gebruikt nadat deze is vrijgegeven, dus deze stijl is meer geschikt voor lidgegevens en globale variabelen. Zelfs voor lokale variabelen kan het een goede benadering zijn als de functie doorgaat nadat het geheugen is vrijgegeven.

Om de stijl te voltooien, moet u de pointers ook naar NULL initialiseren voordat ze een echte pointerwaarde krijgen.


Antwoord 2, autoriteit 14%

De meeste reacties waren gericht op het voorkomen van een dubbele vrije, maar het instellen van de aanwijzer op NULL heeft nog een ander voordeel. Zodra u een aanwijzer vrijmaakt, kan dat geheugen opnieuw worden toegewezen door een andere aanroep aan malloc. Als je nog steeds de originele aanwijzer in de buurt hebt, kun je eindigen met een bug waarbij je probeert de aanwijzer gratis te gebruiken en een andere variabele corrumpeert, en dan komt je programma in een onbekende staat en kunnen er allerlei slechte dingen gebeuren (crash als je geluk hebt, datacorruptie als je pech hebt). Als u de aanwijzer na gratis op NULL had gezet, zou elke poging om later via die aanwijzer te lezen/schrijven resulteren in een segfault, wat over het algemeen de voorkeur heeft boven willekeurige geheugenbeschadiging.

Om beide redenen kan het een goed idee zijn om de aanwijzer op NULL te zetten na free(). Het is echter niet altijd nodig. Als de pointervariabele bijvoorbeeld direct na free() buiten het bereik valt, is er niet veel reden om deze op NULL in te stellen.


Antwoord 3, autoriteit 13%

Het instellen van een pointer naar NULLna freeis een twijfelachtige praktijk die vaak wordt gepopulariseerd als een regel voor “goed programmeren” op een duidelijk onjuist uitgangspunt. Het is een van die valse waarheden die tot de categorie “klinkt goed” behoren, maar in werkelijkheid absoluut niets nuttigs bereiken (en soms tot negatieve gevolgen leiden).

Naar verluidt zou het instellen van een pointer op NULLna freehet gevreesde “double free”-probleem voorkomen wanneer dezelfde pointerwaarde wordt doorgegeven aan freemeer dan eens. In werkelijkheid doet zich echter in 9 van de 10 gevallen het echte “dubbele vrije” probleem voor wanneer verschillendeaanwijzerobjecten met dezelfde aanwijzerwaarde worden gebruikt als argumenten voor free. Onnodig te zeggen dat het instellen van een aanwijzer op NULLna freeabsoluut niets oplevert om het probleem in dergelijke gevallen te voorkomen.

Natuurlijk is het mogelijk om tegen een “dubbel vrij”-probleem aan te lopen wanneer hetzelfde pointer-object als argument voor freewordt gebruikt. In werkelijkheid duiden dergelijke situaties echter normaal gesproken op een probleem met de algemene logische structuur van de code, niet op een toevallige “dubbele gratis”. Een goede manier om het probleem in dergelijke gevallen aan te pakken, is door de structuur van de code te herzien en te heroverwegen om te voorkomen dat dezelfde aanwijzer meer dan eens wordt doorgegeven aan free. In dergelijke gevallen is het instellen van de aanwijzer op NULLen het probleem “opgelost” beschouwen, niets meer dan een poging om het probleem onder het tapijt te vegen. Het zal in het algemeen gewoon niet werken, omdat het probleem met de codestructuur zich altijd op een andere manier zal manifesteren.

Ten slotte, als uw code specifiek is ontworpen om erop te vertrouwen dat de aanwijzerwaarde NULLis of niet NULL, is het prima om de aanwijzerwaarde in te stellen op NULLna free. Maar als algemene regel voor “goede praktijken” (zoals in “zet je aanwijzer altijd op NULLna free“) is het, nogmaals, een bekende en vrij nutteloze regel nep, vaak gevolgd door enkele om puur religieuze, voodoo-achtige redenen.


Antwoord 4, autoriteit 6%

Dit wordt als een goede gewoonte beschouwd om te voorkomen dat het geheugen wordt overschreven. In de bovenstaande functie is het niet nodig, maar vaak kan het toepassingsfouten vinden.

Probeer in plaats daarvan iets als dit:

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = NULL;
}
#else
#define myfree(p) do { void ** p_tmp = (p); free(*(p_tmp)); *(p_tmp) = NULL; } while (0)
#endif

De DEBUG_VERSION laat je profiel vrijmaken in foutopsporingscode, maar beide zijn functioneel hetzelfde.

Bewerken: Do … while toegevoegd zoals hieronder voorgesteld, bedankt.


Antwoord 5, autoriteit 2%

Als u een aanwijzer bereikt die free()d is geweest, kan deze breken of niet. Dat geheugen kan opnieuw worden toegewezen aan een ander deel van je programma en dan krijg je geheugenbeschadiging,

Als u de aanwijzer instelt op NULL en u deze opent, crasht het programma altijd met een segfault. Niet meer ,,soms werkt het”, niet meer ,,crashes op onvoorspelbare wijze”. Het is veel gemakkelijker te debuggen.


Antwoord 6, autoriteit 2%

Het instellen van de aanwijzer op het free‘d geheugen betekent dat elke poging om via de aanwijzer toegang te krijgen tot dat geheugen onmiddellijk zal crashen, in plaats van ongedefinieerd gedrag te veroorzaken. Het maakt het veel gemakkelijker om te bepalen waar het mis is gegaan.

Ik begrijp je argument: aangezien nPtrbuiten bereik valt direct na nPtr = NULL, lijkt er geen reden te zijn om het in te stellen op NULL. In het geval van een struct-lid of ergens anders waar de aanwijzer niet meteen buiten het bereik valt, is het logischer. Het is niet meteen duidelijk of die aanwijzer al dan niet opnieuw zal worden gebruikt door code die hem niet zou moeten gebruiken.

Het is waarschijnlijk dat de regel wordt vermeld zonder onderscheid te maken tussen deze twee gevallen, omdat het veel moeilijker is om de regel automatisch af te dwingen, laat staan ​​voor de ontwikkelaars om deze te volgen. Het kan geen kwaad om na elke gratis aanwijzers op NULLte zetten, maar het heeft de potentie om op grote problemen te wijzen.


Antwoord 7, autoriteit 2%

de meest voorkomende bug in c is de dubbele gratis. Eigenlijk doe je zoiets

free(foobar);
/* lot of code */
free(foobar);

en het eindigt behoorlijk slecht, het besturingssysteem probeert wat reeds vrijgemaakt geheugen vrij te maken en over het algemeen maakt het een fout. Dus de goede gewoonte is om NULLin te stellen, zodat u een test kunt maken en kunt controleren of u dit geheugen echt moet vrijmaken

if(foobar != null){
  free(foobar);
}

ook om op te merken dat free(NULL)niets zal doen, dus je hoeft het if-statement niet te schrijven. Ik ben niet echt een OS-goeroe, maar ik ben knap, zelfs nu de meeste besturingssystemen dubbel gratis crashen.

Dat is ook een belangrijke reden waarom alle talen met garbagecollection (Java, dotnet) er zo trots op waren dit probleem niet te hebben en ook het geheugenbeheer als geheel niet aan de ontwikkelaars over te hoeven laten.


Antwoord 8, autoriteit 2%

Het idee hierachter is om te voorkomen dat de vrijgekomen aanwijzer per ongeluk opnieuw wordt gebruikt.


Antwoord 9, autoriteit 2%

Dit (kan) echt belangrijk zijn. Hoewel je het geheugen vrijmaakt, kan een later deel van het programma iets nieuws toewijzen dat toevallig in de ruimte terechtkomt. Uw oude aanwijzer zou nu verwijzen naar een geldig stuk geheugen. Het is dan mogelijk dat iemand de aanwijzer zou gebruiken, wat resulteert in een ongeldige programmastatus.

Als je de aanwijzer NULL geeft, zal elke poging om deze te gebruiken dereferentie van 0x0 verwijderen en daar crashen, wat gemakkelijk te debuggen is. Willekeurige wijzers die naar willekeurig geheugen verwijzen, zijn moeilijk te debuggen. Het is natuurlijk niet nodig, maar daarom staat het in een document met best practices.


Antwoord 10, autoriteit 2%

Van de ANSI C-standaard:

void free(void *ptr);

De vrije functie zorgt ervoor dat de spatie
waarnaar door ptr wordt verwezen om de toewijzing ongedaan te maken,
dat wil zeggen, beschikbaar gesteld voor verdere
toewijzing. Als ptr een nulaanwijzer is,
er vindt geen actie plaats. Anders, als de
argument komt niet overeen met een pointer
eerder teruggestuurd door de calloc,
malloc , of realloc functie, of als
de ruimte is vrijgegeven door a
bel naar gratis of realloc , het gedrag
is niet gedefinieerd.

“het ongedefinieerde gedrag” is bijna altijd een programmacrash. Om dit te voorkomen is het veilig om de aanwijzer op NULL te zetten. free() zelf kan dit niet doen omdat het alleen een aanwijzer wordt doorgegeven, niet een aanwijzer naar een aanwijzer. Je kunt ook een veiligere versie van free() schrijven die de aanwijzer NULL geeft:

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

Antwoord 11, autoriteit 2%

Onlangs kwam ik dezelfde vraag tegen nadat ik op zoek was naar het antwoord. Ik kwam tot deze conclusie:

Het is een goede gewoonte, en men moet dit volgen om het op alle (embedded) systemen overdraagbaar te maken.

free()is een bibliotheekfunctie, die varieert naargelang het platform verandert, dus je zou niet moeten verwachten dat na het doorgeven van de aanwijzer naar deze functie en na het vrijmaken van geheugen, deze aanwijzer op NULL wordt gezet . Dit is mogelijk niet het geval voor een bibliotheek die voor het platform is geïmplementeerd.

ga dus altijd voor

free(ptr);
ptr = NULL;

Antwoord 12

Ik vind dit weinig hulp omdat in mijn ervaring wanneer mensen toegang krijgen tot een vrijgekomen geheugentoewijzing, dit bijna altijd is omdat ze ergens een andere verwijzing naar hebben. En dan is het in strijd met een andere persoonlijke coderingsstandaard die “Vermijd nutteloze rommel” is, dus ik doe het niet omdat ik denk dat het zelden helpt en de code iets minder leesbaar maakt.

Echter – ik zal de variabele niet op null zetten als de aanwijzer niet opnieuw moet worden gebruikt, maar vaak geeft het ontwerp op een hoger niveau me een reden om hem toch op null in te stellen. Als de aanwijzer bijvoorbeeld een lid van een klas is en ik heb verwijderd waar het naar verwijst, dan is het “contract” als je wilt van de klas dat dat lid op elk moment naar iets dat geldig is, dus het moet worden ingesteld op null om die reden. Een klein verschil, maar ik denk een belangrijke.

In c++ is het belangrijk om altijd na te denken over wie deze gegevens bezitwanneer u wat geheugen toewijst (tenzij u slimme aanwijzers gebruikt, maar zelfs dan is enig nadenken vereist). En dit proces leidt ertoe dat pointers over het algemeen lid zijn van een klasse en over het algemeen wilt u dat een klasse te allen tijde in een geldige staat is, en de gemakkelijkste manier om dat te doen is door de lidvariabele in te stellen op NULL om aan te geven dat deze wijst nu tot niets.

Een algemeen patroon is om alle lidaanwijzers in de constructor op NULL te zetten en de destructoraanroep te laten verwijderen op alle verwijzingen naar gegevens waarvan uw ontwerp zegt dat die klasse bezit. In dit geval moet u duidelijk de aanwijzer op NULL zetten wanneer u iets verwijdert om aan te geven dat u eerder geen gegevens bezit.

Dus om samen te vatten, ja, ik zet de aanwijzer vaak op NULL nadat ik iets heb verwijderd, maar het is als onderdeel van een groter ontwerp en gedachten over wie de eigenaar is van de gegevens in plaats van het blindelings volgen van een codeerstandaardregel. Ik zou dit in jouw voorbeeld niet doen, omdat ik denk dat het geen voordeel heeft om dit te doen en het voegt “rommel” toe, wat in mijn ervaring net zo verantwoordelijk is voor bugs en slechte code als dit soort dingen.


Antwoord 13

Deze regel is handig wanneer u de volgende scenario’s probeert te vermijden:

1) Je hebt een erg lange functie met ingewikkelde logica en geheugenbeheer en je wilt niet per ongeluk de aanwijzer naar het verwijderde geheugen later in de functie opnieuw gebruiken.

2) De aanwijzer is een ledenvariabele van een klasse met een vrij complex gedrag en u wilt de aanwijzer in andere functies niet per ongeluk opnieuw gebruiken.

In uw scenario, het maakt niet veel zin, maar als de functie langer zou worden, zou het er toe doen.

U kunt beweren dat het instellen van het tot null daadwerkelijk logische fouten kan maskeren, of in het geval waarin u aanneemt dat het geldig is, crasht u nog steeds op null, dus het maakt niet uit.

In het algemeen zou ik u adviseren om het te instellen op null wanneer u denkt dat het een goed idee is en niet de moeite neemt als u denkt dat het het niet waard is. Focus in plaats daarvan op het schrijven van korte functies en goed ontworpen klassen.


Antwoord 14

Dit is misschien meer een argument om alle wijzers naar nul te initialiseren, maar zoiets kan een zeer stiekeme bug zijn:

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}
void caller() {
  some_func();
  other_func();
}

pbelt in dezelfde plaats op de stapel als de eerste nPtr, dus het kan nog steeds een schijnbaar geldige aanwijzer bevatten. Toewijzen aan *pkan alle soorten niet-gerelateerde dingen overschrijven en leiden tot lelijke bugs. Vooral als de compiler lokale variabelen initialiseert met nul in de debug-modus, maar niet als optimalisaties zijn ingeschakeld. Dus de debug-builds tonen geen tekenen van de bug tijdens het ontgrendelen van builds blazen willekeurig op …


Antwoord 15

Stel de aanwijzer in die net is bevrijd om niet verplicht, maar een goede praktijk. Op deze manier kunt u 1) vermijden met behulp van een bevrijd punt 2) GRATIS IT Towice


Antwoord 16

Er zijn twee redenen:

Vermijd crashes wanneer dubbele bevrijding

Geschreven door Ragez in een dubbele vraag .

De meest voorkomende bug in C is het dubbele
vrij. Eigenlijk doe je iets als
dat

free(foobar);
/* lot of code */
free(foobar);

En het eindigt behoorlijk slecht, het besturingssysteem
om een ​​reeds bevrijder geheugen te bevrijden en
Over het algemeen is het Segfault. Dus het goede
Oefenen is om in te stellen op NULL, dus jij
kan test en controleer of je echt
Noodzaak om dit geheugen te bevrijden

if(foobar != NULL){
  free(foobar);
}

Er moet ook worden opgemerkt dat free(NULL)
zal niets doen, dus dat hoeft het niet te doen
Schrijf de IF-verklaring. ik ben niet
echt een OS Guru maar ik ben behoorlijk
Nu zouden de meeste besturingen op dubbel vallen
gratis.

Dat is ook een belangrijke reden waarom alles
Talen met Garbage Collection
(Java, DotNet) was zo trots op dat niet
dit probleem hebben en ook niet
om te vertrekken naar de ontwikkelaar de
geheugenbeheer als geheel.

Vermijd het gebruik van reeds vrijgegeven aanwijzingen

Geschreven door Martin v. Löwis in een Ander antwoord .

Instelling ongebruikte wijzers naar null is een
Defensieve stijl, bescherming tegen
Bungelende aanwijzer insecten. Als een bungelend
Pointer is toegankelijk nadat het is bevrijd,
je mag willekeurig lezen of overschrijven
geheugen. Als een null-aanwijzer is toegankelijk,
Je krijgt de meesten een onmiddellijke crash
Systemen, vertel je meteen wat
De fout is.

Voor lokale variabelen kan het een
beetje zinloos als het is
“Voor de hand liggend” dat de aanwijzer niet is
toegankelijk meer na het bevrijden, dus
Deze stijl is meer geschikt voor
Ledengegevens en globale variabelen. Ook al
Voor lokale variabelen kan het een goed zijn
aanpak als de functie doorgaat
nadat het geheugen is vrijgegeven.

Om de stijl te voltooien, moet u ook
Initialiseer de wijzers van NULL eerder
ze krijgen een echte aanwijzer toegewezen
waarde.


Antwoord 17

Om toe te voegen aan wat anderen hebben gezegd, is een goede methode voor het gebruik van aanwijzers om altijd te controleren of het een geldige aanwijzer is of niet. Iets als:


if(ptr)
   ptr->CallSomeMethod();

Door de aanwijzer expliciet te markeren als NULL nadat deze is vrijgemaakt, is dit soort gebruik in C/C++ mogelijk.


Antwoord 18

Het instellen van een pointer naar NULL is om te beschermen tegen de zogenaamde double-free – een situatie waarin free() meer dan eens wordt aangeroepen voor hetzelfde adres zonder het blok op dat adres opnieuw toe te wijzen.

Double-free leidt tot ongedefinieerd gedrag – meestal stapelt corruptie op of crasht het programma onmiddellijk. Free() aanroepen voor een NULL-aanwijzer doet niets en is daarom gegarandeerd veilig.

Dus de beste werkwijze, tenzij u nu zeker weet dat de aanwijzer het bereik onmiddellijk of zeer snel na free() verlaat, is om die aanwijzer op NULL te zetten, zodat zelfs als free() opnieuw wordt aangeroepen, deze nu wordt aangeroepen voor een NULL-aanwijzer en ongedefinieerd gedrag wordt vermeden.


Antwoord 19

Het idee is dat als je de niet langer geldige aanwijzer probeert te dereferentie nadat je deze hebt vrijgemaakt, je hard wilt falen (segfault) in plaats van stil en mysterieus.

Maar… wees voorzichtig. Niet alle systemen veroorzaken een segfault als u NULL negeert. Op (ten minste enkele versies van) AIX, *(int *)0 == 0, en Solaris is optionele compatibiliteit met deze AIX-functie.


Antwoord 20

Op de oorspronkelijke vraag:
De aanwijzer direct op NULL zetten na het vrijgeven van de inhoud is een complete verspilling van tijd, op voorwaarde dat de code aan alle vereisten voldoet, volledig is gedebugd en nooit meer zal worden gewijzigd. Aan de andere kant kan het defensief NULL-tekenen van een aanwijzer die is vrijgemaakt heel handig zijn wanneer iemand gedachteloos een nieuw codeblok onder de free() toevoegt, wanneer het ontwerp van de originele module niet correct is, en in het geval van -compileert-maar-doet-niet-wat-ik-wil-bugs.

In elk systeem is er een onbereikbaar doel om het zo gemakkelijk mogelijk te maken om het juiste te doen, en de onherleidbare kosten van onnauwkeurige metingen. In C wordt ons een reeks zeer scherpe, zeer sterke gereedschappen aangeboden, die veel dingen kunnen creëren in de handen van een geschoolde arbeider, en allerlei metaforische verwondingen kunnen veroorzaken als ze onjuist worden gehanteerd. Sommige zijn moeilijk te begrijpen of correct te gebruiken. En mensen, die van nature risicomijdend zijn, doen irrationele dingen zoals het controleren van een aanwijzer op NULL-waarde voordat ze er gratis mee bellen …

Het meetprobleem is dat wanneer u goed van minder goed probeert te onderscheiden, hoe complexer de zaak, hoe groter de kans dat u een ambigue meting krijgt. Als het doel is om alleen goede praktijken te behouden, worden sommige dubbelzinnige weggegooid met de eigenlijk niet goede. ALS het je doel is om het niet-goede te elimineren, dan kunnen de dubbelzinnigheden bij het goede blijven. De twee doelen, alleen goed houden of duidelijk slecht elimineren, lijken lijnrecht tegenover elkaar te staan, maar er is meestal een derde groep die noch het een noch het ander is, sommige van beide.

Voordat u een klacht indient bij de kwaliteitsafdeling, moet u eerst de bugdatabase doorzoeken om te zien hoe vaak of ooit ongeldige pointerwaarden problemen veroorzaakten die moesten worden opgeschreven. Als u echt het verschil wilt maken, identificeer dan het meest voorkomende probleem in uw productiecode en stel drie manieren voor om dit te voorkomen


Antwoord 21

Het is altijd raadzaam om een ​​pointervariabele te declareren met NULLzoals,

int *ptr = NULL;

Stel dat ptrverwijst naar een 0x1000geheugenadres.
Na het gebruik van free(ptr), is het altijd aan te raden om de pointervariabele ongeldig te maken door opnieuw te declareren bij NULL.
bijv.:

free(ptr);
ptr = NULL;

Indien niet opnieuw gedeclareerd tot NULL, blijft de pointervariabele naar hetzelfde adres wijzen (0x1000), deze pointervariabele wordt een bungelende aanwijzer.
Als u een andere aanwijzervariabele definieert (laten we zeggen q) en dynamisch een adres toewijst aan de nieuwe aanwijzer, bestaat de kans dat hetzelfde adres (0x1000) door een nieuwe aanwijzer wordt overgenomen variabel. Als u in dat geval dezelfde aanwijzer (ptr) gebruikt en de waarde bijwerkt op het adres waarnaar dezelfde aanwijzer verwijst (ptr), dan zal het programma uiteindelijk een waarde naar de plaats waar qwijst (aangezien pen qnaar hetzelfde adres wijzen (0x1000) ).

bijv.

*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.

Antwoord 22

Omdat u een team voor kwaliteitsborging heeft, wil ik een klein punt toevoegen over QA. Sommige geautomatiseerde QA-tools voor C zullen toewijzingen aan vrijgemaakte pointers markeren als “nutteloze toewijzing aan ptr“. Bijvoorbeeld PC-lint/FlexeLint van Gimpel Software zegt:
tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

Er zijn manieren om berichten selectief te onderdrukken, zodat u nog steeds aan beide QA-vereisten kunt voldoen, mocht uw team daartoe besluiten.


Antwoord 23

Om een ​​lang verhaal kort te maken: je wilt niet per ongeluk (per ongeluk) toegang krijgen tot het adres dat je hebt vrijgemaakt. Omdat, wanneer u het adres vrijmaakt, u dat adres in de heap toestaat om aan een andere toepassing te worden toegewezen.

Als u de aanwijzer echter niet op NULL zet en per ongeluk probeert de verwijzing naar de aanwijzer te verwijderen, of de waarde van dat adres te wijzigen; JE KUNT HET NOG STEEDS DOEN. MAAR NIET IETS DAT JE LOGISCH WILT DOEN.

Waarom heb ik nog steeds toegang tot de geheugenlocatie die ik heb vrijgemaakt? Omdat: u misschien het geheugen hebt vrijgemaakt, maar de pointervariabele had nog steeds informatie over het heapgeheugenadres. Stel deze dus als defensieve strategie in op NULL.

Other episodes