errore di segmentazione strtok

Sto cercando di capire perché il seguente snippet di codice sta dando un errore di segmentazione:

void tokenize(char* line) { char* cmd = strtok(line," "); while (cmd != NULL) { printf ("%s\n",cmd); cmd = strtok(NULL, " "); } } int main(void) { tokenize("this is a test"); } 

So che strtok () in realtà non tokenize sui letterali stringa, ma in questo caso, la line punta direttamente alla stringa "this is a test" che è internamente un array di char . Esiste una line di tokenizzazione senza copiarla in un array?

Il problema è che stai tentando di modificare una stringa letterale. In questo modo il comportamento del tuo programma non è definito.

Dire che non sei autorizzato a modificare una stringa letterale è una semplificazione eccessiva. Dire che i valori letterali delle stringhe const sono errati; loro non sono.

ATTENZIONE: segue la depressione.

La stringa letterale "this is a test" è un’espressione di tipo char[15] (14 per la lunghezza, più 1 per la terminazione di '\0' ). Nella maggior parte dei contesti, incluso questo, tale espressione viene convertita implicitamente in un puntatore al primo elemento dell’array, di tipo char* .

Il comportamento di tentare di modificare l’array a cui fa riferimento una stringa letterale non è definito, non perché è const (non lo è), ma perché lo standard C dice specificatamente che non è definito.

Alcuni compilatori potrebbero permetterti di farla franca. Il tuo codice potrebbe effettivamente modificare la matrice statica corrispondente al letterale (che potrebbe causare una grande confusione in seguito).

La maggior parte dei compilatori moderni, tuttavia, memorizzerà la matrice in memoria di sola lettura, non nella ROM fisica, ma in una regione di memoria che è protetta dalle modifiche del sistema di memoria virtuale. Il risultato del tentativo di modificare tale memoria è in genere un errore di segmentazione e un arresto anomalo del programma.

Allora perché non sono costanti stringa letterali? Dato che non dovresti davvero provare a modificarli, sarebbe sicuramente logico – e il C ++ fa const string letterali. La ragione è storica. La parola chiave const non esisteva prima che fosse introdotta dallo standard ANSI C del 1989 (sebbene probabilmente fu implementata da alcuni compilatori prima). Quindi un programma pre-ANSI potrebbe assomigliare a questo:

 #include  print_string(s) char *s; { printf("%s\n", s); } main() { print_string("Hello, world"); } 

Non c’era modo di far rispettare il fatto che print_string non è autorizzato a modificare la stringa puntata da s . Realizzare string letterali const in ANSI C avrebbe rotto il codice esistente, cosa che il comitato ANSI C ha cercato molto duramente di evitare. Da allora non c’è stata una buona opportunità per apportare un tale cambiamento alla lingua. (I progettisti di C ++, principalmente Bjarne Stroustrup, non erano così preoccupati della retrocompatibilità con C.)

Come hai detto, non puoi modificare una stringa letterale, che è ciò che fa strtok . Devi fare

 char str[] = "this is a test"; tokenize(str); 

Questo crea l’array str e lo inizializza con this is a test\0 , e passa un puntatore ad esso per tokenize .

C’è una buona ragione per cui provare a tokenizzare una stringa costante in fase di compilazione causerà un errore di segmentazione: la stringa costante è in memoria di sola lettura.

Il compilatore C esegue le stringhe costanti in fase di compilazione nell’eseguibile e il sistema operativo le carica in memoria di sola lettura (.rodata in un file ELF * nix). Poiché questa memoria è contrassegnata come di sola lettura e poiché strtok scrive nella stringa passata, si ottiene un errore di segmentazione per la scrittura nella memoria di sola lettura.

Sono sicuro che verrai sconfitto da questo … ma “strtok ()” è intrinsecamente pericoloso e incline a cose come le violazioni di accesso.

Qui, la risposta è quasi certamente usando una costante di stringa.

Prova questo invece:

 void tokenize(char* line) { char* cmd = strtok(line," "); while (cmd != NULL) { printf ("%s\n",cmd); cmd = strtok(NULL, " "); } } int main(void) { char buff[80]; strcpy (buff, "this is a test"); tokenize(buff); } 

Strok modifica il suo primo argomento per renderlo token. Quindi non è ansible passare una stringa letterale, poiché è di tipo const char * e non può essere modificata, quindi il comportamento non definito. Devi copiare la stringa letterale in un array di caratteri che può essere modificato.

Che cosa stai cercando di fare con la tua osservazione “… è internamente una serie di char ?

Il fatto che "this is a test" è internamente una serie di char non cambia nulla. È ancora una stringa letterale (tutte le stringhe letterali sono matrici di caratteri non modificabili). Il tuo strtok tenta ancora di tokenizzare una stringa letterale. Questo è il motivo per cui si blocca.

Ho appena ricevuto l’errore Errore di segmentazione dal tentativo di usare printf per stampare il token ( cmd nel tuo caso) dopo che è diventato NULL.