Test dei puntatori per validità (C / C ++)

C’è un modo per determinare (programmaticamente, ovviamente) se un dato puntatore è “valido”? Il controllo di NULL è semplice, ma per quanto riguarda le cose come 0x00001234? Durante il tentativo di dereferenziare questo tipo di puntatore si verifica un’eccezione / arresto anomalo.

È preferibile un metodo multipiattaforma, ma anche la piattaforma specifica (per Windows e Linux) va bene.

Aggiornamento per chiarimenti: il problema non è con puntatori scaduti / liberati / non inizializzati; invece, sto implementando un’API che prende i puntatori dal chiamante (come un puntatore a una stringa, un handle di file, ecc.). Il chiamante può inviare (per scopo o per errore) un valore non valido come puntatore. Come posso evitare un crash?

Aggiornamento per chiarimenti: il problema non è con puntatori vecchi, liberati o non inizializzati; invece, sto implementando un’API che prende i puntatori dal chiamante (come un puntatore a una stringa, un handle di file, ecc.). Il chiamante può inviare (per scopo o per errore) un valore non valido come puntatore. Come posso evitare un crash?

Non puoi fare quel controllo. Non c’è semplicemente modo di verificare se un puntatore è “valido”. Devi fidarti che quando le persone usano una funzione che accetta un puntatore, quelle persone sanno cosa stanno facendo. Se ti passano 0x4211 come valore del puntatore, allora devi fidarti che punti per indirizzare 0x4211. E se colpiscono “accidentalmente” un object, anche se si usasse qualche funzione del sistema operativo spaventoso (IsValidPtr o qualsiasi altra cosa), si scivolerebbe ancora in un errore e non fallirebbe velocemente.

Inizia a utilizzare i puntatori nulli per segnalare questo tipo di cose e informa l’utente della tua libreria che non dovrebbero usare i puntatori se tendono a passare accidentalmente dei puntatori non validi, seriamente 🙂

Prevenire un arresto anomalo causato dall’invio del chiamante con un puntatore non valido è un buon modo per rendere bug silenziosi e difficili da trovare.

Non è forse meglio per il programmatore usare la tua API per avere un chiaro messaggio che il suo codice è falso bloccandolo piuttosto che nasconderlo?

Su Win32 / 64 c’è un modo per farlo. Tenta di leggere il puntatore e cattura l’esecuzione di SEH risultante che verrà lanciata in caso di fallimento. Se non getta, allora è un puntatore valido.

Il problema con questo metodo è che restituisce solo se è ansible leggere i dati dal puntatore. Non fornisce alcuna garanzia sulla sicurezza del tipo o su un numero qualsiasi di altri invarianti. In generale, questo metodo è valido solo per dire “si, posso leggere quel particolare posto nella memoria in un momento che è passato”.

In breve, non farlo;)

Raymond Chen ha un post sul blog su questo argomento: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

Ecco tre semplici modi in cui un programma C sotto Linux può essere introspettivo sullo stato della memoria in cui è in esecuzione e sul perché la domanda abbia risposte sofisticate appropriate in alcuni contesti.

  1. Dopo aver chiamato getpagesize () e arrotondato il puntatore al limite di una pagina, puoi chiamare mincore () per scoprire se una pagina è valida e se capita di far parte del set di lavoro del processo. Nota che questo richiede alcune risorse del kernel, quindi dovresti eseguirne il benchmark e determinare se chiamare questa funzione sia veramente appropriato nella tua API. Se la tua API gestirà gli interrupt o la lettura dalle porte seriali nella memoria, è opportuno chiamarla per evitare comportamenti imprevedibili.
  2. Dopo aver chiamato stat () per determinare se è disponibile una directory / proc / self, è ansible aprire e leggere / proc / self / maps per trovare informazioni sulla regione in cui risiede un puntatore. Studia la pagina man per proc, il sistema pseudo-file delle informazioni di processo. Ovviamente questo è relativamente costoso, ma si potrebbe essere in grado di cavarsela con il caching del risultato del parse in un array che si può cercare in modo efficiente usando una ricerca binaria. Considera anche / proc / self / smaps. Se la tua API è per il calcolo ad alte prestazioni, allora il programma vorrà sapere di / proc / self / numa che è documentato sotto la pagina man di numa, l’architettura di memoria non uniforms.
  3. La chiamata get_mempolicy (MPOL_F_ADDR) è appropriata per il lavoro delle API di calcolo ad alte prestazioni in cui sono presenti più thread di esecuzione e si sta gestendo il lavoro per avere affinità per la memoria non uniforms in relazione ai core CPU e alle risorse socket. Tale api ti dirà anche se un puntatore è valido.

Sotto Microsoft Windows c’è la funzione QueryWorkingSetEx documentata sotto l’API Process Status (anche nell’API NUMA). Come corollario alla programmazione sofisticata dell’API NUMA, questa funzione consente anche di eseguire semplici attività di “puntatori di test per la validità (C / C ++)”, pertanto è improbabile che venga deprecata per almeno 15 anni.

AFAIK non c’è modo. Si dovrebbe cercare di evitare questa situazione impostando sempre i puntatori su NULL dopo aver liberato memoria.

Dai un’occhiata a questa e questa domanda. Dai anche un’occhiata ai puntatori intelligenti .

Per quanto riguarda la risposta un po ‘in questa discussione:

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () per Windows.

Il mio consiglio è di stare lontano da loro, qualcuno ha già postato questo: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx

Un altro post sullo stesso argomento e dello stesso autore (credo) è questo: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx (“IsBadXxxPtr dovrebbe essere chiamato CrashProgramRandomly “).

Se gli utenti della tua API inviano dati errati, lascialo andare in crash. Se il problema è che i dati passati non vengono utilizzati fino a un momento successivo (e ciò rende più difficile trovare la causa), aggiungi una modalità di debug in cui le stringhe e così via vengono registrate alla voce. Se sono cattivi sarà ovvio (e probabilmente crash). Se succede spesso, potrebbe valere la pena di spostare la tua API fuori processo e lasciare che si blocchino il processo API invece del processo principale.

Innanzitutto, non vedo alcun motivo nel cercare di proteggersi dal chiamante che cerca deliberatamente di provocare un incidente. Potrebbero facilmente farlo tentando di accedere attraverso un puntatore non valido. Ci sono molti altri modi: potrebbero semplicemente sovrascrivere la tua memoria o la pila. Se è necessario proteggere da questo tipo di cose, è necessario che si esegua in un processo separato utilizzando socket o altri IPC per la comunicazione.

Scriviamo un bel po ‘di software che consente ai partner / clienti / utenti di estendere le funzionalità. Inevitabilmente ogni errore ci viene segnalato prima, quindi è utile essere in grado di mostrare facilmente che il problema è nel codice del plug-in. Inoltre ci sono problemi di sicurezza e alcuni utenti sono più affidabili di altri.

Usiamo un numero di metodi diversi a seconda delle esigenze di prestazioni / volume e affidabilità. Dalla maggior parte dei preferiti:

  • processi separati che utilizzano socket (spesso trasmettono dati come testo).

  • processi separati che usano la memoria condivisa (se passano grandi quantità di dati).

  • stesso processo thread separati tramite la coda dei messaggi (se i messaggi brevi frequenti).

  • stesso processo ha separato i thread tutti i dati passati allocati da un pool di memoria.

  • stesso processo tramite chiamata diretta alla procedura – tutti i dati passati allocati da un pool di memoria.

Cerchiamo di non ricorrere mai a ciò che stai cercando di fare quando si ha a che fare con software di terze parti, specialmente quando ci vengono forniti i plug-in / librerie come binari piuttosto che come codice sorgente.

L’uso di un pool di memoria è abbastanza semplice nella maggior parte dei casi e non deve essere inefficiente. Se si assegnano i dati in primo luogo, è banale controllare i puntatori rispetto ai valori assegnati. È anche ansible memorizzare la lunghezza allocata e aggiungere valori “magici” prima e dopo i dati per verificare la presenza di un tipo di dati e di superamenti di dati validi.

Ho molta simpatia per la tua domanda, dato che sono io stesso in una posizione quasi identica. Apprezzo che molte delle risposte stanno dicendo, e sono corrette: la routine che fornisce il puntatore dovrebbe fornire un puntatore valido. Nel mio caso, è quasi inconcepibile che possano aver corrotto il puntatore – ma se fossero riusciti, sarebbe stato il mio software ad andare in crash, e ME che avrebbe avuto la colpa 🙁

Il mio requisito non è che io continui dopo un errore di segmentazione – sarebbe pericoloso – voglio solo segnalare ciò che è accaduto al cliente prima di terminarlo in modo che possano correggere il loro codice piuttosto che incolparmi!

Ecco come ho trovato a farlo (su Windows): http://www.cplusplus.com/reference/clibrary/csignal/signal/

Per dare una sinossi:

#include  using namespace std; void terminate(int param) /// Function executed if a segmentation fault is encountered during the cast to an instance. { cerr << "\nThe function received a corrupted reference - please check the user-supplied dll.\n"; cerr << "Terminating program...\n"; exit(1); } ... void MyFunction() { void (*previous_sigsegv_function)(int); previous_sigsegv_function = signal(SIGSEGV, terminate); <-- insert risky stuff here --> signal(SIGSEGV, previous_sigsegv_function); } 

Ora questo sembra comportarsi come mi auguro (stampa il messaggio di errore, quindi termina il programma) – ma se qualcuno può individuare un difetto, per favore fatemelo sapere!

Non ci sono disposizioni in C ++ per verificare la validità di un puntatore come caso generale. Si può ovviamente supporre che NULL (0x00000000) sia cattivo, e vari compilatori e librerie amano usare “valori speciali” qua e là per rendere più facile il debug (Ad esempio, se mai vedo un puntatore apparire come 0xCECECECE in Visual Studio so Ho fatto qualcosa di sbagliato) ma la verità è che dal momento che un puntatore è solo un indice nella memoria è quasi imansible dirlo semplicemente guardando il puntatore se è l’indice “giusto”.

Ci sono vari trucchi che puoi fare con dynamic_cast e RTTI per assicurare che l’object puntato sia del tipo che vuoi, ma tutti richiedono che tu stia puntando a qualcosa di valido in primo luogo.

Se si desidera assicurarsi che il programma sia in grado di rilevare i puntatori “non validi”, il mio consiglio è questo: Impostare ogni puntatore che si dichiara su NULL o un indirizzo valido immediatamente dopo la creazione e impostarlo su NULL immediatamente dopo aver liberato la memoria a cui punta. Se sei diligente su questa pratica, allora controllare NULL è tutto ciò di cui hai bisogno.

Non c’è alcun modo portatile per farlo, e farlo per piattaforms specifiche può essere qualsiasi cosa tra difficile e imansible. In ogni caso, non si dovrebbe mai scrivere codice che dipende da tale controllo: non lasciare che i puntatori assumano valori non validi in primo luogo.

Impostare il puntatore su NULL prima e dopo l’uso è una buona tecnica. Questo è facile da fare in C ++ se si gestiscono i puntatori all’interno di una class per esempio (una stringa):

 class SomeClass { public: SomeClass(); ~SomeClass(); void SetText( const char *text); char *GetText() const { return MyText; } void Clear(); private: char * MyText; }; SomeClass::SomeClass() { MyText = NULL; } SomeClass::~SomeClass() { Clear(); } void SomeClass::Clear() { if (MyText) free( MyText); MyText = NULL; } void SomeClass::Settext( const char *text) { Clear(); MyText = malloc( strlen(text)); if (MyText) strcpy( MyText, text); } 

Non è una buona politica accettare i puntatori arbitrari come parametri di input in un’API pubblica. È meglio avere tipi di “dati semplici” come un intero, una stringa o una struct (intendo una struttura classica con dati semplici all’interno, naturalmente, ufficialmente tutto può essere una struttura).

Perché? Bene perché, come altri dicono, non esiste un modo standard per sapere se ti è stato dato un puntatore valido o uno che punta alla spazzatura.

Ma a volte non hai la scelta: la tua API deve accettare un puntatore.

In questi casi, è compito del chiamante passare un buon puntatore. NULL può essere accettato come valore, ma non come puntatore alla posta indesiderata.

Puoi ricontrollare in qualche modo? Bene, quello che ho fatto in un caso del genere era definire un invariante per il tipo a cui punta il puntatore e chiamarlo quando lo si ottiene (in modalità di debug). Almeno se l’invariante fallisce (o si blocca) sai che ti è stato passato un brutto valore.

 // API that does not allow NULL void PublicApiFunction1(Person* in_person) { assert(in_person != NULL); assert(in_person->Invariant()); // Actual code... } // API that allows NULL void PublicApiFunction2(Person* in_person) { assert(in_person == NULL || in_person->Invariant()); // Actual code (must keep in mind that in_person may be NULL) } 

Come altri hanno già detto, non è ansible rilevare in modo affidabile un puntatore non valido. Prendi in considerazione alcuni dei moduli che potrebbero essere utilizzati da un puntatore non valido:

Potresti avere un puntatore nullo. Questo è quello che si potrebbe facilmente controllare e fare qualcosa.

Si potrebbe avere un puntatore da qualche parte al di fuori della memoria valida. Ciò che costituisce la memoria valida varia a seconda di come l’ambiente di runtime del sistema imposta lo spazio degli indirizzi. Sui sistemi Unix, di solito è uno spazio di indirizzi virtuali che inizia da 0 e raggiunge un numero elevato di megabyte. Sui sistemi embedded, potrebbe essere piuttosto piccolo. Potrebbe non iniziare a 0, in ogni caso. Se la tua app sembra essere in esecuzione in modalità supervisore o equivalente, il tuo puntatore potrebbe fare riferimento a un indirizzo reale, che può essere o meno sottoposto a backup con la memoria reale.

Potresti avere un puntatore da qualche parte all’interno della tua memoria valida, anche all’interno del tuo segmento di dati, bss, stack o heap, ma non puntare a un object valido. Una variante di questo è un puntatore che indicava un object valido, prima che accadesse qualcosa di brutto all’object. Le cose brutte in questo contesto includono deallocazione, danneggiamento della memoria o corruzione del puntatore.

Si potrebbe avere un puntatore illegale flat-out, come un puntatore con allineamento illegale per la cosa a cui si fa riferimento.

Il problema diventa ancora peggiore quando si considerano le architetture basate su segmenti / offset e altre implementazioni di puntatori dispari. Questo genere di cose è normalmente nascosto allo sviluppatore da buoni compilatori e da un uso giudizioso dei tipi, ma se vuoi bucare il velo e cercare di superare in astuzia gli sviluppatori di sistemi operativi e compilatori, beh, puoi, ma non c’è un modo generico per farlo che gestirà tutti i problemi che potresti incontrare.

La cosa migliore che puoi fare è consentire il crash e mettere fuori alcune buone informazioni diagnostiche.

In generale, è imansible da fare. Ecco un caso particolarmente brutto:

 struct Point2d { int x; int y; }; struct Point3d { int x; int y; int z; }; void dump(Point3 *p) { printf("[%d %d %d]\n", p->x, p->y, p->z); } Point2d points[2] = { {0, 1}, {2, 3} }; Point3d *p3 = reinterpret_cast(&points[0]); dump(p3); 

Su molte piattaforms, questo verrà stampato:

 [0 1 2] 

Stai forzando il sistema di runtime a interpretare erroneamente i bit di memoria, ma in questo caso non andrà in crash, perché tutti i bit hanno senso. Questo fa parte del design del linguaggio (guarda il polimorfismo in stile C con struct inaddr , inaddr_in , inaddr_in6 ), quindi non puoi proteggerlo in modo affidabile da nessuna piattaforma.

È incredibile quanta informazione fuorviante si possa leggere negli articoli sopra …

E anche in Microsoft msdn, la documentazione di IsBadPtr è vietata. Oh bene, preferisco l’applicazione di lavoro piuttosto che schiantarsi. Anche se il termine di lavoro potrebbe funzionare in modo errato (purché l’utente finale possa continuare con l’applicazione).

Su Google non ho trovato alcun esempio utile per Windows: ho trovato una soluzione per le app a 32 bit,

http://www.codeproject.com/script/Content/ViewAssociatedFile.aspx?rzp=%2FKB%2Fsystem%2Fdetect-driver%2F%2FDetectDriverSrc.zip&zep=DetectDriverSrc%2FDetectDriver%2Fsrc%2FdrvCppLib%2Frtti.cpp&obid=58895&obtid=2&ovid = 2

ma ho anche bisogno di supportare app a 64 bit, quindi questa soluzione non ha funzionato per me.

Ma ho raccolto i codici sorgente del vino e sono riuscito a cucinare un tipo simile di codice che avrebbe funzionato anche per le applicazioni a 64 bit – allegando il codice qui:

 #include  typedef void (*v_table_ptr)(); typedef struct _cpp_object { v_table_ptr* vtable; } cpp_object; #ifndef _WIN64 typedef struct _rtti_object_locator { unsigned int signature; int base_class_offset; unsigned int flags; const type_info *type_descriptor; //const rtti_object_hierarchy *type_hierarchy; } rtti_object_locator; #else typedef struct { unsigned int signature; int base_class_offset; unsigned int flags; unsigned int type_descriptor; unsigned int type_hierarchy; unsigned int object_locator; } rtti_object_locator; #endif /* Get type info from an object (internal) */ static const rtti_object_locator* RTTI_GetObjectLocator(void* inptr) { cpp_object* cppobj = (cpp_object*) inptr; const rtti_object_locator* obj_locator = 0; if (!IsBadReadPtr(cppobj, sizeof(void*)) && !IsBadReadPtr(cppobj->vtable - 1, sizeof(void*)) && !IsBadReadPtr((void*)cppobj->vtable[-1], sizeof(rtti_object_locator))) { obj_locator = (rtti_object_locator*) cppobj->vtable[-1]; } return obj_locator; } 

E il codice seguente può rilevare se il puntatore è valido o meno, è necessario probabilmente aggiungere un controllo NULL:

  CTest* t = new CTest(); //t = (CTest*) 0; //t = (CTest*) 0x12345678; const rtti_object_locator* ptr = RTTI_GetObjectLocator(t); #ifdef _WIN64 char *base = ptr->signature == 0 ? (char*)RtlPcToFileHeader((void*)ptr, (void**)&base) : (char*)ptr - ptr->object_locator; const type_info *td = (const type_info*)(base + ptr->type_descriptor); #else const type_info *td = ptr->type_descriptor; #endif const char* n =td->name(); 

Questo ottiene il nome della class dal puntatore: penso che dovrebbe essere sufficiente per le tue esigenze.

Una cosa di cui ho ancora paura è l’esecuzione del controllo del puntatore – nel codice snipet sopra c’è già 3-4 chiamate API in corso – potrebbe essere eccessivo per applicazioni critiche.

Sarebbe bene se qualcuno potesse misurare il sovraccarico del controllo del puntatore rispetto, ad esempio, a C # / chiamate c ++ gestite.

In effetti, qualcosa potrebbe essere fatto in occasioni specifiche: ad esempio se si desidera verificare se una stringa del puntatore di stringhe è valida, l’uso di write (fd, buf, szie) syscall può aiutarti a fare la magia: lascia che fd sia un descrittore di file temporaneo file creato per il test e buf che punta alla stringa che stai testando, se il puntatore non è valido, write () restituisce -1 e errno impostato su EFAULT che indica che buf è al di fuori dello spazio di indirizzi accessibile.

IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () per Windows.
Questi richiedono tempo proporzionale alla lunghezza del blocco, quindi per il controllo di integrità controlliamo semplicemente l’indirizzo di partenza.

Ho visto varie librerie utilizzare un metodo per controllare la memoria senza referenze e così via. Credo che semplicemente “sostituiscano” i metodi di allocazione e deallocazione della memoria (malloc / free), che ha una logica che tiene traccia dei puntatori. Suppongo che questo sia eccessivo per il tuo caso d’uso, ma sarebbe un modo per farlo.

Tecnicamente è ansible sovrascrivere l’operatore new (ed eliminare ) e raccogliere informazioni su tutta la memoria allocata, in modo da poter disporre di un metodo per verificare se la memoria heap è valida. ma:

  1. hai ancora bisogno di un modo per controllare se il puntatore è allocato nello stack ()

  2. sarà necessario definire il puntatore “valido”:

a) la memoria su quell’indirizzo è assegnata

b) la memoria a quell’indirizzo è l’indirizzo iniziale dell’object (ad esempio l’indirizzo non nel mezzo di un enorme array)

c) la memoria a quell’indirizzo è l’indirizzo iniziale dell’object del tipo atteso

In conclusione : l’approccio in questione non è C ++, è necessario definire alcune regole che assicurino che la funzione riceva i puntatori validi.

Non c’è modo di fare quel controllo in C ++. Cosa dovresti fare se un altro codice ti passa un puntatore non valido? Dovresti andare in crash. Perché? Dai un’occhiata a questo link: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx

Addendum alla risposta (s) accolta:

Supponiamo che il tuo puntatore possa contenere solo tre valori: 0, 1 e -1 dove 1 indica un puntatore valido, -1 uno non valido e 0 altro uno non valido. Qual è la probabilità che il tuo puntatore sia NULL, tutti i valori sono ugualmente probabili? 1/3. Ora estrai il caso valido, quindi per ogni caso non valido, hai un rapporto 50:50 per catturare tutti gli errori. Sembra buono vero? Ridimensiona questo valore per un puntatore a 4 byte. Ci sono 2 ^ 32 o 4294967294 valori possibili. Di questi, solo UN valore è corretto, uno è NULL e rimangono ancora 4294967292 altri casi non validi. Ricalcola: hai un test per 1 caso non valido (4294967292+ 1). Una probabilità di 2.xe-10 o 0 per scopi pratici. Tale è l’inutilità del controllo NULL.

Sai, un nuovo driver (almeno su Linux) che è in grado di farlo probabilmente non sarebbe così difficile da scrivere.

D’altra parte, sarebbe una follia build i tuoi programmi in questo modo. A meno che tu non abbia un uso specifico e unico per una cosa del genere, non lo consiglierei. Se hai creato un’applicazione di grandi dimensioni caricata con controlli di validità puntatori costanti, probabilmente sarebbe terribilmente lenta.

Questo articolo MEM10-C. Definire e utilizzare una funzione di convalida del puntatore dice che è ansible eseguire un controllo in una certa misura, specialmente con il SO Linux.

dovresti evitare questi metodi perché non funzionano. blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx – JaredPar 15 febbraio 09 alle 16:02

Se non funzionano, il prossimo aggiornamento di Windows lo risolverà? Se non funzionano a livello di concetti, la funzione verrà probabilmente rimossa completamente da Windows API.

La documentazione MSDN afferma che sono vietati e la ragione di ciò è probabilmente un difetto di ulteriore progettazione dell’applicazione (ad esempio, in generale non si dovrebbero mangiare silenziosamente i puntatori non validi – se si è incaricati della progettazione dell’intera applicazione, ovviamente) e le prestazioni / tempo del controllo del puntatore.

Ma non dovresti affermare che non funzionano a causa di alcuni blog. Nella mia applicazione di test ho verificato che funzionano.

questi collegamenti potrebbero essere utili

_CrtIsValidPointer Verifica che un intervallo di memoria specificato sia valido per la lettura e la scrittura (solo versione di debug). http://msdn.microsoft.com/en-us/library/0w1ekd5e.aspx

_CrtCheckMemory Conferma l’integrità dei blocchi di memoria allocati nell’heap di debug (solo versione di debug). http://msdn.microsoft.com/en-us/library/e73x0s4b.aspx

Di seguito funziona in Windows (qualcuno l’ha suggerito prima):

static void copy (void * target, const void * source, int size) {__try {CopyMemory (target, source, size); } __except (EXCEPTION_EXECUTE_HANDLER) {do Qualcosa (- qualunque cosa–); }}

La funzione deve essere statica, autonoma o metodo statico di qualche class. Per testare in sola lettura, copiare i dati nel buffer locale. Per testare su scrittura senza modificare i contenuti, scriverli. Puoi testare solo il primo / ultimo indirizzo. Se il puntatore non è valido, il controllo verrà passato a “doSomething” e quindi all’esterno delle parentesi. Basta non usare nulla che richieda distruttori, come CString.

Su Unix dovresti essere in grado di utilizzare un syscall del kernel che controlla il puntatore e restituisce EFAULT, come ad esempio:

 #include  #include  #include  #include  #include  #include  #include  bool isPointerBad( void * p ) { int fh = open( p, 0, 0 ); int e = errno; if ( -1 == fh && e == EFAULT ) { printf( "bad pointer: %p\n", p ); return true; } else if ( fh != -1 ) { close( fh ); } printf( "good pointer: %p\n", p ); return false; } int main() { int good = 4; isPointerBad( (void *)3 ); isPointerBad( &good ); isPointerBad( "/tmp/blah" ); return 0; } 

ritorno:

 bad pointer: 0x3 good pointer: 0x7fff375fd49c good pointer: 0x400793 

Probabilmente c’è un migliore syscall da usare rispetto a open () [forse access], poiché c’è la possibilità che ciò possa portare a un vero e proprio codepath per la creazione di file, e un successivo requisito di chiusura.