promenade dans un const * char (débutant)

promenade dans un const * char (débutant) - C - Programmation

Marsh Posté le 30-11-2017 à 23:22:23    

Bonsoir,
 
Voici le code qui m'interroge :
 

Code :
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. typedef char * string ;
  5. int main()
  6. {
  7. const string a = "test" ;
  8. *a++;
  9. printf("%c\n", *a) ;
  10. return 0;
  11. }


 
Si j'enlève le const ça fonctionne. *a++ m'amène à la case suivante et le printf de test l'affiche bien le char 'e'.
Quand je mets le const, j'obtiens à la compilation l'erreur suivante :
 
 

Code :
  1. error: increment of read-only variable ‘a’


 
Quand j'incrémente en faisant *a++ je modifie ma chaine ? Je pensais que je me déplaçais juste dedans... normal que ça marche pas quand  c'est un const ?
 
D'avance merci
 

Reply

Marsh Posté le 30-11-2017 à 23:22:23   

Reply

Marsh Posté le 01-12-2017 à 03:02:34    

Je devrais pas répondre à cette heure ci, j'espère ne pas raconter de conneries... :o
 
Plusieurs choses:
Ton typedef est très mal choisi et je dirais même il est faux: Un string c'est un tableau, pas un pointeur. La différence est que pour un tableau il y a de la place pour les caractères réservés en mémoire, pour un pointeur non. Mais tu ne peux pas dire typedef char string[] car il faut indiquer une taille, hors pour un truc générique tu ne connais pas celle-ci par avance. Les typedef c'est très bien pour les trucs complexes genre pointeur de fonction, mais pour les bricolages avec les strings c'est à éviter je dirais. En plus si on lit un code avec des typedef faut toujours aller voir ce qui se cache derrière d'abord, c'est pénible et dans notre cas vraiment inutile.
 
*a++ correspond à *(a++), autrement dit tu incrémentes le pointeur et ensuite tu le déréférences mais tu ne fais rien avec le résultat. L'étoile est donc inutile et si tu as activé les warnings de ton compilateur (TRÈS important!) ce dernier te le dirait: warning: value computed is not used [-Wunused-value]
(Pour GCC: rajouter -Wall)
 
Ce que tu as écrit signifie que le pointeur est constant, pas la chaîne de caractères vers laquelle il pointe (mais dans notre cas celle-ci sera forcément constante aussi car c'est un string literal). Il faut mettre le const entre le "char" et l'étoile dans le typedef (si tu insistes là-dessus, voir ma remarque en haut): typedef char const * string;
 
Pour finir voici un petit exemple concernant cette promenade:

Code :
  1. #include <stdio.h>
  2.  
  3. int main(void)
  4. {
  5.    char * ptr="abc123"; //ptr pointe vers l'espace mémoire qui contient notre chaine (constante)
  6.    
  7.    while(*ptr!='\0') //avec l'étoile on déréférence pour savoir si on est arrivé en bout de chaine ('\0')
  8.    {
  9.        printf("%x -> %c\n", ptr, *ptr);
  10.        ptr++;
  11.    }
  12.  
  13.    return 0;
  14. }


 
ou avec un tableau (string modifiable):

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main(void)
  5. {
  6.    char string[7]; // il faut de la place pour le '\0' final aussi!
  7.    
  8.    strncpy(string, "abc123", 7); // on ne peut PAS assigner un string avec le signe égal en C
  9.              //(sauf cas particulier au moment de déclarer la variable, voir en bas), string="abc123" serait faux!
  10.    
  11.    char *ptr=string; //on ne peut pas incrémenter string directement car c'est un tableau, pas un pointeur!
  12.    
  13.    while(*ptr!='\0')
  14.    {
  15.        printf("%x -> %c\n", ptr, *ptr);
  16.        ptr++;
  17.    }
  18.  
  19.    return 0;
  20. }


 
ou alternative avec un tableau et un index:

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int main(void)
  5. {
  6.    char string[]="abc123"; // quand on déclare un tableau on peut assigner un string,
  7.            //il sera "mis" dans le tableau avec le '\0' final
  8.            //et la taille du tableau est implicite, inutile (mais possible) de l'indiquer
  9.  
  10.    unsigned int i=0;
  11.    
  12.    while(string[i]!='\0')
  13.    {
  14.        printf("%d -> %c\n", i, string[i]);
  15.        i++;
  16.    }
  17.  
  18.    return 0;
  19. }


 
Tu remarquera la différenciation au niveau nom des variables entre string et pointeur (ptr).
 
Bon, ça fait beaucoup, j'espère ne pas avoir aggravé la confusion... :pt1cable:

Reply

Marsh Posté le 01-12-2017 à 11:03:41    

nanataw, la forme correcte de ton programme serait:
 

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(void)
  4. {
  5.     const char (*a) = "test";
  6.     a++;
  7.     printf("%c\n", *a) ;
  8.     return 0;
  9. }


 
*a est un const char donc a est un pointeur sur un const char.
 
Et c'est très bien ainsi, car quand on fait

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(void)
  4. {
  5.     char *a = "test";
  6.     a++;
  7.     printf("%c\n", *a) ;
  8.     return 0;
  9. }


dans les faits, a pointe en réalité des const char et vouloir modifier la valeur d'un caractère pointé par a va déclencher une run-time error.
Alors que ce sera détecté à la compilation si l'on déclare a comme un pointeur sur un const char.
 
 
C'est toute la différence avec ce qui est fait ici:

Code :
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(void)
  4. {
  5.   char a[] = "test";
  6.   char *p = a;
  7.   ++p;
  8.   printf("%c\n", *p) ;
  9.   return 0;
  10. }


 
a est un tableau qui initialisé par copie de la zone contenant "test".  C'est le seul cas ou on n'a pas besoin de donner sa taille au tableau, RdC, car le compilateur la calcule avant de créer le tableau, à partir de la taille de ce qui doit être copié. a est ici un char[5].
De ce fait, son contenu est modifiable.
 
Donc
char *a = "test"; // a pointe en fait sur des chars non modifiables (dans une zone créée a la compilation), et on ne le dit pas, dangereux
const char (*a) = "test"; // a pointe en fait sur des chars non modifiables, et on le dit, OK
char a[] = "test"; // a est un char[5] créé a l'exécution, qui contient une copie de "test", donc modifiable, OK
 
Noter que dans tous les cas, on est  la merci d'un run-time error par dépassement de la taille du tableau. Donc faire un test if (*p) avant d'avancer son pointeur peut être utile.
Dans la pratique:
J'ai une chaine "test" utilisée uniquement en lecture: const char (*a) = "test";
J'ai une chaine "test" dont certains caractères peuvent être modifié: char a[] = "test"; c'est un cas assez rare
J'ai une chaine initialisée à "test" complètement modifiable, même en taille: char *a = strdup("test'); qu'on oubliera pas de libérer avec un free après utilisation, et on fait du realloc si sa taille devient insuffisante.
Et... bien souvent
J'ai une chaine initialisée à "test" complètement modifiable, même en taille, mais dont je sais qu'elle ne sera jamais plus grande que TAILLE_MAX
char a[TAILLE_MAX+1] = "test";
(ici le compilo crée le tableau avec la taille donnée, puis fait la copie (éventuellement avec troncature si la taille du tableau est trop petite)).
L'avantage de cette technique, c'est que c'est le programme qui va gérer automatiquement la libération du tableau lorsqu'il sort du scope.
 
A+,


Message édité par gilou le 01-12-2017 à 11:39:06

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed