Le type char - un vrai casse tête

Résolu/Fermé
hymenoptera Messages postés 36 Date d'inscription mercredi 24 août 2016 Statut Membre Dernière intervention 18 décembre 2018 - Modifié le 18 déc. 2018 à 10:20
hymenoptera Messages postés 36 Date d'inscription mercredi 24 août 2016 Statut Membre Dernière intervention 18 décembre 2018 - 18 déc. 2018 à 15:42
Bonjour,

Franchement j'ai vraiment besoin d'aide pour les types char en C, je trouve que c'est horrible à gérer et qu'on a des erreurs très facilement. Je cherche sur le net mais je ne comprends toujours pas les bases de ce type qui rend fou !

Pour commencer j'ai le code suivant :

char phrase[1000];
strcpy(phrase, "bonjour je suis la");
printf("%s \n", phrase);


Ca compile sans aucun soucis, ça s’exécute parfaitement... Maintenant je choisis une taille dynamique :

char * phrase;
strncpy(phrase, "bonjour je suis la", 1000);
printf("%s \n", phrase);


en compilant j'ai un seg fault, je change alors le print par :

printf("%s \n", &phrase);


j'obtiens un warning :
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char **’ [-Wformat=]
printf("%s \n", &phrase);


et quand j’exécute ça m'affiche n'importe quoi :'(
En fait le warning je l'ai compris, le souci c'est que je ne sais pas comment afficher simplement...

----------------------------------------------------------------------------

J'ai une autre question, cette fois ci ça concerne les char dans les structures. J'ai le code suivant :

 typedef struct test
        {
        char phrase1 [100];
        char * phrase2;
        } test;

        test chaine=
        {
                .phrase1="bonjour !",
                .phrase2="au revoir",
        };

        printf("%s \n %s\n", chaine.phrase1, chaine.phrase2);


Ca compile et ça s’exécute sans aucun souci et je n'ai aucune erreur. Je vous avoue que je comprends rien. Y a une base que je pige pas

Alors que si je déclare comme ça :

test chaine;
     chaine.phrase1="bonjour !",
     chaine.phrase2="au revoir",


J'ai une erreur

error: assignment to expression with array type
chaine.phrase1="bonjour !",


Maintenant je change mon code en :

test chaine;
                strcpy(chaine.phrase1,"bonjour !");
                chaine.phrase2="au revoir";

        printf("%s \n %s\n", chaine.phrase1, chaine.phrase2);


Et crotte ça marche... Je comprends encore moins...

J'en déduis que si je fais :

char * phrase;
phrase="bonjour je suis la";
printf("%s \n", phrase);


Et oui ça marche.

En fait mon problème c'est que j'ai cherché des solutions en faisant des tests mais à chaque fois je suis surprise par le résultat, je ne comprends pas vraiment ce qui se passe. Si je fais un "print phrase" et ça plante, je le remplace par "print &phrase" ça marche mais je ne comprends pas pourquoi.

Je ne sais pas c'est quoi la meilleur façon de faire pour que tout fonctionne normalement. J'ai peur qu'en trouvant des solutions comme ça en "trifouillant" je risque d'avoir des soucis avec mon code dans le futur.

Par exemple je ne vois pas pourquoi ce code plante :

char * phrase;
strncpy(phrase, "bonjour je suis la", 1000);
printf("%s \n", phrase);


alors que celui là marche parfaitement :

char * phrase;
phrase="bonjour je suis la";
printf("%s \n", phrase);



Je m'excuse pour le long poste et encore je n'ai pas écrit les autres cas qui m'énervait, je vous remercie de me lire et de m'apporter des clarifications.
Très bonne journée à tous :)
A voir également:

2 réponses

NHenry Messages postés 15113 Date d'inscription vendredi 14 mars 2003 Statut Modérateur Dernière intervention 22 avril 2024 331
18 déc. 2018 à 09:35
Quand tu fais :
char * phrase;
strncpy(phrase, "bonjour je suis la", 1000); 


Tu déclare un pointeur, mais tu ne l'initalise pas, du coup, ça mets tes données dans un endroit non prévu.
Il faut l'assigner soit avec une chaine de longueur cohérente à ton usage ou avec une de ces lignes :
char * phrase="Une chaine d'initialisation";
char  phrase[20];
char * phrase=malloc(...)

1
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 18 déc. 2018 à 11:35
Salut hymenoptera,

char
est un type entier, qui sert à représenter un seul caractère.

Une chaîne de caractères nécessite une zone mémoire où plusieurs char peuvent être stockés, y compris le caractère
'\0'
qui signale la fin de la chaîne.

1.

Dans la première partie de ton post, tu nous dis :

Pour commencer j'ai le code suivant :
char phrase[1000];
strcpy(phrase, "bonjour je suis la");
printf("%s \n", phrase);

Ca compile sans aucun soucis, ça s’exécute parfaitement...

En ligne 1, tu as définit
phrase
comme étant un tableau de 1000
char
. Tu as donc réservé un espace mémoire de 1000
char
et tu n'as aucun problème pour y stocker ta chaîne et son affichage s'exécute comme il se doit le
phrase
dans le
printf
passant à cette fonction le pointeur vers le premier élément du tableau.

Maintenant je choisis une taille dynamique :
char * phrase;
strncpy(phrase, "bonjour je suis la", 1000);
printf("%s \n", phrase);

en compilant j'ai un seg fault


Le C ne va pas automatiquement allouer la mémoire nécessaire sur un pointeur, c'est à toi de la gérer.
En ligne 1 tu as défini
phrase
comme étant un pointeur sur
char
. Un pointeur est une variable qui sert à stocker une adresse mémoire. Un pointeur sur
char
n'est donc pas, à lui seul, un type magique qui te permet de stocker quoi que ce soit d'autre ... qu'une seule adresse mémoire. A ce stade là, tu n'as donc rien réservé dynamiquement. De plus, ton pointeur (le contenu de la variable
phrase
), n'est pas initialisé, et contient n'importe quoi (une adresse mémoire arbitraire constituée de ce qu'il y a en mémoire à cet endroit). Donc lorsqu'en ligne 2 du tentes de copier ton texte avec un pointeur pointant n'importe où, il en résulte une erreur de segmentation à l'exécution.

Pour utiliser le pointeur, tu dois l'initialiser en y mettant une adresse d'une mémoire allouée au programme. Pour réserver une quantité de mémoire déterminée à l'exécution, tu réserves de la mémoire sur le tas avec
malloc
et tu libères la mémoire avec
free
.

char * phrase = NULL;
phrase = malloc(1000);
if (phrase != NULL) {
    strncpy(phrase, "bonjour je suis la", 1000);
    printf("%s \n", phrase);
}
free(phrase);


Ensuite, tu dis :

je change alors le print par :
printf("%s \n", &phrase);

j'obtiens un warning :
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char **’ [-Wformat=]
printf("%s \n", &phrase);
et quand j’exécute ça m'affiche n'importe quoi :'(
En fait le warning je l'ai compris, le souci c'est que je ne sais pas comment afficher simplement...

printf
avec
%s
nécessite que tu passes un pointeur sur
char
, or, là tu passes un pointeur sur un pointeur sur
char
. Le compilateur t'avertit, mais tu ne vas rien afficher de cohérent, et ton programme plantera de toutes façon à la ligne précédente.

2.

Ce que tu constates avec tes tests dans la 2ème partie de ton post est dû à deux choses.

J'ai le code suivant :
 typedef struct test
        {
        char phrase1 [100];
        char * phrase2;
        } test;

        test chaine=
        {
                .phrase1="bonjour !",
                .phrase2="au revoir",
        };

        printf("%s \n %s\n", chaine.phrase1, chaine.phrase2);

Ca compile et ça s’exécute sans aucun souci et je n'ai aucune erreur. Je vous avoue que je comprends rien. Y a une base que je pige pas


Le C sait gérer les affectations lors des déclarations de variables.

De la même façon, en C, tu peux faire :
char phrase[100] = "toto";
et cela va réserver un espace mémoire de 100, copier dans cette zone mémoire allouée "toto" avec le caractère terminateur '\0'. Tout cela est fait par le compilateur au stade de la compilation.

Tu dis aussi :

celui là marche parfaitement :
char * phrase;
phrase="bonjour je suis la";
printf("%s \n", phrase);

Là on n'est plus au stade de la déclaration en ligne 2. Cependant, cela "marche" car lorsque ce code est compilé, le compilateur va réserver un espace mémoire dans la pile pour le stockage de la chaîne littérale constante
"bonjour je suis la"
. Dans ton code, lorsque tu fais
phrase="bonjour je suis la";
, tu affectes à la variable
phrase
l'adresse mémoire où ces données sont stockées.

Ce qui explique ceci :

Alors que si je déclare comme ça :
test chaine;
     chaine.phrase1="bonjour !",
     chaine.phrase2="au revoir",

J'ai une erreur
error: assignment to expression with array type
chaine.phrase1="bonjour !",


Oui, tu as une erreur, car "bonjour !" est évalué par le compilateur C sous la forme d'une adresse mémoire pointant vers la chaîne littérale en question, alors que le type
chaine.phrase1
est un type tableau. Tu ne peux pas faire cela, sauf lors de la déclaration. D'ailleurs, c'est mieux que cela ne marche pas, autrement, tu pourrais écraser l'adresse vers ton espace mémoire réservé à l'usage du tableau. Pour une fois, le C te défend contre ce type de risque.

Pour
chaine.phrase2
, on affecte l'adresse mémoire pointant vers la chaîne littérale en question à une variable pointeur sur
char
qui ne peut accueillir qu'une adresse (pointant sur des données de ce type), donc tout va bien.

Pour ajouter à la casuistique, lors de la déclaration, tu peux valablement faire :

    char * phrase1 = "bonjour !";
    char phrase2[] = "au revoir";

Dans le premier cas, tu as une chaîne littérale à ta disposition, dans le deuxième une chaîne modifiable sous forme de tableau, dont la taille et l'affectation au tableau sont gérées par le compilateur à la déclaration.

:-)

Dal
1
hymenoptera Messages postés 36 Date d'inscription mercredi 24 août 2016 Statut Membre Dernière intervention 18 décembre 2018 1
18 déc. 2018 à 15:42
ahhhh merci infiniment pour ces éclaircissements. Tu m'as vraiment éclairé comme t'as pas idée !

C'est bête, mais je n'ai pas pensé à allouer de la mémoire avec char, alors qu'avec un double ou int c'est automatique. Je comprends bien maintenant que les chaînes de caractères sont en fait des tableaux :D, un peu spéciaux ces tableaux quand même <.<"

Un grand merci. Je ferme le sujet car c'est très complet et super clair ^__^

Très bonne fin de journée
0