Wat betekent het om een “ null-controle ” uit te voeren in C of C ++?

Ik heb C ++ geleerd en ik kan null maar moeilijk begrijpen. In het bijzonder vermelden de tutorials die ik heb gelezen het uitvoeren van een “nulcontrole”, maar ik weet niet zeker wat dat betekent of waarom het nodig is.

  • Wat is precies null?
  • Wat betekent het om “te controleren op null”?
  • Moet ik altijd op null controleren?

Alle codevoorbeelden worden zeer op prijs gesteld.

Reacties

Antwoord

In C en C ++ zijn pointers inherent onveilig, dat wil zeggen, wanneer u een pointer verwijdert, het is uw eigen verantwoordelijkheid om ervoor te zorgen dat het ergens geldig is; dit maakt deel uit van waar handmatig geheugenbeheer over gaat (in tegenstelling tot de automatische geheugenbeheerschemas die zijn geïmplementeerd in talen zoals Java , PHP of de .NET-runtime, waardoor je niet zonder veel moeite ongeldige referenties kunt maken).

Een veelvoorkomende oplossing die veel fouten opvangt, is om alle verwijzingen in te stellen die nergens naar verwijzen. als NULL (of, in de juiste C ++, 0), en controleer dat voordat je de aanwijzer opent. In het bijzonder is het gebruikelijk om alle verwijzingen naar NULL te initialiseren (tenzij u al iets heeft om ze naar te verwijzen wanneer u ze declareert), en ze in te stellen op NULL wanneer u delete of free() ze (tenzij ze onmiddellijk daarna buiten het bereik vallen). Voorbeeld (in C, maar ook geldige C ++):

void fill_foo(int* foo) { *foo = 23; // this will crash and burn if foo is NULL } 

Een betere versie:

void fill_foo(int* foo) { if (!foo) { // this is the NULL check printf("This is wrong\n"); return; } *foo = 23; } 

Zonder de null-controle, zal het doorgeven van een NULL-pointer aan deze functie een segfault veroorzaken, en er is niets dat u kunt doen – het besturingssysteem zal eenvoudig uw proces beëindigen en misschien core-dump of een dialoogvenster voor een crashrapport openen. Met de nulcontrole op zijn plaats, kunt u de juiste foutafhandeling uitvoeren en netjes herstellen – het probleem zelf oplossen, de huidige bewerking afbreken, een logboekinvoer schrijven, de gebruiker op de hoogte stellen, wat dan ook geschikt is.

Opmerkingen

  • @MrLister wat bedoel je, null checks werken niet ‘ in C ++? Je hoeft alleen de pointer op null te initialiseren wanneer je het declareert.
  • Wat ik bedoel is dat je moet onthouden dat je de pointer op NULL moet zetten, anders won ‘ t werk. En als je je herinnert, met andere woorden, als je weet dat de pointer NULL is, hoef je ‘ toch niet fill_foo aan te roepen. fill_foo controleert of de pointer een waarde heeft, niet of de pointer een geldige waarde heeft. In C ++ is het niet gegarandeerd dat pointers NULL zijn of een geldige waarde hebben.
  • Een assert () zou hier een betere oplossing zijn. Het heeft geen zin ‘ te proberen ” veilig te zijn “. Als NULL werd doorgegeven, ‘ is duidelijk verkeerd, dus waarom zou je niet expliciet crashen om de programmeur volledig op de hoogte te stellen? (En tijdens de productie doet ‘ er niet toe, omdat je ‘ hebt bewezen dat niemand fill_foo zal noemen () met NULL, toch? Echt, het ‘ is niet zo moeilijk.)
  • Don ‘ niet vergeten om te vermelden dat een nog betere versie van deze functie referenties zou moeten gebruiken in plaats van pointers, waardoor de NULL-controle overbodig wordt.
  • Dit is niet waar handmatig geheugenbeheer over gaat, en een beheerd programma zal ook ontploffen, ( of maak op zijn minst een uitzondering, net zoals een native programma dat in de meeste talen zal doen,) als u probeert een nulreferentie te verwijderen.

Answer

De andere antwoorden dekten vrijwel uw exacte vraag. Er wordt een nulcontrole uitgevoerd om er zeker van te zijn dat de pointer die je hebt ontvangen daadwerkelijk naar een geldige instantie van een type verwijst (objecten, primitieven, enz.).

Ik ga hier mijn eigen advies toevoegen, echter. Vermijd null-controles. 🙂 Null-controles (en andere vormen van defensief programmeren) maken code onoverzichtelijk, en maken deze feitelijk gevoeliger voor fouten dan andere foutafhandelingstechnieken.

Mijn favoriete techniek als het gaat om object pointers is om het Null Object patroon te gebruiken. Dat betekent dat je een (pointer – of beter nog, verwijzing naar een) lege array of lijst retourneert in plaats van null, of het retourneren van een lege string (“”) in plaats van null, of zelfs de string “0” (of iets equivalent aan “niets” in de context) waar je verwacht dat het geparseerd wordt tot een geheel getal.

Als bonus is hier een kleinigheidje dat je misschien niet wist over de nulwijzer, die (voor het eerst formeel) werd geïmplementeerd door CAR Hoare voor de Algol W-taal in 1965.

Ik noem het mijn fout van een miljard dollar. Het was de uitvinding van de nulreferentie in 1965. Op dat moment ontwierp ik het eerste uitgebreide typesysteem voor verwijzingen in een object georiënteerde taal (ALGOL W). Mijn doel was ervoor te zorgen dat al het gebruik van referenties absoluut veilig zou zijn, waarbij de controle automatisch door de compiler zou worden uitgevoerd. Maar ik kon de verleiding niet weerstaan om een nulreferentie in te voeren, simpelweg omdat het zo was eenvoudig te implementeren. Dit heeft geleid tot ontelbare fouten, kwetsbaarheden en systeemcrashes, die de afgelopen veertig jaar waarschijnlijk een miljard dollar aan pijn en schade hebben veroorzaakt.

Opmerkingen

  • Null-object is zelfs erger dan alleen een null-pointer. Als een algoritme X gegevens Y nodig heeft die u niet heeft, dan is dat een bug in uw programma , die u eenvoudigweg verbergt door te doen alsof u dat wel doet.
  • Het hangt ervan af de context, en hoe dan ook, testen op ” gegevensaanwezigheid ” is beter dan testen op null in mijn boek. Uit mijn ervaring, als een algoritme bijvoorbeeld aan een lijst werkt en de lijst is leeg, dan heeft het algoritme simpelweg niets te doen, en het bereikt dat door alleen standaard controle-instructies te gebruiken, zoals for / foreach.
  • Als het algoritme niets te maken heeft, waarom noemt u het dan zelfs maar? En de reden waarom je het misschien in de eerste plaats wilde noemen is omdat het iets belangrijks doet .
  • @DeadMG Omdat programmas over invoer gaan, en in de echte wereld, in tegenstelling tot huiswerkopdrachten, invoer kan irrelevant zijn (bijv. leeg). Code wordt nog steeds hoe dan ook gebeld. Je hebt twee opties: je controleert op relevantie (of leegte), of je ontwerpt je algoritmen zodat ze goed lezen en goed werken zonder expliciet op relevantie te controleren met behulp van voorwaardelijke uitspraken.
  • Ik kwam hier om bijna de dezelfde opmerking, dus gaf u in plaats daarvan mijn stem. Ik zou er echter ook aan willen toevoegen dat dit representatief is voor een groter probleem van zombie-objecten – elke keer dat je objecten hebt met initialisatie in meerdere fasen (of vernietiging) die niet volledig leven maar niet helemaal dood zijn. Als je ” veilige ” code ziet in talen zonder deterministische afronding die in elke functie controles heeft toegevoegd om te zien of het object is verwijderd, het is dit algemene probleem dat het ‘ s hoofd grootbrengt. Je zou nooit if-null moeten werken, je zou moeten werken met staten die de objecten hebben die ze nodig hebben voor hun leven.

Antwoord

De null-pointerwaarde vertegenwoordigt een goed gedefinieerd “nergens”; het is een ongeldige pointer-waarde die gegarandeerd ongelijk vergelijkt met enige andere pointer-waarde. Als u probeert de verwijzing naar een null-aanwijzer ongedaan te maken, resulteert dit in ongedefinieerd gedrag en dit leidt meestal tot een runtime-fout. U wilt er dus zeker van zijn dat een aanwijzer niet NULL is voordat u probeert de verwijzing naar de aanwijzer ongedaan te maken. Een aantal C- en C ++ -bibliotheekfuncties retourneert een null-pointer om een foutconditie aan te geven. De bibliotheekfunctie malloc retourneert bijvoorbeeld een null-pointerwaarde als het aantal bytes dat is aangevraagd niet kan worden toegewezen, en een poging om toegang te krijgen tot het geheugen via die pointer zal (meestal) leiden naar een runtime-fout:

int *p = malloc(sizeof *p * N); p[0] = ...; // this will (usually) blow up if malloc returned NULL 

We moeten er dus voor zorgen dat de malloc aanroep is geslaagd door de waarde van te controleren p tegen NULL:

int *p = malloc(sizeof *p * N); if (p != NULL) // or just if (p) p[0] = ...; 

Wacht even met je sokken, dit gaat een beetje hobbelig.

Er is een null pointer waarde en een null pointer constante , en de twee zijn niet noodzakelijk hetzelfde. De null-pointer waarde is elke waarde die de onderliggende architectuur gebruikt om “nergens” weer te geven. Deze waarde kan 0x00000000 of 0xFFFFFFFF of 0xDEADBEEF zijn, of iets heel anders. Ga er niet vanuit dat de null pointer waarde altijd 0 is.

De null pointer constante , OTOH, is altijd een 0-waarde integrale expressie. Voor zover het uw broncode betreft, vertegenwoordigt 0 (of een andere integrale uitdrukking die evalueert naar 0) een null-pointer. Zowel C als C ++ definiëren de NULL-macro als de null-pointerconstante. Wanneer uw code is gecompileerd, wordt de null-pointer constante vervangen door de juiste null-pointer waarde in de gegenereerde machinecode.

Houd er ook rekening mee dat NULL slechts één van de vele mogelijke ongeldige pointer-waarden is; als je een auto pointer-variabele declareert zonder deze expliciet te initialiseren, zoals

int *p; 

de waarde die aanvankelijk in de variabele is opgeslagen onbepaald , en komt mogelijk niet overeen met een geldig of toegankelijk geheugenadres. Helaas is er geen (draagbare) manier om te bepalen of een niet-NULL pointer-waarde geldig is of niet voordat u deze probeert te gebruiken. Dus als u te maken heeft met pointers, is het meestal een goed idee om ze expliciet te initialiseren NULL wanneer u ze declareert, en om ze in te stellen op NULL wanneer ze nergens actief naar verwijzen.

Merk op dat dit meer een probleem is in C dan in C ++; idiomatische C ++ zou niet zo vaak pointers moeten gebruiken.

Answer

Er zijn een aantal methoden, die allemaal in wezen hetzelfde doen ding.

 int *foo = NULL; //sometimes set to 0x00 or 0 or 0L instead of NULL 

null-controle (controleer of de pointer null is), versie A

 if( foo == NULL) 

null-controle, versie B

 if( !foo ) //since NULL is defined as 0, !foo will return a value from a null pointer 

null-controle, versie C

 if( foo == 0 ) 

Van de drie geef ik er de voorkeur aan om het eerste vinkje te gebruiken, omdat het toekomstige ontwikkelaars expliciet vertelt waar je naar probeerde te controleren EN het duidelijk maakt dat je verwachtte dat foo een aanwijzer zou zijn.

Antwoord

Dat doe je niet. De enige reden om een pointer te gebruiken in C ++ is omdat je expliciet de aanwezigheid van null-aanwijzers wilt; anders kun je een referentie nemen, die zowel semantisch gemakkelijker te gebruiken is als niet-nul garandeert.

Opmerkingen

  • @James: ‘ nieuw ‘ in kernelmodus?
  • @James: een implementatie van C ++ die de mogelijkheden vertegenwoordigt die een aanzienlijke meerderheid van C ++ codeerders genieten. Dat omvat alle C ++ 03 taalfuncties (behalve export) en alle C ++ 03 bibliotheekfuncties en TR1 en een flink stuk C ++ 11.
  • Ik doe zou willen dat mensen ‘ niet zouden zeggen dat ” referenties garanderen niet-nul. ” Ze doneren ‘ t. Het is net zo eenvoudig om een null-referentie te genereren als een null-pointer, en ze verspreiden zich op dezelfde manier.
  • @Stargazer: De vraag is 100% overbodig als je de tools gewoon gebruikt zoals de taalontwerpers en goed praktijk suggereert dat je dat zou moeten doen.
  • @DeadMG, het maakt ‘ niet uit of het redundant is. Je hebt ‘ de vraag niet beantwoord . Ik ‘ zal het nog een keer zeggen: -1.

Antwoord

Als je de NULL-waarde niet aanvinkt, vooral als dat een pointer naar een struct is, heb je misschien een beveiligingsprobleem ontmoet – NULL pointer dereference. NULL pointer dereference kan leiden tot enkele andere ernstige beveiligingsproblemen, zoals bufferoverloop, race condition … waardoor de aanvaller de controle over je computer kan krijgen.

Veel softwareleveranciers zoals Microsoft, Oracle, Adobe, Apple … brengen een softwarepatch uit om deze beveiligingsproblemen op te lossen. Ik denk dat je dat zou moeten doen controleer de NULL-waarde van elke pointer 🙂

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *