Qual è esattamente il pericolo di utilizzare valori di debug magici (come 0xDEADBEEF) come valori letterali?

StackOverflow https://stackoverflow.com/questions/1681144

Domanda

Inutile dire che l'uso di puntatori letterali esadecimali codificati è un disastro:

int *i = 0xDEADBEEF;
// god knows if that location is available

Tuttavia, qual è esattamente il pericolo nell'usare valori letterali esadecimali come variabili? valori?

int i = 0xDEADBEEF;
// what can go wrong?

Se questi valori sono davvero "pericolosi" a causa loro utilizzare in vari scenari di debug, ciò significa che anche se non utilizzo questi valori letterali, qualsiasi programma che durante il runtime si imbatte in uno di questi valori potrebbe bloccarsi.

Qualcuno è interessato a spiegare i reali pericoli derivanti dall'uso di valori letterali esadecimali?


Modificare: giusto per chiarire, non mi riferisco all'uso generale delle costanti nel codice sorgente.Sto parlando specificamente di problemi relativi allo scenario di debug ciò potrebbe portare all'uso di valori esadecimali, con l'esempio specifico di 0xDEADBEEF.

È stato utile?

Soluzione

Credo che la preoccupazione sollevata l'indirizzo IP formattazione domanda prima di oggi non era correlato all'uso di letterali esadecimali in generale, ma l'uso specifico di 0xDEADBEEF. Almeno, questo è il modo in cui l'ho letto.

C'è una preoccupazione con l'utilizzo 0xDEADBEEF in particolare, anche se a mio parere si tratta di una piccola. Il problema è che molti debugger e sistemi di runtime hanno già cooptato questo particolare valore come un valore marcatore per indicare mucchio non allocato, cattivi puntatori sullo stack, ecc.

Non ricordo la parte superiore della mia testa solo che i sistemi di debug e runtime utilizzano questo particolare valore, ma io l'ho visto usato in questo modo diverse volte nel corso degli anni. Se si esegue il debug in uno di questi ambienti, l'esistenza della costante 0xDEADBEEF nel codice non sarà diversa dai valori nella RAM non allocato o qualsiasi altra cosa, quindi nella migliore delle ipotesi non si avrà come utile RAM discariche, e nel peggiore dei casi si otterrà avvertimenti dal debugger.

In ogni caso, questo è ciò che penso che il commentatore originale voleva dire quando ti ha detto che era un male per "l'uso in vari scenari di debug".

Altri suggerimenti

Non c'è più pericolo nel usando un esagono letterale rispetto a qualsiasi altro tipo di letterale.

Se la sessione di debug finisce l'esecuzione di dati come il codice senza di voi volerlo a, sei in un mondo di dolore in ogni caso.

Naturalmente, c'è il normale "valore magico" vs "ben di nome costante" questione codice odore / pulizia, ma che non è proprio il tipo di pericolo Credo che tu stia parlando.

Con poche eccezioni, nulla è “costante”.

Preferiamo chiamarle "variabili lente": il loro valore cambia così lentamente che non ci importa ricompilare per cambiarle.

Tuttavia, non vogliamo avere molte istanze di 0x07 in un'applicazione o in uno script di test, dove ogni istanza ha un significato diverso.

Vogliamo mettere un'etichetta su ogni costante che renda totalmente inequivocabile il suo significato.

if( x == 7 )

Cosa significa "7" nella frase sopra?È la stessa cosa di

d = y / 7;

È lo stesso significato di "7"?

I casi di test sono un problema leggermente diverso.Non abbiamo bisogno di una gestione estesa e attenta di ogni istanza di un valore letterale numerico.Serve invece la documentazione.

Possiamo, in una certa misura, spiegare da dove viene "7" includendo un piccolo suggerimento nel codice.

assertEquals( 7, someFunction(3,4), "Expected 7, see paragraph 7 of use case 7" );

Una "costante" dovrebbe essere dichiarata - e nominata - esattamente una volta.

Un "risultato" in un test unitario non è la stessa cosa di una costante e richiede un po' di attenzione nello spiegare da dove proviene.

Un esagono letterale è diverso da un letterale decimale come 1. Qualsiasi significato speciale di un valore è dovuto al contesto di un particolare programma.

Non c'è alcun motivo per cui non si dovrebbe assegnare 0xdeadbeef a una variabile.

Ma guai al programmatore che tenta di assegnare 3735928559 decimale, o 33653337357 ottale, o peggio di tutto:. 11011110101011011011111011101111 binario

big endian o little endian?

Un pericolo è quando costanti sono assegnati a una matrice o struttura con i membri di diverse dimensioni; Endian -ness del compilatore o della macchina (compresi JVM vs CLR) influenzerà l'ordinamento dei byte.

Questo problema vale per i valori non costanti, anche, naturalmente.

Ecco un, certamente forzato, ad esempio. Qual è il valore di tampone [0] dopo l'ultima riga?

const int TEST[] = { 0x01BADA55,  0xDEADBEEF };
char buffer[BUFSZ];

memcpy( buffer, (void*)TEST, sizeof(TEST));

Non vedo alcun problema con l'utilizzo come un valore. Il suo solo un numero, dopo tutto.

Non c'è pericolo in utilizzando un valore esadecimale hard-coded per un puntatore (come il tuo primo esempio) nel giusto contesto. In particolare, quando si fa molto lo sviluppo di hardware a basso livello, questo è il modo in cui si accede registri di memoria mappata. (Anche se è meglio dare loro nomi con un #define, per esempio). Ma a livello di applicazione non dovrebbe mai bisogno di fare un incarico del genere.

Io uso CAFEBABE Io non l'ho visto usato con qualsiasi debugger prima.

int *i = 0xDEADBEEF;
// god knows if that location is available

int i = 0xDEADBEEF;
// what can go wrong?

Il pericolo che vedo è lo stesso in entrambi i casi: è stato creato un valore di bandiera che non ha contesto immediato. Non c'è niente di i in entrambi i casi che mi permetta di sapere 100, 1000 o 10000 linee che c'è un valore bandiera potenzialmente critica ad esso associati. Quello che hai piantato è un bug mina che, se non ricordo per verificare la presenza in ogni uso possibile, potrei essere di fronte a un terribile problema di debug. Ogni uso di i dovrà ora apparire così:

if (i != 0xDEADBEEF) { // Curse the original designer to oblivion
    // Actual useful work goes here
}

Ripetere quanto sopra per tutti i 7000 casi in cui è necessario utilizzare i nel codice.

Ora, perché è il sopra peggiore di questa?

if (isIProperlyInitialized()) { // Which could just be a boolean
    // Actual useful work goes here
}

Come minimo, posso individuare diverse criticità:

  1. Ortografia : Sono un terribile dattilografa. Come facilmente si ad individuare 0xDAEDBEEF in una revisione del codice? O 0xDEADBEFF? D'altra parte, so che il mio compilazione sarà barf immediatamente isIProperlyInitialised () (inserire il obbligatoria s vs z dibattito qui).
  2. Esposizione di senso . Piuttosto che cercare di nascondere le vostre bandiere nel codice, hai intenzionalmente creato un metodo che il resto del codice può vedere.
  3. Opportunità per l'accoppiamento . E 'del tutto possibile che un puntatore o riferimento è collegato ad una cache vagamente definita. Un controllo di inizializzazione potrebbe essere sovraccaricato di controllare prima se il valore è nella cache, quindi per cercare di riportarlo nella cache e, se tutto ciò che non funziona, return false.

In breve, è altrettanto facile scrivere il codice si ha realmente bisogno in quanto è di creare un valore magico misterioso. Il codice-maintainer del futuro (che molto probabilmente sarà voi) vi ringrazierà.

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