IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

[] et * , même combat ?


Sujet :

C

  1. #1
    Membre éclairé Avatar de ypcman
    Homme Profil pro
    Retraité codeur !
    Inscrit en
    Janvier 2011
    Messages
    599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Retraité codeur !
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2011
    Messages : 599
    Points : 887
    Points
    887
    Par défaut [] et * , même combat ?
    Bonjour.
    Je ne comprend pas pourquoi et ont un comportement différent

    Ce code fonctionne parfaitement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <stdlib.h>
    #include<string.h>
     
    int main(void) {
    	char mot[] = "test";
    	printf("%s est de longueur %lu\n", mot, strlen(mot));
    	printf("mot[1]  : %c\n", mot[1]);
    	mot[1] = 'o';
    	printf("%s\n", mot);
    	return EXIT_SUCCESS;
    }
    et affiche
    test est de longueur 4
    mot[1] : e
    tost
    Mais celui-ci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <stdlib.h>
    #include<string.h>
     
    int main(void) {
    	char *mot = "test";
    	printf("%s est de longueur %lu\n", mot, strlen(mot));
    	printf("mot[1]  : %c\n", mot[1]);
    	mot[1] = 'o';
    	printf("%s\n", mot);
    	return EXIT_SUCCESS;
    }
    compile mais n'affiche que
    test est de longueur 4
    mot[1] : e
    Pourtant, dans les deux cas, le compilateur crée un tableau de caractères de 4+1(\0) caractères ...

  2. #2
    Membre averti
    Homme Profil pro
    très occupé
    Inscrit en
    Juillet 2014
    Messages
    137
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : très occupé

    Informations forums :
    Inscription : Juillet 2014
    Messages : 137
    Points : 411
    Points
    411
    Par défaut
    Salut ypcman,

    Crée un "string literal". Selon le standard du C (par exemple dans C11, au 6.4.5 String literals) :

    "If the program attempts to modify such an array, the behavior is
    undefined.
    "

    Comme le comportement est indéfini, ton programme peut faire n'importe quoi lorsque tu tentes de modifier le contenu de la chaîne créée.

    Tu devrais traiter ce type de déclaration comme une constante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    const char * mot = "test";
    Concrètement, que tu mettes le mot clef const ou pas, ce type de déclaration aboutit à inclure la chaîne littérale dans une partie du code exécutable du programme qui, à l'exécution, est disponible dans un segment de mémoire en lecture seule utilisé par le programme (code segment).

  3. #3
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 720
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 720
    Points : 31 037
    Points
    31 037
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par ypcman Voir le message
    Pourtant, dans les deux cas, le compilateur crée un tableau de caractères de 4+1(\0) caractères ...
    Non. Seule l'instruction char mot[] crée un tableau. L'autre instruction char * mot ne crée pas de tableau (tout comme l'instruction int xxx par exemple ne crée pas non plus de tableau). Pour créer un tableau, il faut des crochets
    L'instruction char * mot déclare simplement un type (tout comme l'instruction int xxx déclare un type). Pour xxx ça déclare un type "int", pour mot ça déclarer un type "pointeur". Rien de plus (et rien de moins).

    Ensuite, seconde étape, le traitement du "=". Dans l'instruction char mot[]="test", le compilateur va
    • compter la string à lui mettre (effectivement comme tu l'as dit il y a bien 5 caractères)
    • créer un tableau de cette taille
    • copier un à un chaque caractère de cette string dans le tableau de char


    Tandis que dans l'instruction char * mot="test", le compilateur va
    • stocker la string "test" dans une zone spécifique du code (zone non modifiable)
    • récupérer l'adresse de cet emplacement
    • stocker cette adresse dans le pointeur "mot" (car ça sert à ça un pointeur: pouvoir stocker une adresse, rien de plus (et là encore rien de moins)


    Ca "semble" identique car tu ne fais que lire ce mot. Or lire un tableau de char, ou lire une zone de char, c'est exactement la même chose. Là où ça va se corser, c'est si tu t'amuses à modifier un des caractères (mot[2]='x' par exemple). Dans le premier cas, la string ayant été "copiée" dans le tableau "mot" ça ira (tu as en effet le droit de modifier les éléments de tes tableaux). Mais dans le second cas, comme tu ne fais que cibler une zone read-only, quand tu vas vouloir la modifier...

    Accessoirement l'écriture char * mot avec cette étoile au milieu est la pire qui soit. C'est l'écriture de "ouh là là c'est chaud je sais pas quoi faire de l'étoile". Soit tu la mets à gauche (char* mot), privilégiant ainsi l'aspect "mot est un pointeur, je gère", soit tu la mets à droite (char *mot), privilégiant l'aspect "si je pointe sur mot j'obtiens un char, je gère". Attention, soyons bien clair, toutes les écritures, du point de vue du compilo, signifient la même chose. C'est donc juste un effet de "quelle est ma façon de gérer" que je veux montrer aux autres lecteurs de mon code. Mais l'étoile au milieu c'est "je gère que dalle, je sais pas où je vais".

    Citation Envoyé par ypcman Voir le message
    Mais celui-ci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <stdlib.h>
    #include<string.h>
     
    int main(void) {
    	char *mot = "test";
    	printf("%s est de longueur %lu\n", mot, strlen(mot));
    	printf("mot[1]  : %c\n", mot[1]);
    	mot[1] = 'o';
    	printf("%s\n", mot);
    	return EXIT_SUCCESS;
    }
    compile mais n'affiche que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    test est de longueur 4
    mot[1] : e
    Tu es sûr que ça n'affiche que ça? Normalement tu dois avoir en plus Erreur de segmentation (core dumped) provenant de la tentative d'écriture en zone RO (enfin ça dépend aussi de ton environnement de travail...)

  4. #4
    Membre éclairé Avatar de ypcman
    Homme Profil pro
    Retraité codeur !
    Inscrit en
    Janvier 2011
    Messages
    599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Retraité codeur !
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2011
    Messages : 599
    Points : 887
    Points
    887
    Par défaut
    Merci -Eks- et Sve@r, ça me semble plus clair maintenant.
    • Avec [] il y a réservation de zone mémoire contiguë (en RW) pour stocker les caractères formant le mot (plus le '\0')
    • Avec *, il n'y a qu'une affectation d'une adresse en mémoire, le mot étant stocké ailleurs en read-only ...

    Bien vu Sve@r pour la position de *

  5. #5
    Membre averti
    Homme Profil pro
    très occupé
    Inscrit en
    Juillet 2014
    Messages
    137
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : très occupé

    Informations forums :
    Inscription : Juillet 2014
    Messages : 137
    Points : 411
    Points
    411
    Par défaut
    Salut Sve@r,

    Citation Envoyé par Sve@r Voir le message
    (...)
    Accessoirement l'écriture char * mot avec cette étoile au milieu est la pire qui soit. C'est l'écriture de "ouh là là c'est chaud je sais pas quoi faire de l'étoile". Soit tu la mets à gauche (char* mot), privilégiant ainsi l'aspect "mot est un pointeur, je gère", soit tu la mets à droite (char *mot), privilégiant l'aspect "si je pointe sur mot j'obtiens un char, je gère". Attention, soyons bien clair, toutes les écritures, du point de vue du compilo, signifient la même chose. C'est donc juste un effet de "quelle est ma façon de gérer" que je veux montrer aux autres lecteurs de mon code. Mais l'étoile au milieu c'est "je gère que dalle, je sais pas où je vais".
    (...)
    En ce qui me concerne, j'aime bien l'écriture char * mot; et je ne considère pas que cela soit la "pire qui soit", et qu'il est même tout à fait raisonnable d'utiliser cette forme.

    Lorsque l'on écrit un type en C, avec des mots clefs du langage C, on les sépare naturellement par des espaces sinon le compilateur ne comprend rien. Par exemple dans unsigned char c;, "unsigned char" est un type et, bien sûr, on a des espaces entre ces mots clefs permettant la construction du type et la variable déclarée au moyen de ce type.

    Lorsque l'on écrit int * i;, "int *" est un type, et sous prétexte que l'astérisque est un symbole que le parser sait traiter avec ou sans espace (parce qu'il ne peut pas faire partie d'un nom de variable), je ne vois pas pourquoi il ne serait pas tout à fait raisonnable de le traiter identiquement à un mot clef permettant de construire un type pour ce qui n'est, en réalité, qu'une manière symbolique d'écrire "un pointeur sur char". Il a une fonction comparable à l'exemple précédent.

    En revanche, lorsque l'étoile est utilisée, non pas pour représenter un type, mais pour déréférencer un pointeur et permettre d'accéder à la valeur pointée, j'aime bien écrire printf("i = %d\n", *i); car cela permet de distinguer visuellement le fait que l'astérisque est ici un opérateur unaire de déréférencement de ce qui suit immédiatement, le symbole astérisque ayant alors une signification différente.

    Bien sûr là, on parle de goûts et de couleurs, et chacun voit midi à sa porte.

    A mon sens, ce qui importe, pour un lecteur, c'est que le style d'écriture du code soit cohérent.

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 720
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 720
    Points : 31 037
    Points
    31 037
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par -Eks- Voir le message
    Salut Sve@r,


    Citation Envoyé par -Eks- Voir le message
    En ce qui me concerne, j'aime bien l'écriture char * mot et je ne considère pas que cela soit la "pire qui soit", et qu'il est même tout à fait raisonnable d'utiliser cette forme.
    Mwwwoui... et le double pointeur ? char * * mot ???
    Bon c'est pas grave, effectivement c'est goûts et couleurs. Bizarrement quand j'écris sans faire attention j'écris char *mot et ensuite je reviens modifier exprès en char* mot parce que je préfère indiquer "oui, pour moi mot est un pointeur"...

  7. #7
    Membre averti
    Homme Profil pro
    très occupé
    Inscrit en
    Juillet 2014
    Messages
    137
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : très occupé

    Informations forums :
    Inscription : Juillet 2014
    Messages : 137
    Points : 411
    Points
    411
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    (...)
    Mwwwoui... et le double pointeur ? char * * mot ???
    (...)
    Pour les pointeurs sur des pointeurs, je considère que char ** mot; est un bon compromis entre concision et respect de la sémantique de l'usage de ce symbole.

    (c'est mon opinion et je la partage )

  8. #8
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 317
    Points : 354
    Points
    354
    Par défaut
    Essaye peut-être avec char *mot=calloc(5,sizeof(char));
    Puis strcpy();

  9. #9
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    317
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 317
    Points : 354
    Points
    354
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    int main(void)
    {
        char *mot=calloc(5,sizeof(char));
        strcpy(mot,"test");
     
        printf("Contenu: %s\n",mot);
        mot[1]='o';
     
        printf("contenu: %s\n",mot);
        exit(0);    
     
    }
    Maintenant pour expliquer pourquoi le second printf() ne passe pas... là j'ai toujours eu du mal.

    EDIT: chez moi j'ai un segmentation fault quand je compile avec OLD comme directive.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
     
    int main(void)
    {
        #ifndef OLD
        char *mot=calloc(5,sizeof(char));
        strcpy(mot,"test");
        #else
        char *mot="test"; // <-- DANGEREUX CA ^^
        #endif
     
     
        printf("Contenu: %s\n",mot);
        mot[1]='o'; // ICI ça va faire BOUM !!
     
        printf("Nouveau contenu: %s\n",mot);
        exit(0);    
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    sirius:/exemples/2022/forum # gcc test.c -o test -DOLD -Wall
    sirius:/exemples/2022/forum # ./test
    Contenu: test
    Segmentation fault (core dumped)
    sirius:/exemples/2022/forum #
    Ce qui me semble logique car char *mot est un pointeur sur UN char et j'en tape 4 en mémoire (<--), mais je suis surpris que le compilo même avec -Wall ne bronche pas du tout.

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 720
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 720
    Points : 31 037
    Points
    31 037
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    Essaye peut-être avec char *mot=calloc(5,sizeof(char));
    Puis strcpy();
    Non, ce n'était pas le but de sa question. Il ne veut pas copier des caractères dans une zone (et allouer via malloc là où un simple tableau suffit...) mais juste comprendre la différence entre deux écritures.

    Citation Envoyé par hurukan Voir le message
    Ce qui me semble logique car char *mot est un pointeur sur UN char et j'en tape 4 en mémoire (<--), mais je suis surpris que le compilo même avec -Wall ne bronche pas du tout.
    Effectivement mot est un pointeur sur un char ok. Mais ce char se trouve perdu au milieu des autres. Donc là où pointe "mot" (on va dire pour l'exemple que c'est à l'adresse 0x40) effectivement il y a le char 't'. Mais à côté, donc à l'adresse 0x41, il y a le caractère 'e'. Et le caractère 's' à la case suivante (0x42) et le caractère 't' à la case 0x43 et enfin le caractère '\0' à l'adresse 0x44. Parce que, quand tu écris "test" dans ton code, le compilo il doit le stocker quelque part ce "test". Et donc il le stocke dans une zone statique de la RAM, avec un caractère par case.
    Ensuite, pour afficher "test", il suffit d'aller au début (0x40) et afficher ce qui s'y trouve là et sur les cases suivantes de la zone en déroulant (donc via une boucle). Et stopper quand on trouve le caractère '\0'. C'est exactement ce qui se passe quand tu demandes "%s" au printf() mais qu'on peut coder manuellement pour voir ce qui se passe
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
    #include <stdlib.h>
     
    int main(void) {
    	char *mot="test";
    	printf("mot=%p\n", mot);
    	for (char *pt=mot; *pt != '\0'; pt++)
    		printf("pt=%p (%c)\n", pt, *pt);
    	exit(0);	
    }

    En fait, ça marche parce que (et c'est garanti dans la norme), un tableau est toujours contigü en mémoire. Donc (corollaire), tenir un tableau ne nécessite que de tenir son premier élément, d'où le fait que tu as beau avoir un pointeur sur "un" truc, cela ne t'empêche pas d'accéder quand-même via ce pointeur à tout les autres trucs qui se trouvent à côté dans la mémoire (suffit juste de décaler ce pointeur en avant et aussi, pourquoi pas, en arrière). Tout ce que tu as à t'assurer, c'est que tu ne débordes pas plus loin dans la mémoire que la zone que tu as toi-même déclarée.
    Et donc au résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    mot=0x55aa86943004
    pt=0x55aa86943004 (t)
    pt=0x55aa86943005 (e)
    pt=0x55aa86943006 (s)
    pt=0x55aa86943007 (t)
    Les valeurs des adresses peuvent varier (dépend de l'OS et de l'état de la RAM au lancement du programme) mais ce qui ne variera pas ce sera
    • le fait que la l'adresse contenue dans "mot" se retrouve dans "pt" (normal, on a demandé pt=mot en début de boucle)
    • le fait que les autres adresses se suivront toujours de 1 en 1 (le pt++ dans la boucle)


    La raison pour laquelle tu as ce segfault n'est pas parce que tu associes 5 caractères à un pointeur sur un char, mais parce que tu tentes de modifier ces caractères alors qu'ils se trouvent dans une zone RO de la mémoire.

  11. #11
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 677
    Points
    13 677
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    je suis surpris que le compilo même avec -Wall ne bronche pas du tout.
    Let's rencontrer -Wwrite-strings

Discussions similaires

  1. cin et QTextStream : même combat !
    Par Virgile le chat dans le forum Débuter
    Réponses: 7
    Dernier message: 01/10/2009, 16h47
  2. vector<Mere> et vector<Fille> même combat
    Par Ghurdyl dans le forum Débuter
    Réponses: 11
    Dernier message: 21/08/2009, 13h40
  3. L'australie et la Chine, même combat !
    Par Davidbrcz dans le forum La taverne du Club : Humour et divers
    Réponses: 5
    Dernier message: 06/11/2008, 09h38
  4. Soustraction : Excel ou VBA même combat
    Par ouskel'n'or dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 05/01/2008, 00h17
  5. asp et php, même combat ?
    Par sansblague dans le forum ASP
    Réponses: 1
    Dernier message: 08/11/2006, 08h40

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo