Domanda

Quello che non capisco su C / C ++ è:

Sì, tutti lo usano per ottenere eseguibili incredibilmente veloce, in modo da compilare con l'ottimizzazione attivata.

Ma per la compilazione con le informazioni di debug attivata, non ci preoccupiamo per la velocità. Allora, perché non includere ulteriori informazioni in tale modalità di compilazione, ad esempio di rilevare alcuni segfaults prima che accadano? In effetti, inserire un assert(ptr != NULL) prima di ogni accesso a un ptr puntatore. Perché il compilatore non può farlo? Anche in questo caso, che dovrebbe essere disattivata per default, ma ci dovrebbe essere una tale possibilità, credo.

Modifica Alcune persone hanno detto che il rilevamento ho suggerito non ha senso o non fa nulla che la relazione di segmentation fault non sarebbe già fare. Ma quello che ho in mente è solo un'interruzione più aggraziato e informativo, che stampa il nome del file e la linea il numero del codice incriminato, proprio come un assert() farebbe.

È stato utile?

Soluzione

Ci sono alcuni grossi problemi con la tua proposta:

Quali condizioni vuoi il compilatore di rilevare? Su Linux / x86, accesso unaligned può causare SIGBUS e stack overflow può causare SIGSEGV, ma in entrambi i casi è tecnicamente possibile scrivere l'applicazione per rilevare tali condizioni e sicuro "correttamente". controlli puntatore NULL possono essere rilevati, ma i bug più insidiosi sono dovuti alla non valido accesso puntatore, invece di puntatori NULL.

Il C e C ++ linguaggi di programmazione offrono una flessibilità sufficiente quindi è impossibile per un tempo di esecuzione di determinare con il 100% di successo se un dato indirizzo casuale è un puntatore valido di un tipo arbitrario.

Cosa ti piacerebbe l'ambiente runtime da fare quando rileva questa situazione? Non può correggere il comportamento (a meno che non si crede nella magia). Si può solo continuare l'esecuzione o uscire. Ma aspettate un attimo ... questo è quello che già succede quando un segnale viene consegnato! Il programma esce, un core dump viene generato, e che core dump può essere utilizzato da sviluppatori di applicazioni per determinare lo stato del programma quando si è arrestato.

Quello che stai sostenendo in realtà suona come si desidera eseguire l'applicazione in un debugger (gdb) o attraverso una qualche forma di virtualizzazione (valgrind). Questo è già possibile, ma non ha senso farlo per difetto, perché fornisce alcun beneficio per i non-sviluppatori.

Aggiornamento di rispondere ai commenti:

Non c'è motivo di modificare il processo di compilazione per le versioni di debug. Se avete bisogno di un "dolce" versione di debug dell'applicazione, è necessario eseguire all'interno di un debugger. E 'molto facile per avvolgere il vostro eseguibile in uno script che fa questo per voi in modo trasparente.

Altri suggerimenti

Che cosa dovrebbe fare il programma in quel caso? Se si informa l'utente di un bug, allora questo è ciò che il segfault fa.

Se si suppone di andare avanti e di evitare il bug, come fa a sapere cosa fare?

Per non parlare che se lo ha fatto in qualche modo magicamente sa come continuare correttamente, allora avete un bug nel build di rilascio (build di debug hanno lo scopo di aiutare a identificare e correggere gli errori - non nasconderli).


In risposta alla informazioni supplementari aggiunte alla domanda (immagino ho frainteso il tuo intento):

  

quello che ho in mente è solo un'interruzione più aggraziato e informativo, che stampa il nome del file e il numero di riga del codice incriminato, proprio come un assert () farebbe.

Questo è qualcosa che il compilatore potrebbe fare - come dici tu, il compilatore sarebbe essenzialmente automaticamente inserendo un assert() ovunque un puntatore è stato dereferenziato. Questo potrebbe aggiungere abbastanza significativo alle dimensioni di una build di debug, ma probabilmente sarebbe ancora accettabile per molti (o la maggior parte) scopi. Credo che questa sarebbe una ragionevole opzione per un compilatore di fornire.

Non sono sicuro di quale compilatore venditori direbbero ... Forse inviare una richiesta su sito Collegati di Microsoft per la VC ++ prodotto e vedere quello che dicono.

Sono d'accordo con Michael Burr che questo in realtà non fare o aiutare nulla.

Inoltre, questo ancora non avrebbe funzionato per i puntatori penzolanti che tendono ad essere molto più insidiosa e difficile da rintracciare rispetto puntatori nulli.

Almeno con puntatori nulli E 'abbastanza semplice per assicurarsi che siano valide prima di Deref.

Credo che il manifesto originale vuole l'applicazione per fermare nel debugger. Si dovrebbe avere accesso a tutte le variabili di stack e stack in modo si avrebbe la possibilità di capire perché il programma è in questo stato.

Se si sta sviluppando in C / C ++, un gestore di memoria di debug si può risparmiare un sacco di tempo. sovraccarichi del buffer, l'accesso cancellato la memoria, perdite di memoria, e così via sono abbastanza facili da trovare e correggere. Ci sono diversi sul mercato o si può trascorrere 2 o 3 giorni di tempo per scrivere il proprio e ottenere il 90% della funzionalità necessarie. Se si sta scrivendo applicazioni senza di loro, si stanno facendo il vostro lavoro molto più difficile di quanto dovrebbe essere.

C'è un motivo in più una semplice assert(ptr != NULL) non funziona prima di dereferenziazione un puntatore non funziona: Non ogni puntatore non valido (anche quelli che ha cominciato la vita come NULL) è infatti pari a 0

.

In primo luogo prendere in considerazione il caso in cui si dispone di una struttura con diversi membri:

struct mystruct {
    int first;
    int second;
    int third;
    int fourth;
};

Se si dispone di un puntatore a ptr mystruct e si tenta di accedere ptr->second, il compilatore sta per generare il codice che gli annunci 4 (supponendo interi a 32 bit) per ptr e accedere a tale posizione di memoria. Se ptr è 0, la posizione attuale di memoria accessibile sarà 4. Che è ancora valida, ma non sarebbe stato catturato da una semplice asserzione. (Il compilatore poteva ragionevolmente prevedere per controllare l'indirizzo di ptr prima di aggiungere 4, nel qual caso l'affermazione sarebbe prenderlo.)

In secondo luogo, si consideri il caso in cui si ha una matrice di struct mystruct e si passa un elemento arbitrario un'altra funzione. Se si tenta di accedere al secondo elemento della matrice, inizierà a 16 byte oltre il primo puntatore. Non c'è modo il compilatore poteva ragionevolmente prevedere che fare quello che vuoi in modo affidabile in tutti i casi, senza prendere legittima l'aritmetica dei puntatori.

Che cosa si vuole veramente fare è utilizzare il sistema operativo e l'hardware per la cattura di accesso alla memoria non valida e non allineati e uccidere la vostra applicazione, quindi capire come ottenere le informazioni di debug è necessario. Il modo più semplice è semplicemente quello di eseguire all'interno di un debugger. Se stai usando gcc su Linux, vedere come generare uno stacktace quando i miei C ++ app crash . Presumo ci sono modi simili a fare la stessa cosa con altri compilatori.

C'è già l'equivalente di un assert (prt! = Null) come parte del sistema operativo. Ecco perché si ottiene un segfault invece di sovrascrivere importanti dati importanti all'indirizzo 0 e poi davvero incasinare il sistema.

Dato che si dispone di un file di simboli per il vostro eseguibile, è possibile mappare la posizione dello schianto di un numero di riga. Il debugger fa per voi se si esegue nel debugger, come altri hanno detto. Visual C ++ offre ancora "just-in-time" di debug dove, quando il programma si blocca, è possibile collegare un debugger al processo si è schiantato per vedere dove sia il problema.

Tuttavia, se si desidera avere questa funzionalità su macchine in cui non è installato Visual C ++, è ancora possibile farlo con un po 'di codifica. È possibile impostare un gestore di eccezioni utilizzando SetUnhandledExceptionFilter che verrà chiamato quando il programma va in crash. Nel gestore, si può guardare il record di eccezione e utilizzare SymGetLineFromAddr64 per determinare quali la linea di origine che stava eseguendo. Ci sono molte funzioni della libreria "di debug di aiuto" che ti permette di estrarre ogni tipo di informazione. Vedere gli articoli su MSDN , e anche il articoli su www.debuginfo.com .

Quindi stai dicendo che prima che il sistema genera un errore, dovrebbe lanciare un errore .... si avvertono dell'errore imminente?

Cos'hai essere il punto? Quando ricevo un segfault, so che significa che ho ottenuto un segfault. Non ho bisogno di un messaggio separato prima a dire "si avrà ora un segfault".

Sono che manca completamente il punto qui? : P

Modifica: Capisco quello che vuoi dire a tua modifica, ma non è facile da implementare. Il problema è che non è il compilatore o la lingua o il runtime che decide quello che dovrebbe accadere se si accede a un puntatore errato. Il linguaggio fa ufficialmente promesse o garanzie su questo. Invece, i fuochi OS fuori un errore, senza sapere che si tratta di un eseguibile di debug, senza sapere che il numero di riga innescato il problema, o qualsiasi altra cosa. L'unica cosa che questo errore dice è "si è tentato di accedere indirizzo X, e non posso permetterlo. Die". Cosa dovrebbe fare il compilatore che fare con questo?

Allora, chi dovrebbe generare questo messaggio di errore è stata utile? E come? Il compilatore potrebbe farlo, certo, ma avvolgendo tutti di accesso unico puntatore nella gestione degli errori, per garantire che se si verifica una violazione segfault / accesso, lo cattura, e innescare un'assert invece. Il problema è che questo sarebbe incredibilmente lento . Non solo "troppo lento per il rilascio", ma "troppo lento per essere utilizzabile". Si presuppone inoltre che il compilatore ha accesso a tutti codice che chiamano in. Che cosa succede se si chiama una funzione in una libreria di terze parti? Pointer accede all'interno che non possono essere avvolti in codice di gestione degli errori, perché il compilatore non genera il codice per quella libreria.

Il sistema operativo potrebbe farlo, ammesso che era disposto / in grado di caricare i file di simboli rilevanti, in qualche modo di rilevare se si sta eseguendo un file eseguibile di debug e così via ... Proprio così può stampare un numero di riga. Parlare di overengineering. Questo non è certo il lavoro del sistema operativo.

E infine, cosa vorresti guadagnare in questo modo? Perché non è sufficiente accendere il debugger? Si rompe automaticamente quando succede qualcosa del genere, dando preciso numero di riga e tutto il resto.

potrebbe essere fatto, ma sarebbe terribilmente complicata, e coinvolgono sia il compilatore e il sistema operativo, e il beneficio sarebbe estremamente piccola. Si otterrebbe una scatola pop-up che indica informazioni che il debugger è già in grado di dirvi. E con queste informazioni, si sarebbe poi ... accende il debugger in ogni caso per scoprire che cosa è andato storto.

Buona idea, ma solo in un caso particolare. Cioè prima che tu sia un puntatore dereferenziazione funciton. La ragione è che il debugger sempre calciare in, ma dopo dereferenziazione un puntatore a funzione nulla lo stack è girato. Quindi, si hanno problemi a trovare il codice incriminato. Se il chiamante controllati prima di chiamare, il debugger sarebbe in grado di dare uno stack completo.

Un controllo più generico sarebbe quello di vedere se i punti di puntatore alla memoria che è eseguibile. NULL non è, ma molti sistemi operativi può anche utilizzare le funzionalità di CPU per rendere segmenti di memoria specifici non eseguibile.

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