2009-09-23 8 views
6

Nel seguente codice, copio una stringa in un char * str, che è lungo 10 caratteri, usando strncpy().stringa non termina su NULL ma si comporta ancora normalmente, perché?

Ora in base al manuale strncpy(), "Avviso: se non esiste alcun byte null tra i primi n byte di src, la stringa inserita in dest non sarà terminata null." Che è esattamente ciò che accade qui.

La stringa di origine è lunga 26 caratteri e ho copiato 10 caratteri, quindi nessun carattere nullo viene inserito alla fine di str.

Ma quando stampo il contenuto di str, a partire da 0 fino a ottenere '\ 0', si comporta normalmente.

Perché? Quando non c'è nessun '\ 0' posto alla fine, allora perché il ciclo si ferma nel punto corretto?

Quello che ho capito è che dovrebbe dare "Errore di segmentazione" o almeno non dovrebbe fermarsi lì e continuare a stampare alcuni valori di spazzatura.

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 
    memset(str, 0, sizeof(char) * SIZE); 

    strncpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

Ecco l'output:

 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 

Qualsiasi aiuto sarà apprezzato.

EDIT

Esiste un modo corretto per controllare se una stringa termina a '\ 0' o no? Ho sempre pensato che il ciclo sopra descritto fosse il test finale, ma ora sembra che non lo sia.

Diciamo che otteniamo una stringa da una funzione sviluppata da un altro programmatore. Ora, come faremo a sapere che termina nella posizione corretta con '\ 0'. Può essere che non lo faccia, quindi andrà oltre la dimensione effettiva fino a quando non otterremo un po 'di' \ 0 '. Non possiamo mai sapere la dimensione effettiva della stringa.

Quindi, come possiamo affrontare questa situazione?

Qualche suggerimento?

+2

sizeof (char) è * sempre * 1 !! – paxdiablo

+1

@Pax: true, ma ecco una discussione su questo: http://stackoverflow.com/questions/1011806/is-it-necessario-per-multiply-by-sizeof-char-when-manipulating-memory – sharptooth

+0

strncmp prende il numero di caratteri, non la dimensione della stringa, quindi è sbagliato utilizzarlo lì. –

risposta

6

Per quanto riguarda la tua modifica, penso che essere pedante aiuterà a chiarire alcune questioni.

In C non esiste una stringa. Esiste un concetto di una "stringa C" che è ciò che funziona con la libreria standard C, che è definita come nient'altro che una sequenza di caratteri terminata NUL, quindi non esiste realmente una stringa "terminata non nulla" "in C. Quindi la tua domanda è meglio formulata come" Come posso determinare se un buffer di caratteri arbitrari è una stringa C valida? " o "Come posso determinare se la stringa che ho trovato è la stringa desiderata"

La risposta alla prima domanda, sfortunatamente, è quella di esaminare il buffer solo linearmente finché non si incontra un byte NUL come si sta facendo. Questo ti darà la lunghezza della stringa C.

La seconda domanda non ha una risposta facile.Dato che C non ha un tipo di stringa effettivo con metadati di lunghezza (o la capacità di portare attorno alle dimensioni degli array attraverso le chiamate di funzione), non c'è un modo reale per determinare se la lunghezza della stringa che abbiamo determinato sopra è la lunghezza di la stringa desiderata. Potrebbe essere ovvio se iniziamo a vedere segfaults nel programma o "garbage" nell'output, ma in generale siamo bloccati a eseguire operazioni con le stringhe eseguendo la scansione fino al primo byte NUL (di solito con un upperbound sulla lunghezza della stringa in modo da evitare confusione buffer overrun errors)

15

Succede solo che c'è un byte nullo proprio oltre la fine del blocco assegnato.

probabilità malloc() alloca più memoria e mette cosiddetti custodia valori che capita di contenere byte nulli o mette alcuni metadati per essere utilizzato da free() tardi e questi metadati succede a contenere un diritto byte nullo in quella posizione.

Ad ogni modo non si deve fare affidamento su questo comportamento. Devi richiedere (malloc()) un altro byte per il carattere null in modo che anche la posizione del carattere nullo sia assegnata legalmente a te.

Non esiste un modo portatile per verificare se una stringa è terminata in modo null. Può succedere che una volta superata la fine del blocco assegnato, il programma si bloccherà. Oppure può capitare che ci sia un carattere nullo oltre la fine del blocco e si sovrascriva la memoria oltre la fine del blocco in seguito quando si manipola la stringa mal interpretata.

Idealmente è necessaria una funzione che verificherebbe se un determinato indirizzo è stato assegnato a voi e appartiene alla stessa allocazione di un altro dato indirizzo (forse l'inizio del blocco). Questo sarebbe lento e non ne vale la pena e non c'è un modo standard per farlo.

In altre parole, se si incontra una stringa che deve essere terminata con il null, ma in realtà non si è fottuti in una volta - il tuo programma si imbatterà in un comportamento indefinito.

+0

No, non c'è. –

+0

sì, capita di essere un byte null alla fine della stringa. Se provi di dimensioni diverse, otterrai * output errato. –

+0

Quindi non esiste un modo standard per verificare se una stringa è nullo o no. Questa è una brutta notizia. Penso che tutti i programmatori che lavorano sull'applicazione debbano concordare su alcuni standard. Come i primi tre caratteri di un puntatore ne indicheranno le dimensioni e dal quarto inizierà la stringa vera e propria. –

4

Perché funziona?

La memoria allocata ha un byte '\0' nel posto giusto. (Ad esempio, se si utilizza Visual C++ in modalità Debug, gli zeri del gestore heap allocano la memoria prima di consegnarla al programma, ma potrebbe anche essere pura fortuna.)

C'è un modo corretto per verificare se una stringa termina a '\0' o no?

No. È necessario che le stringhe siano terminate da zero (che è quello che si aspettano le funzioni di gestione della stringa lib di Cdd) oppure è necessario portarle dietro in una variabile extra. Se non hai nessuno dei due, hai un bug.

Ora Come sapremo che qualche stringa da qualche funzione sviluppata da qualche altro programmatore termina al posto giusto con '\0'. Potrebbe essere che non lo fa, quindi andrà oltre la dimensione effettiva fino a quando non otteniamo alcuni '\0'. Non possiamo mai sapere la dimensione effettiva della stringa.

Quindi, come possiamo affrontare questa situazione?

Non è possibile. Se l'altra funzione la mette così male, sei fregato così tanto.

+0

Informazioni sulla memoria di azzeramento del gestore di heap: il compilatore Microsoft non azzera la memoria (n build di debug o release). Quando si utilizza l'heap di debug, il runtime MSVC riempirà la memoria allocata con byte 0xCD, non zero. Riempire con la 'spazzatura' piuttosto che cancellare la memoria è solitamente più efficace nel trovare i problemi. Inoltre, parte della memoria prima e dopo la lega verrà riempita con valori 0xFD. Vedi http://stackoverflow.com/questions/370195/when-and-why-will-an-os-initialise-memory-to-0xcd-0xdd-etc-on-malloc-free-new/370362#370362 –

+0

@ Micheal: Per quanto ne so, potresti avere ragione. Tuttavia, ISTR avendo letto ancora e ancora che le variabili non vengono azzerate è una tipica causa per le versioni di rilascio che si bloccano mentre le versioni di debug funzionano in VC. '' – sbi

0

Sharptooth ha spiegato la causa probabile del comportamento, quindi non lo ripeterò.

Nell'assegnazione buffer, ho sempre over-allocare da un byte, come questo:

#define SIZE 10 
char* buf = malloc(sizeof(char)*(SIZE+1)); 
/* error-check the malloc call here */ 
buf[SIZE] = '\0'; 
+0

Eh, "sizeof (char) - (SIZE + 1)"? Meno? –

+0

possiamo anche fare questo meme (dest, 0, SIZE); strncpy (dest, source, SIZE -1); In questo modo l'ultimo byte avrà uno zero. –

+0

Questo dovrebbe essere * - volte. Nuova tastiera :) – gnud

0

Sei fortunato ad avere zero oltre la regione di spazio assegnata.

Prova questo codice su tutte le altre piattaforme e vedrai che potrebbe non funzionare allo stesso modo.

0

Penso che la risposta di Sharptooth sia giusta. C'è più spazio allocato. Modifico il programma come segue:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    int *p; 
    int actual_length; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 

    actual_length = (int)*(str - 4) - 1 - 4; 
    printf("actual length of str is %d\n", actual_length); 
    p = (int*) malloc(sizeof(int)); 
    if (p == NULL) exit(1); 
    *p = -1; 
    char* pc = (char*)(p - 1); 
    pc [0] = 'z'; 
    pc [1] = 'z'; 
    pc [2] = 'z'; 
    pc [3] = 'z'; 

    memset(str, 0, sizeof(char) * SIZE); 

    memcpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    int i; 
    for (i = SIZE; i < actual_length; i++) 
    str[i] = 'y'; 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

L'uscita è

actual length of str is 12 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 
str[ 10 ] has got : y 
str[ 11 ] has got : y 
str[ 12 ] has got : z 
str[ 13 ] has got : z 
str[ 14 ] has got : z 
str[ 15 ] has got : z 
str[ 16 ] has got : \377 
str[ 17 ] has got : \377 
str[ 18 ] has got : \377 
str[ 19 ] has got : \377 

Il mio sistema operativo è Debian Squeeze/sid.

Problemi correlati