Perché questo codice C di inversione della stringa provoca un errore di segmentazione? [duplicare]
-
06-07-2019 - |
Domanda
Questa domanda ha già una risposta qui:
Sto provando a scrivere il codice per invertire una stringa (sto solo cercando di migliorare la programmazione in C e la manipolazione del puntatore), ma non riesco a capire perché sto ricevendo un errore di segmentazione :
#include <string.h>
void reverse(char *s);
int main() {
char* s = "teststring";
reverse(s);
return 0;
}
void reverse(char *s) {
int i, j;
char temp;
for (i=0,j = (strlen(s)-1); i < j; i++, j--) {
temp = *(s+i); //line 1
*(s+i) = *(s+j); //line 2
*(s+j) = temp; //line 3
}
}
Sono le linee 2 e 3 a causare l'errore di segmentazione. Comprendo che potrebbero esserci modi migliori per farlo, ma sono interessato a scoprire che cosa specificamente nel mio codice sta causando l'errore di segmentazione.
Aggiorna : ho incluso la funzione di chiamata come richiesto.
Soluzione
Non c'è modo di dire solo da quel codice. Molto probabilmente, stai passando un puntatore che punta a memoria non valida, memoria non modificabile o qualche altro tipo di memoria che non può essere elaborata nel modo in cui la elabori qui.
Come si chiama la funzione?
Aggiunto: stai passando un puntatore a una stringa letterale. I letterali di stringa non sono modificabili. Non puoi invertire una stringa letterale.
Passa invece un puntatore a una stringa modificabile
char s[] = "teststring";
reverse(s);
Questo è già stato spiegato fino alla morte qui. " teststring "
è una stringa letterale. Lo stesso letterale stringa è un oggetto non modificabile. In pratica i compilatori potrebbero (e lo faranno) metterlo in memoria di sola lettura. Quando si inizializza un puntatore del genere
char *s = "teststring";
il puntatore punta direttamente all'inizio della stringa letterale. Qualsiasi tentativo di modificare ciò a cui punta s
viene considerato fallito nel caso generale. Puoi leggerlo, ma non puoi scriverlo. Per questo motivo si consiglia vivamente di puntare a valori letterali stringa solo con variabili da puntatore a const
const char *s = "teststring";
Ma quando dichiari il tuo s
come
char s[] = "teststring";
ottieni un array s
completamente indipendente situato nella normale memoria modificabile, che è solo inizializzata con stringa letterale. Ciò significa che l'array modificabile indipendente s
otterrà il suo valore iniziale copiato dalla stringa letterale. Successivamente, l'array s
e la stringa letterale continuano a esistere come oggetti completamente indipendenti. Il valore letterale non è ancora modificabile, mentre l'array s
è modificabile.
Fondamentalmente, quest'ultima dichiarazione è funzionalmente equivalente a
char s[11];
strcpy(s, "teststring");
Altri suggerimenti
Il tuo codice potrebbe essere segfaulting per una serie di motivi. Ecco quelli che mi vengono in mente
- s è NULL
- s indica una stringa const che viene mantenuta nella memoria di sola lettura
- s non è NULL terminato
Penso che il numero 2 sia il più probabile. Puoi mostrarci il sito di chiamata di reverse?
MODIFICA
Sulla base del tuo esempio n. 2 è sicuramente la risposta. Una stringa letterale in C / C ++ non è modificabile. Il tipo corretto è in realtà const char *
e non char *
. Quello che devi fare è passare una stringa modificabile in quel buffer.
Esempio rapido:
char* pStr = strdup("foobar");
reverse(pStr);
free(pStr);
Stai testando qualcosa del genere?
int main() {
char * str = "foobar";
reverse(str);
printf("%s\n", str);
}
Questo rende letteralmente str una stringa e probabilmente non sarai in grado di modificarla (segfaults per me). Se definisci char * str = strdup (foobar)
dovrebbe funzionare bene (funziona per me)
La tua dichiarazione è completamente sbagliata:
char* s = "teststring";
" testString " è memorizzato nel segmento di codice, che è di sola lettura, come il codice. E, s è un puntatore a "teststring", allo stesso tempo, stai provando a cambiare il valore di un intervallo di memoria di sola lettura. Pertanto, errore di segmentazione.
Ma con:
char s[] = "teststring";
s viene inizializzato con "teststring", che ovviamente si trova nel segmento di codice, ma in questo caso è in corso un'operazione di copia aggiuntiva nello stack.
Quale compilatore e debugger stai usando? Usando gcc e gdb, vorrei compilare il codice con -g flag e quindi eseguirlo in gdb. Quando segfault, farei semplicemente una backtrace (comando bt in gdb) e vedrei quale è la linea offensiva che causa il problema. Inoltre, eseguivo il codice passo dopo passo, mentre " guardando " i valori del puntatore in gdb e sanno dov'è esattamente il problema.
Buona fortuna.
Come alcune delle risposte fornite sopra, la memoria delle stringhe è di sola lettura. Tuttavia, alcuni compilatori forniscono un'opzione per compilare con stringhe scrivibili. Per esempio. con gcc
, le versioni 3.x supportate -fwritable-stringhe
ma le versioni più recenti no.
Vedi Domanda 1.32 nell'elenco FAQ C:
Qual è la differenza tra queste inizializzazioni?
char a[] = "string literal"; char *p = "string literal";
Il mio programma si arresta in modo anomalo se provo ad assegnare un nuovo valore a
p [i]
.Risposta:
Un letterale stringa (il termine formale per una stringa tra virgolette doppie in sorgente C) può essere utilizzato in due modi leggermente diversi:
Come inizializzatore per un array di caratteri, come nella dichiarazione di
char a []
, specifica i valori iniziali dei caratteri in quell'array (e, se necessario, le sue dimensioni).Ovunque altro, si trasforma in una matrice di caratteri statica senza nome e questa matrice senza nome può essere memorizzata in memoria di sola lettura e che pertanto non può necessariamente essere modificata . In un contesto di espressione, l'array viene convertito immediatamente in un puntatore, come al solito (vedere la sezione 6), quindi la seconda dichiarazione inizializza
p
per puntare al primo elemento dell'array senza nome.Alcuni compilatori hanno un interruttore che controlla se i letterali di stringa sono scrivibili o meno (per compilare il vecchio codice), e alcuni possono avere opzioni per far sì che i letterali di stringa vengano trattati formalmente come array di
const char
(per migliore rilevazione degli errori).( sottolineatura mia )
Vedi anche Torna alle nozioni di base di Joel .
Penso che strlen
non può funziona poiché s non è terminato NULL. Quindi il comportamento del tuo per iterazione non è quello che ti aspetti.
Poiché il risultato di strlen sarà superiore alla lunghezza, scriverai in memoria dove non dovresti essere.
Inoltre s punta a stringhe costanti trattenute da una memoria di sola lettura. Non puoi modificarlo. Prova a inizializzare usando la funzione gets come avviene nella strlen esempio