web-dev-qa-db-fra.com

Comment attribuer correctement une nouvelle valeur de chaîne?

J'essaie de comprendre comment résoudre ce problème trivial en C, de la manière la plus propre et la plus sûre. Voici mon exemple:

#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;
}

L'erreur du compilateur est:

main.c: dans la fonction ‘main’: main.c: 18: erreur: types incompatibles lors de l’affectation au type ‘char [20]’ à partir du type ‘char *’

Je comprends que C (pas C++) n’a pas de type String et utilise à la place des tableaux de caractères, alors une autre façon de faire était de modifier la structure de l’exemple pour contenir les pointeurs de caractères:

#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;
}

Cela fonctionne comme prévu, mais je me demande s’il existe un meilleur moyen de le faire. Merci.

44
Gianluca Bargelli

Le premier exemple ne fonctionne pas car vous ne pouvez pas attribuer de valeurs à des tableaux - les tableaux fonctionnent (en quelque sorte) comme les pointeurs const à cet égard. Ce que vous pouvez cependant faire, c'est copier une nouvelle valeur dans le tableau:

strcpy(p.name, "Jane");

Les tableaux de caractères peuvent être utilisés si vous connaissez la taille maximale de la chaîne à l'avance, par exemple. Dans le premier exemple, vous êtes sûr à 100% que le nom tiendra dans 19 caractères (et non 20, car il faut toujours un caractère pour stocker la valeur zéro finale).

Inversement, les pointeurs sont meilleurs si vous ne connaissez pas la taille maximale possible de votre chaîne et/ou si vous souhaitez optimiser votre utilisation de la mémoire, par exemple. évitez de réserver 512 caractères pour le nom "John". Cependant, avec les pointeurs, vous devez allouer de manière dynamique le tampon auquel ils pointent et le libérer quand vous n'en avez plus besoin pour éviter les fuites de mémoire.

pdate: exemple de tampons alloués dynamiquement (en utilisant la définition de structure de votre deuxième exemple):

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);
40
Péter Török

Considérez les chaînes comme des objets abstraits et les tableaux de caractères comme des conteneurs. La chaîne peut avoir n'importe quelle taille, mais le conteneur doit avoir au moins 1 fois la longueur de la chaîne (pour contenir le terminateur nul).

C a très peu de support syntaxique pour les chaînes. Il n'y a pas d'opérateur de chaîne (uniquement les opérateurs char-array et char-pointer). Vous ne pouvez pas assigner de chaînes.

Mais vous pouvez appeler des fonctions pour vous aider à réaliser ce que vous voulez.

La fonction strncpy() pourrait être utilisée ici. Pour une sécurité maximale, je suggère de suivre ce modèle:

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

Regardez aussi les fonctions strncat() et memcpy().

9
Artelius

Les deux structures sont différentes. Lorsque vous initialisez la première structure, environ 40 octets de mémoire sont alloués. Lorsque vous initialisez la seconde structure, environ 10 octets de mémoire sont alloués. (Le montant réel dépend de l'architecture)

Vous pouvez utiliser les littéraux de chaîne (constantes de chaîne) pour initialiser les tableaux de caractères. C'est pourquoi

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

fonctionne dans le premier exemple.

Vous ne pouvez pas affecter (au sens conventionnel) une chaîne en C.

Les littéraux de chaîne que vous avez ("John") sont chargés en mémoire lorsque votre code est exécuté. Lorsque vous initialisez un tableau avec l'un de ces littéraux, la chaîne est copiée dans un nouvel emplacement de mémoire. Dans votre deuxième exemple, vous copiez simplement le pointeur sur (l'emplacement de) le littéral de chaîne. Faire quelque chose comme:

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

cela pourrait provoquer des erreurs de compilation ou d’exécution (je ne suis pas sûr.) C’est une mauvaise idée car vous modifiez la chaîne littérale "Hello" qui, par exemple sur un microcontrôleur, pourrait être située dans une mémoire en lecture seule.

5
Gus