2009-06-03 10 views
5

Con le stringhe in stile c, come si assegna un carattere a un indirizzo di memoria a cui punta un puntatore di carattere? Ad esempio, nell'esempio seguente, voglio cambiare num in "123456", quindi ho provato a impostare p sulla cifra in cui si trova '0' e provo a sovrascriverlo con '4'. Grazie.Perché questo codice per modificare una stringa non funziona?

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    char* num = (char*)malloc(100); 
    char* p = num; 

    num = "123056"; 

    p = p+3; //set pointer to where '4' should be 
    p = '4'; 

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

    return 0; 
} 
+0

Eventuali duplicati di [Perché ricevo un errore di segmentazione quando si scrive in una stringa inizializzata con "char \ * s" non ma "char s \ [\]"? ] (http://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha) – jww

risposta

12

Che il codice non funziona, semplicemente perché la linea:

num = "123056"; 

cambia num a punto dalla memoria allocata (e p rimane indicando la memoria in modo che non sono più nella stessa posizione) a ciò che è più probabile memoria di sola lettura. Non sei autorizzato a cambiare la memoria appartenente a stringhe letterali, è un comportamento indefinito.

È necessario il seguente:

#include <stdio.h> 
#include <stdlib.h> 
int main (void) { 
    char *num = malloc (100); // do not cast malloc return value. 
    char *p = num; 

    strcpy (num, "123056"); // populate existing block with string. 

    p = p + 3;    // set pointer to where '0' is. 
    *p = '4';     // and change it to '4'. 

    printf ("%s\n", num); // output it. 

    return 0; 
} 
+0

Inoltre, è necessario liberare la memoria. –

+2

Questa è una buona forma, @Igor, ma non è necessaria dato che il programma sta uscendo immediatamente. E l'idea di un frammento è quella di trasmettere informazioni rilevanti, non tutte le informazioni, altrimenti, dobbiamo controllare che anche il malloc() abbia funzionato. – paxdiablo

0

Basta usare *p = '4'; ...!

+0

Dereferenziamento non lavoro. La stringa "num" è immutabile o qualcosa del genere? Sto compilando con cc su linux. –

+0

Questo è sbagliato; dovrebbe essere '4', non 4. –

+0

Sì, la stringa letterale è probabilmente allocata in un segmento di memoria contrassegnato come di sola lettura e qualsiasi tentativo di scrittura in esso causa un errore di runtime. – sharptooth

13

Prima di tutto, quando si esegue:

num = "123056"; 

Non sta copiando la stringa "123056" per l'area di heap allocata da malloc(). In C, l'assegnazione di un puntatore char * un valore letterale di stringa equivale a impostare come una costante - cioè identico a:

char str[] = "123056"; 

Allora, che cosa hai appena compiuto c'è che hai abbandonato il tuo unico riferimento alla Area heap da 100 byte allocata da malloc(), motivo per cui il codice successivo non stampa il valore corretto; 'p' punta ancora all'area di heap allocata per malloc() (dal momento che l'num l'ha indicata al momento dell'assegnazione), ma non lo è più num.

Presumo che in realtà si intendesse fare era copia la stringa "123056" in quell'area di heap. Ecco come fare:

strcpy(num, "123056"); 

Anche se, questo è meglio pratica per una serie di motivi:

strncpy(num, "123056", 100 - 1); /* leave room for \0 (null) terminator */ 

Se avessi appena fatto:

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

int  main(void) { 
     char *num = malloc(100); 
     char *p = num; 

     strncpy(num, "123056", 100 - 1); 

     p = p + 3; 
     *p = '4'; 

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

     return 0; 
} 

Si sarebbe ottenuto il risultato corretto:

123456 

È possibile contrarre questa operazione:

p = p + 3; 
*p = '4'; 

... e di evitare l'iterazione il puntatore, da deferencing come segue:

*(p + 3) = '4'; 

Poche altre note:

  • Sebbene stilistica comune pratica, la trasmissione del valore di ritorno di malloc() a (char *) non è necessaria. La conversione e l'allineamento del tipo void * sono garantiti dal linguaggio C.

  • Controllare SEMPRE il valore di ritorno di malloc(). Sarà NULL se l'allocazione dell'heap non è riuscita (ad esempio, hai esaurito la memoria) ea quel punto il tuo programma dovrebbe uscire.

  • A seconda dell'implementazione, l'area della memoria allocata da malloc() può contenere rifiuti obsoleti in determinate situazioni.E 'sempre una buona idea a zero fuori dopo l'allocazione:

    memset(num, 0, 100); 
    
  • non dimenticare mai di free() tuo mucchio! In questo caso, il programma uscirà e il sistema operativo ripulirà la tua spazzatura, ma se non prendi l'abitudine, avrai perdite di memoria in pochissimo tempo.

Quindi, ecco la versione "best practice":

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

int  main(void) { 
     char *num, *p; 

     /* 
     * Don't take 1-byte chars for granted - good habit to get into. 
     */ 

     num = malloc(sizeof(char) * 100); 

     if(num == NULL) 
       exit(1); 

     memset(num, 0, sizeof(char) * 100); 

     p = num; 

     strncpy(num, "123056", 100 - 1); 

     *(p + 3) = '4'; 

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

     free(num); 

     return 0; 
} 
+1

Grazie per gli ottimi consigli sulla gestione della memoria. Questi suggerimenti sono utili per le persone che provengono da linguaggi di raccolta automatica dei rifiuti! –

+0

Sicuro! Felice di aiutare! –

+0

Post eccellente! Molte informazioni preziose. Grazie. –

0

Beh, sai p è di tipo puntatore. Memorizza l'indirizzo di char '0'. Se assegni a p il valore di '4'. Ci vorra '4' come indirizzo. Tuttavia, l'indirizzo "4" è illegale. È possibile utilizzare l'operatore '*' per ottenere il valore dell'indirizzo che è memorizzato in p memorizzato.

+0

Un nitpick, l'indirizzo '4' non è illegale. Le implementazioni possono abbastanza facilmente utilizzare la memoria a 0x00000034 (è persino allineata correttamente per una parola a 32 bit). – paxdiablo

+0

Bene, ho detto illegale per questa applicazione. È un indirizzo di memoria sconosciuto. – Sefler

1

Oltre al problema * p che altri hanno evidenziato, si verificano anche problemi di utilizzo della memoria. Hai un buffer di 100 byte, con contenuti sconosciuti. Hai un altro buffer di 7 byte, contenente la stringa "123056" e un terminatore nullo. Lo stai facendo:

  1. num è impostato per puntare al buffer di 100 byte
  2. p è impostato per puntare a num; cioè, punta al buffer da 100 byte
  3. ripristinando num per puntare al buffer da 7 byte; p punta ancora nel buffer di 100 byte
  4. Si utilizza p per modificare il buffer di 100 byte
  5. quindi si utilizza num stampare il buffer di 7 byte

Quindi non sta stampando lo stesso buffer che stai modificando.

+0

Grazie, Bruce, la tua spiegazione è stata d'aiuto, ma la stroncata di Pax lo ha chiarito davvero. –