Cosa significa fare un "controllo nullo" in C o C ++?

19

Ho imparato C ++ e sto avendo difficoltà a capire nulla. In particolare, i tutorial che ho letto menzionano un "controllo nulla", ma non sono sicuro di cosa significhi o perché sia necessario.

  • Che cos'è esattamente null?
  • Che cosa significa "verificare la nullità"?
  • Devo sempre controllare null?

Qualsiasi esempio di codice sarebbe molto apprezzato.

    
posta kdi 07.06.2012 - 07:03
fonte

6 risposte

26

In C e C ++, i puntatori sono intrinsecamente insicuri, ovvero, quando si designa un puntatore, è responsabilità dell'utente assicurarsi che punti in qualche modo valido; questo fa parte di ciò che riguarda la "gestione manuale della memoria" (in contrapposizione agli schemi di gestione automatica della memoria implementati in linguaggi come Java, PHP o il runtime .NET, che non consente di creare riferimenti non validi senza sforzi considerevoli).

Una soluzione comune che cattura molti errori è quella di impostare tutti i puntatori che non puntano a nulla come NULL (o, nel C ++ corretto, 0 ), e controllando ciò prima di accedere al puntatore. In particolare, è pratica comune inizializzare tutti i puntatori su NULL (a meno che tu non abbia già qualcosa a cui puntarli quando li dichiari) e impostarli su NULL quando delete o free() (a meno che non vadano fuori ambito subito dopo). Esempio (in C, ma anche C ++ valido):

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

Una versione migliore:

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

Senza il controllo Null, il passaggio di un puntatore NULL in questa funzione causerà un segfault e non c'è nulla che tu possa fare: il sistema operativo semplicemente ucciderà il tuo processo e forse core-dump o farà apparire una finestra di dialogo del rapporto di arresto. Con il controllo Null in atto, è possibile eseguire correttamente la gestione degli errori e recuperare con garbo - correggere il problema da soli, interrompere l'operazione corrente, scrivere una voce di registro, informare l'utente, qualsiasi cosa sia appropriata.

    
risposta data 07.06.2012 - 07:40
fonte
6

Le altre risposte coprivano praticamente la tua domanda esatta. Viene effettuato un controllo nullo per accertarsi che il puntatore ricevuto effettivamente punti a un'istanza valida di un tipo (oggetti, primitive, ecc.).

Comunque, aggiungerò il mio consiglio qui. Evita i controlli nulli. :) Verifiche nulle (e altre forme di Programmazione difensiva) aumentano il codice di ingombro, rendendolo effettivamente più incline agli errori rispetto ad altre tecniche di gestione degli errori.

La mia tecnica preferita quando si tratta di puntatori di oggetti consiste nell'utilizzare il schema Oggetti Null . Ciò significa restituire un (puntatore - o meglio ancora, riferimento a un) array o lista vuota anziché null, o restituire una stringa vuota ("") invece di null, o anche la stringa "0" (o qualcosa di equivalente a "niente" "nel contesto) in cui ti aspetti che venga analizzato su un numero intero.

Come bonus, ecco una piccola cosa che potresti non sapere sul puntatore nullo, che è stato (prima formalmente) implementato da C.A.R. Hoare per il linguaggio Algol W nel 1965.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

    
risposta data 07.06.2012 - 19:31
fonte
4

Il valore del puntatore nullo rappresenta un "nulla" ben definito; è un valore del puntatore non valido che è garantito per il confronto non uguale a qualsiasi altro valore del puntatore. Il tentativo di dereferenziare un puntatore nullo comporta un comportamento indefinito e di solito porta a un errore di runtime, quindi è necessario assicurarsi che un puntatore non sia NULL prima di tentare di dereferenziarlo. Un numero di funzioni di libreria C e C ++ restituirà un puntatore nullo per indicare una condizione di errore. Ad esempio, la funzione di libreria malloc restituirà un valore puntatore nullo se non è in grado di allocare il numero di byte che sono stati richiesti e il tentativo di accedere alla memoria tramite quel puntatore causerà (di solito) un errore di runtime:

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

Quindi dobbiamo assicurarci che la chiamata a malloc sia riuscita controllando il valore di p rispetto a NULL:

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

Ora, tieni duro i calzini un minuto, questo diventerà un po 'accidentato.

C'è un puntatore nullo valore e un puntatore nullo costante , e i due non sono necessariamente uguali. Il puntatore nullo valore è il valore che l'architettura sottostante utilizza per rappresentare "da nessuna parte". Questo valore può essere 0x00000000 o 0xFFFFFFFF o 0xDEADBEEF o qualcosa di completamente diverso. Non dare per scontato che il puntatore nullo valore sia sempre 0.

Il puntatore nullo costante , OTOH, è sempre un'espressione integrale con valore 0. Per quanto riguarda il tuo codice sorgente , 0 (o qualsiasi espressione integrale che valuti a 0) rappresenta un puntatore nullo. Sia C che C ++ definiscono la macro NULL come costante del puntatore nullo. Una volta compilato il codice, il puntatore nullo costante verrà sostituito con il puntatore nullo appropriato valore nel codice macchina generato.

Inoltre, tieni presente che NULL è solo uno dei molti possibili valori del puntatore non valido ; se dichiari una variabile puntatore automatico senza inizializzarla esplicitamente, come

int *p;

il valore inizialmente memorizzato nella variabile è indeterminato e potrebbe non corrispondere a un indirizzo di memoria valido o accessibile. Sfortunatamente, non esiste un modo (portatile) per stabilire se un valore di puntatore non NULL è valido o meno prima di tentare di usarlo. Quindi, se hai a che fare con i puntatori, di solito è una buona idea inizializzarli in modo esplicito su NULL quando li dichiari, e impostarli su NULL quando non puntano attivamente a qualcosa.

Si noti che questo è più un problema in C che in C ++; C ++ idiomatico non dovrebbe usare i puntatori tanto.

    
risposta data 08.06.2012 - 22:45
fonte
2

Ci sono un paio di metodi, tutti essenzialmente fanno la stessa cosa.

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

controllo null (controlla se il puntatore è nullo), versione A

if( foo == NULL)

controllo null, versione B

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

controllo null, versione C

if( foo == 0 )

Dei tre, preferisco usare il primo controllo in quanto indica esplicitamente agli sviluppatori futuri che cosa stavi cercando di verificare E chiarisce che ti aspettavi che foo fosse un puntatore.

    
risposta data 07.06.2012 - 19:07
fonte
2

Non lo fai. L'unica ragione per utilizzare un puntatore in C ++ è perché desideri esplicitamente la presenza di puntatori nulli; altrimenti, puoi prendere un riferimento, che è semanticamente più facile da usare e garantisce non nulla.

    
risposta data 07.06.2012 - 19:37
fonte
-1

Se non si controlla il valore NULL, specialmente se si tratta di un puntatore a una struttura, è possibile che si sia verificata una vulnerabilità di sicurezza - Dereferenziamento puntatore NULL. La dereferenziazione del puntatore NULL può portare ad alcune altre gravi vulnerabilità di sicurezza come l'overflow del buffer, le condizioni di competizione ... che possono consentire al pirata informatico di assumere il controllo del tuo computer.

Molti fornitori di software come Microsoft, Oracle, Adobe, Apple ... rilasciano una patch software per correggere queste vulnerabilità di sicurezza. Penso che dovresti controllare il valore NULL di ogni puntatore:)

    
risposta data 09.06.2012 - 12:15
fonte

Leggi altre domande sui tag