Domanda

Esiste un modo per determinare in modo portabile il limite superiore e inferiore dei valori del puntatore vuoto in ANSI C89 / ISO C90? (Al momento non ho una copia dello standard con me (ho uno a casa.) Naturalmente se i valori del puntatore vuoto sono garantiti come non firmati, questo compito è banale (tramite sizeof (vuoto *)); tuttavia, non riesco a ricordare se questo è garantito o meno. Posso pensare ad alcuni molto inefficienti algoritmi (incremento fino all'overflow, ecc.), ma vorrei sapere se qualcuno ha un modo relativamente economico (in termini di complessità temporale) e portatile per calcolare questi limiti.

- EDIT -

Inoltre: esiste un modo portatile per determinare la validità dei valori del puntatore?

Perché: questo è emerso in una discussione con un collega e mi ha sorpreso. Non so a cosa stia lavorando, ma voglio solo sapere perché sono interessato! : -)

È stato utile?

Soluzione

Non esiste un modo portatile per determinare se un determinato puntatore è valido o meno. Devi sapere con che tipo di sistema di memoria hai a che fare. A seconda del sistema operativo e del processore, potrebbe esserci o meno un modo per interrogare le tabelle di pagine del gestore della memoria virtuale per determinare gli intervalli validi di puntatori.

Ad esempio, su Linux, è possibile esaminare il file mmap speciale in / proc per ottenere la mappa di memoria virtuale di un processo. Ecco un esempio di cat che legge la propria mappa di memoria:

$ cat /proc/self/mmap
08048000-0804c000 r-xp 00000000 09:00 5128276                            /bin/cat
0804c000-0804d000 rw-p 00003000 09:00 5128276                            /bin/cat
0804d000-0806e000 rw-p 0804d000 00:00 0                                  [heap]
f7ca7000-f7e40000 r--p 00000000 09:00 3409654                            /usr/lib/locale/locale-archive
f7e40000-f7e41000 rw-p f7e40000 00:00 0 
f7e41000-f7f68000 r-xp 00000000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f68000-f7f6d000 r--p 00127000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6f000-f7f72000 rw-p f7f6f000 00:00 0 
f7f83000-f7f85000 rw-p f7f83000 00:00 0 
f7f85000-f7f9a000 r-xp 00000000 09:00 2637871                            /lib/ld-2.3.6.so
f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871                            /lib/ld-2.3.6.so
ff821000-ff836000 rw-p 7ffffffea000 00:00 0                              [stack]
ffffe000-fffff000 r-xp ffffe000 00:00 0                                  [vdso]

Puoi vedere gli intervalli di puntatori validi, insieme ai bit che indicano se la memoria è (r) eadable, (w) ritable, e (x) ecutable o (p) rinviata (cioè non paginata su disco) .

Altri suggerimenti

I puntatori sono garantiti dalle specifiche come non firmati. Ma perché mai vuoi trovare i limiti? " Tutto tra 0x00000001 e 0xffffffff " non è davvero un test utile, poiché il numero di puntatori validi sarà un piccolo sottoinsieme di ciò.

void * è sempre abbastanza grande da contenere un puntatore alla memoria indirizzabile. Qualsiasi altro uso è severamente vietato dall'associazione di baseball della Major League.

Esempio: dec-10 era un'architettura a 36 bit con parole a 36 bit. Tuttavia gli indirizzi erano 18 bit e si potevano contenere 2 puntatori in qualsiasi registro / parola.

Sì, questo è un esempio estremo. Se devi fare matematica con i puntatori, sizeof è valido; ma fare matematica puntatore su qualsiasi cosa diversa da una matrice contigua è più schiva che schifosa.

Infine - non usare mai un 'void *' per memorizzare un puntatore a un oggetto o un puntatore al membro in C ++. Molte implementazioni di compilatori utilizzano effettivamente più puntatori "fisici" per implementare l'ereditarietà multipla di classi concrete (o parzialmente concrete). In realtà questo non viene quasi mai perché pochissime persone usano l'ereditarietà multipla in questo modo, e quando lo fanno, molto raramente taglia e separa i puntatori. Quando viene fuori, è davvero difficile capire cosa è successo.

Devi discernere il valore intero in cui un void * può essere trasmesso dal modello di bit effettivo in memoria - il cast di un void * in un tipo intero può comportare conversioni!

Supponendo sizeof (void *) == sizeof (long) , per un void * p potrebbe essere falso quanto segue:

((long)p) == *((long *)&p)

Inoltre, lo standard non specifica se esiste anche un tipo intero abbastanza grande da contenere i valori di tutti i puntatori validi!

Pertanto, non esiste un modo portatile per fare ciò che si desidera fare ...

A parte una regione che corrisponde a NULL, non vi è alcuna restrizione (portatile) sugli indirizzi di memoria. Un sistema operativo sufficientemente potenziato potrebbe utilizzare vari meccanismi CPU / OS per fornire a ciascun processo indirizzi casuali e ben distribuiti con ogni chiamata a malloc (), e posizionare un eseguibile indipendente più ASLR può consentire l'esecuzione del codice da qualsiasi indirizzo.

So che su Win32 i puntatori a 64 bit sono estesi al segno. È divertente ispezionare un minidump a 32 bit da una macchina a 64 bit se non si firmano i puntatori di estensione.

Vedi qui per come 64 -bit pointer ( POINTER_64 ) funziona su Win32.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top