2010-06-28 10 views
37

Sto cercando di capire come risolvere questo banale problema in C, nel modo più pulito/più sicuro. Ecco il mio esempio:Come assegnare correttamente un nuovo valore di stringa?

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    typedef struct 
    { 
     char name[20]; 
     char surname[20]; 
     int unsigned age; 
    } person; 

    //Here i can pass strings as values...how does it works? 
    person p = {"John", "Doe",30}; 

    printf("Name: %s; Age: %d\n",p.name,p.age); 
    // This works as expected... 
    p.age = 25; 
    //...but the same approach doesn't work with a string 
    p.name = "Jane"; 

    printf("Name: %s; Age: %d\n",p.name,p.age); 

    return 1; 
} 

errore del compilatore è:

main.c: In function ‘main’: main.c:18: error: incompatible types when assigning to type ‘char[20]’ from type ‘char *’

capisco che C (non C++) non ha alcun tipo String e utilizza invece array di caratteri, quindi un altro modo per fare questo è stato quello di alterare l'esempio struct per contenere i puntatori di caratteri:

#include <stdio.h> 

int main(int argc, char *argv[]) 
{ 
    typedef struct 
    { 
     char *name; 
     char *surname; 
     int unsigned age; 
    } person; 

    person p = {"John", "Doe",30}; 

    printf("Name: %s; Age: %d\n",p.name,p.age); 

    p.age = 25; 

    p.name = "Jane"; 

    printf("Name: %s; Age: %d\n",p.name,p.age); 

    return 1; 
} 

questo funziona come previsto, ma mi chiedo se c'è un modo migliore per fare questo. Grazie.

+0

Se dichiariamo struct in 'main()', solo la persona può accedere al suo interno. Prova ad uscire da main per usarlo come globale – EsmaeelE

risposta

29

Il primo esempio non funziona perché non è possibile assegnare valori agli array: gli array funzionano (ordinariamente) come puntatori const in questo senso. Cosa si può fare se è copia di un nuovo valore nella matrice:

strcpy(p.name, "Jane"); 

array Char vanno bene da usare se si conosce la dimensione massima della stringa in anticipo, per esempio nel primo esempio sei sicuro al 100% che il nome si adatti a 19 caratteri (non 20 perché un carattere è sempre necessario per memorizzare il valore zero terminante).

Al contrario, i puntatori sono migliori se non si conosce la dimensione massima possibile della stringa e/o se si desidera ottimizzare l'utilizzo della memoria, ad es. evitare di riservare 512 caratteri per il nome "John". Tuttavia, con i puntatori è necessario allocare dinamicamente il buffer a cui puntano e liberarlo quando non è più necessario, per evitare perdite di memoria.

Aggiornamento: esempio di buffer allocati dinamicamente (utilizzando la definizione struct nel 2 ° esempio):

char* firstName = "Johnnie"; 
char* surname = "B. Goode"; 
person p; 

p.name = malloc(strlen(firstName) + 1); 
p.surname = malloc(strlen(surname) + 1); 

p.age = 25; 
strcpy(p.name, firstName); 
strcpy(p.surname, surname); 

printf("Name: %s; Age: %d\n",p.name,p.age); 

free(p.surname); 
free(p.name); 
7

Pensate a stringhe come oggetti astratti, e gli array char come contenitori. La stringa può avere qualsiasi dimensione ma il contenitore deve essere almeno 1 in più della lunghezza della stringa (per contenere il terminatore null).

C ha un supporto sintattico molto piccolo per le stringhe. Non ci sono operatori di stringa (solo operatori char-array e char-pointer). Non è possibile assegnare stringhe.

Ma è possibile chiamare le funzioni per ottenere ciò che si desidera.

La funzione strncpy() può essere utilizzata qui. Per la massima sicurezza Suggerisco seguendo questo schema:

strncpy(p.name, "Jane", 19); 
p.name[19] = '\0'; //add null terminator just in case 

hanno anche uno sguardo ai strncat() e memcpy() funzioni.

+1

Per ora la migliore risposta, ma anche quella di Péter è buona (mostrando come farlo con i puntatori) quindi aspetto ancora un po 'per vedere se più persone possono aggiungere altri suggerimenti/suggerimenti sull'argomento. –

4

Le due strutture sono diverse. Quando si inizializza la prima struttura, vengono allocati circa 40 byte di memoria. Quando si inizializza la seconda struttura, vengono allocati circa 10 byte di memoria. (La quantità effettiva dipende dall'architettura)

È possibile utilizzare i valori letterali stringa (costanti stringa) per inizzare gli array di caratteri. Questo è il motivo per cui

person p = {"John", "Doe",30};

funziona nel primo esempio.

Non è possibile assegnare (in senso convenzionale) una stringa in C.

Le stringhe letterali che avete ("John") vengono caricati in memoria quando il codice viene eseguito. Quando si inizializza una matrice con uno di questi valori letterali, la stringa viene copiata in una nuova posizione di memoria. Nel tuo secondo esempio, stai semplicemente copiando il puntatore a (posizione di) letterale stringa. Fare qualcosa di simile:

char* string = "Hello"; 
*string = 'C' 

potrebbe causare la compilazione o errori di runtime (non sono sicuro). Si tratta di una cattiva idea, perché si sta modificando la stringa letterale "Ciao" che, ad esempio su un microcontroler, potrebbe essere ubicato nella memoria di sola lettura.

+0

Sei corretto, il compito che hai scritto causa un segfault perché stai provando a modificare un valore del puntatore, ma riferendomi al mio esempio le cose vanno come char * string = "Hello"; string = "C"; (nota che non c'è assegnamento del puntatore sull'ultima istruzione) che funziona come previsto. –

+1

Vorrei ricordare che "modificare un valore del puntatore" non causa necessariamente un segfault. La ragione per cui lo snippet nel mio post originale causa un segfault è perché stai cercando di modificare la memoria che si trova in uno spazio indirizzo limitato. – Gus