2015-06-23 11 views
12

Questo programma C dà un risultato strano:non passando null stringa terminata a printf risultati in valore imprevisto

#include <stdio.h> 
#include <string.h> 

int main(int argc, char *argv[]) 
{ 
    char str1[5] = "abcde"; 
    char str2[5] = " haha"; 

    printf("%s\n", str1); 
    return 0; 
} 

quando si esegue questo codice ottengo:

abcde haha 

voglio solo per stampare il prima stringa come si può vedere dal codice.
Perché stampa entrambi?

+12

Poiché la lunghezza di 'str1' è stata limitata a 5, nessun terminatore di stringa è stato memorizzato come parte dell'array. Pertanto, è stato sovraccaricato sulla stampa nella stringa successiva. Questo è un comportamento indefinito e potrebbe non accadere sempre così. –

+5

Vedere [Nessun errore del compilatore quando il char array di dimensione fissa viene inizializzato senza spazio sufficiente per il terminatore null] (http://stackoverflow.com/q/20694796/1708801) –

risposta

34

"abcde" è in realtà 6 byte a causa del carattere di terminazione null nelle stringhe C. Quando si esegue questa operazione:

char str1[5] = "abcde"; 

Non si memorizza il carattere di terminazione null in modo che non sia una stringa corretta.

Quando si esegue questa operazione:

char str1[5] = "abcde"; 
char str2[5] = " haha"; 
printf("%s\n", str1); 

Succede solo per essere che la seconda stringa viene memorizzata subito dopo la prima, anche se questo non è richiesto.Chiamando printf su una stringa che non ha terminazioni nulle hai già causato un comportamento indefinito.

Aggiornamento:

Come indicato nei commenti da clcto questo può essere evitato non specificando esplicitamente la dimensione della matrice e lasciando che il compilatore determinarlo in base al largo della stringa:

char str1[] = "abcde"; 

o usare un puntatore, invece se funziona per il vostro caso d'uso, anche se non sono la stessa cosa: un comportamento indefinito

char *str1 = "abcde"; 
+0

da ... causato non definito _prima di _... volevi dire ... causato _behavior_ indefinito? – ryyker

+0

Sì, intendevo il comportamento, le dita pensavano più velocemente del cervello, la modifica di Sourav Ghosh è corretta e apprezzata. – missimer

+4

Suggerisci usando 'chr str1 [] =" abcde ";' per impedirlo? – clcto

19

Entrambe le stringhe str1 e str2 non terminano nulla. Pertanto la dichiarazione

invocherà un comportamento non definito.
printf stampa i caratteri in una stringa uno alla volta finché non incontra uno '\0' che non è presente nella stringa. In questo caso, printf continua oltre la fine della stringa finché non trova un carattere null da qualche parte nella memoria. Nel tuo caso sembra che printf oltre la fine della stringa "abcde" e continui a stampare i caratteri dalla seconda stringa " haha" che si trova casualmente subito dopo la prima stringa nella memoria.

meglio cambiare il blocco

char str1[5] = "abcde"; 
    char str2[5] = " haha"; 

a

char str1[] = "abcde"; 
char str2[] = " haha"; 

per evitare questo problema.

+2

.. o soffre di segfault di memoria inaccessibile. –

+0

@WeatherVane; Si. – haccks

+0

Può anche essere visto facendo un 'printf ("% d ", sizeof (str1));' del secondo blocco di stringhe ... '6' – xanatos

11

Tecnicamente, questo comportamento non è inaspettata, è indefinita: il codice sta passando un puntatore a una stringa C che manca terminatore null per printf, che è un comportamento indefinito.

Nel tuo caso, però, accade che il compilatore pone due stringhe back-to-back in memoria, in modo printf imbatte in null terminatore dopo la stampa str2, il che spiega il risultato che si ottiene.

Se si desidera stampare solo la prima stringa, aggiungere spazio per il terminatore null, in questo modo:

char str1[6] = "abcde"; 

Meglio ancora, lasciare che il compilatore calcolare la dimensione corretta per voi:

char str1[] = "abcde"; 
+0

str2 inoltre non è stato dato spazio per un carattere nullo, così è è solo una coincidenza che niente più viene stampato dopo str2? Potrebbe continuare a leggere la memoria ancora di più? – user985366

+0

È stato inaspettato da almeno una persona :) –

6

Hai invocato. Qui:

char str1[5] = "abcde"; 

str1 dispone di spazi per i suddetti cinque lettere, ma senza terminatore null. Quindi il modo in cui si tenta di passare la stringa non terminata null a printf per la stampa, richiama il comportamento non definito.

In generale nella maggior parte dei casi non è consigliabile passare stringhe non terminate da null a funzioni standard che prevedono stringhe (C).

+0

Sì, sembra migliore, +1. –

+0

@ FilipeGonçalves: Grazie –

-1

Lo specificatore %s cerca una terminazione nulla. Pertanto è necessario aggiungere '\0' alla fine della stringa

char str1[6] = "abcde\0"; 
+3

Questo non è corretto. Il compilatore aggiungerà il termine 'nul', a condizione che ci sia spazio per esso. Se dichiari 'char str1 [] =" abcde \ 0 ";' allora 'sizeof str1' sarà 7, non 6, poiché conterrà ** due **' nul's. –

+0

@WeatherVane Beh, non è corretto, a tutti !. Se si "desidera" fornire la dimensione dell'array nella dichiarazione, si * deve * aggiungere manualmente la terminazione nulla. Ora se lasci il compilatore per decidere la dimensione della stringa per te, allora il compilatore aggiungerà la terminazione nulla per te. Quindi si esegue 'char str1 [] =" abcde ";' o 'char str1 [6] =" abcde \ 0 ";'. In entrambi i casi 'sizeof (str1) = 6' – Manolete

+1

No, non * devi * aggiungere' '\ 0'' alla fine della stringa. 'char str1 [6] =" abcde ";' è sufficiente. Se c'è spazio per esso, il compilatore lo aggiungerà. In caso contrario, non è possibile aggiungerlo manualmente. –

4

In C tali dichiarazioni

char str1[5] = "abcde"; 

sono ammessi. In effetti ci sono 6 inizializzatori perché la stringa letterale include lo zero finale. Tuttavia nel lato sinistro viene dichiarata una matrice di caratteri che ha solo 5 elementi. Quindi non include lo zero finale.

Sembra

char str1[5] = { 'a', 'b', 'c', 'd', 'e', '\0' }; 

Se volete compilare questa dichiarazione in C++ il compilatore genera un errore.

Sarebbe preferibile dichiarare la matrice senza specificare esplicitamente la sua dimensione.

char str1[] = "abcde"; 

In questo caso avrebbe la dimensione uguale al numero di caratteri della stringa letterale tra cui la terminazione zero, che è pari a 6. E si può scrivere

printf("%s\n", str1); 

In caso contrario, la funzione continua per stampare caratteri oltre l'array fino a quando non incontra il carattere zero.

Tuttavia non è un errore. È sufficiente specificare l'identificatore di formato nella chiamata di printf:

printf("%5.5s\n", str1); 

e si otterrà il risultato previsto.

+0

Sarebbe molto più bella questa risposta con solo uno spazio aggiunto davanti ai primi 3 blocchi di codice! Un tale peccato devo trovare 6 caratteri da aggiungere per essere in grado di modificarlo :( – PeterSW

+0

@PeterSW OK, questo è per te. Felice? ;-) –

+1

@SouravGhosh Molto meglio :) – PeterSW

Problemi correlati