2016-01-01 11 views
26

ero andato ad un'intervista in cui mi è stato chiesto alla domanda:Cosa succederà se '&' non viene inserito in un'istruzione 'scanf' in C?

What do you think about the following?

int i; 
scanf ("%d", i); 
printf ("i: %d\n", i); 

ho risposto:

  • Il programma verrà compilato con successo.
  • stamperà il numero in modo non corretto, ma verrà eseguito fino alla fine, senza schiantarsi

La risposta che ho fatto è stato sbagliato. Sono stato sopraffatto

Dopo che mi hanno respinto:

The program would crash in some cases and lead to an core dump.

non riuscivo a capire perché programma potrebbe andare in crash? Qualcuno potrebbe spiegarmi la ragione. Qualsiasi aiuto apprezzato.

+8

Qualsiasi compilatore decente si lamenterà di come 'scanf' si aspetta un puntatore –

+0

Si prega di non eseguire il rollback di queste modifiche ai tag. Quel tag è stato rimosso per un motivo. –

+0

@BradLarson, va bene, non lo farò. All'inizio ho pensato che quel tag fosse appropriato per questa domanda. Potresti dirmi il motivo della rimozione del tag? –

risposta

42

Quando una variabile è definita, il compilatore alloca la memoria per quella variabile.

int i; // The compiler will allocate sizeof(int) bytes for i 

i sopra definito non è inizializzato e hanno valore indeterminato.

Per scrivere i dati in quella posizione di memoria allocata per i, è necessario specificare l'indirizzo della variabile. La dichiarazione

scanf("%d", &i); 

scriverà un dato int dall'utente per la posizione di memoria allocata per i.

Se & non è posto prima i, quindi scanf cercherà di scrivere i dati di ingresso alla posizione di memoria i anziché &i. Poiché i contiene un valore indeterminato, esistono alcune possibilità che esso possa contenere il valore equivalente al valore di un indirizzo di memoria o che possa contenere il valore che non rientra nell'intervallo dell'indirizzo di memoria.

In entrambi i casi, il programma potrebbe comportarsi in modo errato e comportare un comportamento indefinito. In tal caso potrebbe succedere di tutto.

+5

È quasi garantito che si verificherà un arresto anomalo, poiché si otterrà un errore di protezione della memoria o l'equivalente su qualunque sistema lo stia eseguendo. Ovviamente, altre cose * potrebbero * accadere, a seconda delle dimensioni del programma, della memoria allocata, ecc., Ma i miei guadagni su un sistema/crash dell'applicazione. – sfdcfox

+1

@sfdcfox i miei soldi sul compilatore notano un comportamento indefinito e trattano il rispettivo blocco di codice come irraggiungibile (= rimuovendolo dal codice e reindirizzando qualsiasi percorso di codice che lo conduce). –

+0

@JanDvorak Ho visto questa idea che il compilatore può effettivamente "cancellare" UB menzionato prima, ma conosci un esempio in cui possiamo vederlo in azione? Sembra difficile da credere - motivo per cui sono abbastanza sicuro che sarà vero, dato che questo è il C++ dopo tutto. ;-) Specificamente qui, vuoi dire che l'errato 'scanf' potrebbe non essere raggiunto, quindi l'utente non può essere richiesto per l'input? Non sapevo che "as-if" potesse produrre effetti collaterali osservabili, ma suppongo che questa regola non si applichi a UB, come tutto il resto. –

17

Beacuse richiama un comportamento non definito. La famiglia di funzioni scanf() prevede un puntatore a un numero intero quando viene trovato lo specificatore "%d". Stai passando un numero intero che può essere interpretato come l'indirizzo di qualche intero ma non lo è. Questo non ha un comportamento definito nello standard. Si compilerà infatti (emetterà comunque un avvertimento) ma funzionerà sicuramente in modo inaspettato.

Nel codice così com'è, c'è ancora un altro problema. La variabile i non viene mai inizializzata, quindi avrà un valore indeterminato, un altro motivo per Comportamento non definito.

Si noti che lo standard non dice nulla di ciò che accade quando si passa un dato tipo quando ci si aspetta un altro tipo, è semplicemente un comportamento indefinito, non importa quali tipi si scambiano. Ma questa particolare situazione rientra in una considerazione speciale perché i puntatori possono essere convertiti in numeri interi, sebbene il comportamento sia definito solo se si converte nuovamente in un puntatore e se il tipo intero è in grado di memorizzare correttamente il valore. Questo è il motivo per cui viene compilato, ma sicuramente non funziona correttamente.

+0

Ma nei giorni del college, quando stavo studiando da _Spirit of C_, c'era una frase del genere: 'scanf ("% s ", name);' –

+10

^che funziona perché 'name' è probabilmente un array di caratteri, e il nome di un array è uguale all'indirizzo del primo elemento di quell'array. – kfx

+0

Questo è molto diverso. Sicuramente 'name' era un array e gli array sono convertiti automaticamente in puntatori al loro primo elemento. Inoltre, sarebbe più sicuro 'char name [100]; scanf ("% 99s", nome); '. –

9

Si sono passati dati con il tipo errato (int* è previsto, ma si passa int a scanf(). Ciò condurrà a comportamento non definito.

Tutto può accadere per comportamento non definito. Il programma potrebbe bloccarsi e potrebbe non bloccarsi.

In un ambiente tipico, suppongo che il programma si arresti in modo anomalo quando alcuni "indirizzi" che puntano a una posizione che non è autorizzata a scrivere dal sistema operativo vengono passati a scanf() e la scrittura in tale posizione avrà il SO terminare il programma applicativo e verrà osservato come un arresto anomalo.

9

Una cosa che le altre risposte non hanno ancora menzionato è che su alcune piattaforme, sizeof (int) != sizeof (int*). Se gli argomenti vengono passati in un certo modo *, scanf potrebbe trarre parte di un'altra variabile o dell'indirizzo di ritorno. La modifica dell'indirizzo di ritorno potrebbe portare a una vulnerabilità di sicurezza.

* Non sono un esperto di linguaggio assembly, quindi prendi questo con un pizzico di sale.

4

I could not understand why the program would crash? Could anyone explain me the reason. Any help appreciated.

Forse un po 'più applicato:

int i = 123; 
scanf ("%d", &i); 

Con il primo comando si alloca memoria per un valore intero e scrivere 123 in questo blocco di memoria. Per questo esempio, diciamo che questo blocco di memoria ha l'indirizzo 0x0000ffff. Con il secondo comando si legge l'input e scanf scrive l'input nel blocco di memoria 0x0000ffff - perché non si sta effettuando l'accesso (dereferenziazione) al valore di questa variabile i ma è l'indirizzo.

Se si utilizza il comando scanf ("%d", i); invece si sta scrivendo l'ingresso alla memoria indirizzo123 (perché questo è il valore memorizzato all'interno di questa variabile). Ovviamente può andare terribilmente storto e causare un crash.

+0

grazie. +1 per la buona spiegazione. (Anche un bel nome preso da The Martian_. –

Problemi correlati