Perché questo codice C di inversione della stringa provoca un errore di segmentazione? [duplicare]

StackOverflow https://stackoverflow.com/questions/1614723

  •  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.

È stato utile?

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

  1. s è NULL
  2. s indica una stringa const che viene mantenuta nella memoria di sola lettura
  3. 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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top