Lo standard C non richiede che i puntatori nulli siano all'indirizzo zero della macchina. TUTTAVIA, 0eseguire il cast di una costante su un valore di puntatore deve risultare in un NULLpuntatore (§6.3.2.3 / 3) e la valutazione del puntatore nullo come booleano deve essere falsa. Questo può essere un po 'scomodo se davvero non vuole un indirizzo pari a zero, e NULLnon è l'indirizzo zero.
Tuttavia, con (pesanti) modifiche al compilatore e alla libreria standard, non è impossibile NULLessere rappresentati con uno schema di bit alternativo pur rimanendo rigorosamente conformi alla libreria standard. Tuttavia, non è sufficiente cambiare semplicemente la definizione di NULLse stesso, poiché in tal NULLcaso si restituirebbe vero.
In particolare, dovresti:
- Fai in modo che gli zeri letterali nelle assegnazioni ai puntatori (o cast ai puntatori) vengano convertiti in qualche altro valore magico come
-1.
- Predisporre test di uguaglianza tra i puntatori e un numero intero costante
0per verificare invece il valore magico (§6.5.9 / 6)
- Disporre per tutti i contesti in cui un tipo di puntatore viene valutato come booleano per verificare l'uguaglianza del valore magico invece di verificare lo zero. Ciò deriva dalla semantica del test di uguaglianza, ma il compilatore può implementarlo internamente in modo diverso. Vedere §6.5.13 / 3, §6.5.14 / 3, §6.5.15 / 4, §6.5.3.3 / 5, §6.8.4.1 / 2, §6.8.5 / 4
- Come sottolineato da caf, aggiorna la semantica per l'inizializzazione di oggetti statici (§6.7.8 / 10) e inizializzatori composti parziali (§6.7.8 / 21) per riflettere la nuova rappresentazione del puntatore nullo.
- Creare un modo alternativo per accedere al vero indirizzo zero.
Ci sono alcune cose che non devi gestire. Per esempio:
int x = 0;
void *p = (void*)x;
Dopo questo, pNON è garantito che sia un puntatore nullo. È necessario gestire solo assegnazioni costanti (questo è un buon approccio per accedere al vero indirizzo zero). Allo stesso modo:
int x = 0;
assert(x == (void*)0); // CAN BE FALSE
Anche:
void *p = NULL;
int x = (int)p;
xnon è garantito che lo sia 0.
In breve, questa stessa condizione è stata apparentemente considerata dal comitato del linguaggio C, e sono state fatte considerazioni per coloro che avrebbero scelto una rappresentazione alternativa per NULL. Tutto quello che devi fare ora è apportare modifiche importanti al tuo compilatore, e presto il gioco è fatto :)
Come nota a margine, potrebbe essere possibile implementare queste modifiche con una fase di trasformazione del codice sorgente prima del compilatore vero e proprio. Cioè, invece del normale flusso di preprocessore -> compilatore -> assemblatore -> linker, dovresti aggiungere un preprocessore -> trasformazione NULL -> compilatore -> assemblatore -> linker. Quindi potresti fare trasformazioni come:
p = 0;
if (p) { ... }
/* becomes */
p = (void*)-1;
if ((void*)(p) != (void*)(-1)) { ... }
Ciò richiederebbe un parser C completo, nonché un parser di tipo e un'analisi di typedef e dichiarazioni di variabili per determinare quali identificatori corrispondono ai puntatori. Tuttavia, in questo modo si potrebbe evitare di dover apportare modifiche alle parti di generazione del codice del compilatore corretto. clang può essere utile per implementarlo: capisco che sia stato progettato pensando a trasformazioni come questa. È probabile che tu debba comunque apportare modifiche anche alla libreria standard, ovviamente.
mprotectproteggere. Oppure, se la piattaforma non ha ASLR o simili, indirizzi oltre la memoria fisica della piattaforma. In bocca al lupo.