Aller au contenu

Discussion:Programmation C/Pointeurs

Le contenu de la page n’est pas pris en charge dans d’autres langues.
Ajouter un sujet
Un livre de Wikilivres.
Dernier commentaire : il y a 1 an par DavidL dans le sujet Exemple de fonction 'échange'

Notes pour Alveric :

J'ai supprimé pas mal de tes modifications, ne le prends pas mal, voilà pourquoi :

- "bytes" pourquoi utiliser un terme anglais alors que l'équivalent français n'est pas ambigü, ni ringard. Ok, j'imagine qu'il y a quelques architectures ultra-ésotérique où un "char" fait autre chose que 8 bits. Mais bon : 1. je ne pense pas que cette doc soit destinée à ses personnes qui vont coder dessus. 2. J'ai dit "en simplifiant à l'extrême".

- Taille variable de pointeur : Là encore, ce n'est de loin pas le cas plus général. Je pense que c'est très maladroit de parler de ça, en mélangeant avec les architectures ayant un accès unifié à la mémoire. À la limite si tu connais une archi qui à un minimum d'avenir et connue par un peu plus que 2 personnes, rajoute une section pour décrire cette particularité. En fait la seule que je connaisse c'est le 286, avec les déclarations non standard type "far" et "near". J'espère que ce n'est pas à ça que tu pensais, parce que c'est complètement mort, et n'a plus rien à faire dans cette doc, si ce n'est qu'en tant que note anecdoctique.

- "dans la mesure où les constantes et expressions du langage n'occupent aucun emplacement en mémoire" : bien-sûr que si que ça en occupe : c'est le code du programme.

Concernant tes précisions sur l'arithmétique sont pas mal, mais ton style est lourd, on l'impression que tu parles à ceux qui savent déjà, et surtout trop dogmatique (fait ci, pas ça, le standard saybien™, il manque trop souvent le "pourquoi"). Donc, j'ai un peu corrigé. Si tu as prévus de corriger mes modifs, merci de noter tes motivations ici. Thierry Pierron 8 juin 2006 à 04:01 (CEST)Répondre

Je ne prends pas mal tes corrections/annulations. Je m'attendais à des réactions et des corrections ; après tout ,c'est un livre collaboratif ;)
Dans l'ordre:
  • pour le byte: Je ne dis pas que le terme octet est ambiguë, ou ringard. Je dis juste que ce n'est pas la même chose que byte, tout du moins en langage C. Je suis d'accord que, sur les architectures "actuelles de bureau", CHAR_BIT == 8. Et ce n'est pas prêt de changer, les fondeurs et surtout les éditeurs de compilateurs n'ayant pas envie de casser du code existant qui se base dessus (parmi d'autres raisons qui n'ont rien à voir avec le C). Mais les implémentations utilisant 16 ou 32 bits pour un char ne sont pas forcément ésotériques. Voilà ce que j'ai trouvé assez rapidement :
  • - http://www.codecomments.com/message683320.html et http://web.linuxfr.org/comments/674702.html#674702 : Analog Devices SHARC 32 bits
    - http://www.embeddedrelated.com/usenet/embedded/show/341-1.php : TMS320C5402 16 bits
    - http://www.embeddedrelated.com/usenet/embedded/show/12330-1.php : TMS320C55x 16 bits
    - http://www.sigala.it/sergio/esd/esd-24jul2001.pdf 32 bits
    - http://groups.google.fr/group/comp.lang.c/msg/c35018075f2d44a6
    Je n'ai trouvé que du traitement du signal (DSP), mais ça ne veut pas dire qu'il n'y a que ça comme machine "moderne" avec plus de 8 bits par char.
    Par ailleurs, je n'aime pas trop ton argument:
    Ok, j'imagine qu'il y a quelques architectures ultra-ésotérique où un "char" fait autre chose que 8 bits. Mais bon : 1. je ne pense pas que cette doc soit destinée à ses personnes qui vont coder dessus.
    Outre le fait que ces architectures ne sont pas si ésotériques que ça, à mon sens, cette doc est destinée à tous ceux qui vont coder en C, quelque soit leur machine ou compilateur. Si leur implémentation a des comportements "inhabituels", évidemment la doc de leur implémentation fait foi, mais je ne vois pas pourquoi on devrait les exclure a priori de notre public. Eux aussi ont le droit d’avoir des cours de C !
    Tel que je comprends ton argument, il faudrait ajouter au paragraphe "Caractères" du chapitre sur les types une remarque du genre:
    Le C définit les tailles mémoires en terme de byte. Cependant, dans la suite de ce livre, on utilisera le terme octet pour parler des tailles d'objets en mémoire, car ces deux notions sont équivalentes sur une large majorité des implémentations du C existantes. Actuellement, seules des architectures, soit anciennes, soit utilisées dans des domaines comme l'embarqué, peuvent avoir des bytes qui font plus de 8 bits. Si vous devez coder sur de telles architectures, ce livre n'est pas fait pour vous.
    Au moins, ça aurait le mérite d'être clair. C'est peut-être expéditif, mais soit on garde le terme byte, et tout le monde peut utiliser ce livre sans problème, soit on utilise octet et tous ceux qui ont 'CHAR_BIT > 8' doivent faire un effort supplémentaire en lisant le livre, voire ne pas le lire et se débrouiller autrement.
    Note que je n'ai rien contre des paragraphes où on prendrait un exemple d'architecture avec CHAR_BIT==8 pour mieux faire comprendre certains points de langage, et que j'admets que la programmation en domaine embarqué nécessite très probablement une connaissance précise de l'environnement sur lequel on travaille (que ce livre n'a pas pour objectif de donner), mais le point sur lequel je ne suis pas d'accord est de faire cette hypothèse dans tout le livre.
    Tout ça pour dire que, personnellement, je ne vois pas où est la lourdeur à utiliser le terme byte plutôt qu'octet. Une fois que le terme est bien défini, il n'y a pas de problème à l'utiliser par la suite, non ? C'est même un des rares cas où je tolère le franglais, la traduction multiplet ne me satisfaisant pas...
  • - "dans la mesure où les constantes et expressions du langage n'occupent aucun emplacement en mémoire" : bien-sûr que si que ça en occupe : c'est le code du programme.
  • Vu comme ça, oui, la valeur est toujours stockée quelque part. Mea culpa donc (je pensais à la mémoire de données, pas au code).
  • taille des pointeurs: ok, il faudrait aller chercher une architecture qui a des pointeurs de taille différente… Dans ce cadre, je crois que le plus simple est tout simplement de ne rien dire sur les tailles des types pointeurs. Autant pour les types entiers, il est nécessaire de savoir quelles sont les valeurs qu’on peut utiliser, autant la connaissance des tailles des types pointeurs est rarement utile. On peut bien évidemment garder l’analogie avec l’index dans une table, mais finalement je pense qu’il vaut bien laisser tomber les précisions sur ces tailles (qui sont hautement non portables et de peu d’intérêt au final).
  • En passant sur c.l.c, je suis tombé sur http://groups.google.fr/group/comp.lang.c/msg/19f07545672ba9e7 qui donne un code illustrant une utilisant avancée du préprocesseur qui irait avec notre discussion (mis à part l'erreur sur le format '%d' de 'printf'). Alveric 14 juin 2006 à 10:35 (CEST)Répondre


    Ah ben, voilà, c'est ce genre d'info que j'aurais préférées voir dans l'article, pas simplement quelques remarques pour dire qu'il y a quelques archies différentes où c'est pas tout à fait comme ça. Je pense qu'il est effectivement utile de parler de ces archies qui addresse la mémoire avec des tailles différentes d'un octet. Par contre je n'aime pas du tout le terme "byte" dans un livre français. Bon, je vais rajouter une explication plus détaillée dans les prochains jours.
    Pour la taille des pointeurs, si ça peut être extrêmement utile. Tu n'as jamais programmé avec l'API Win32 ? Il doit y avoir 32131449968 prototypes de fonctions qui demande un pointeur sous forme d'entier (DWORD). Je ne te parle même pas des champs des structures. Par contre je suis curieux de savoir s'il y a une autre archie que cette merde de 286 qui utilise des tailles différentes pour les pointeurs, et surtout pourquoi il utilise ce mécanisme, plutôt qu'un accès unifié.
    Pour le préprocesseur, il s'agit de la conversion d'un argument en chaine de caractère. C'est déjà décrit quelque part dans le chapitre en question. Bon, cela dit, il n'y pas trop d'exemples détaillant l'intérêt substantifique de la chose, ce serait sans doute pas du luxe d'en rajouter un ou deux. Thierry Pierron 20 juin 2006 à 17:38 (CEST)Répondre
    "Bon, je vais rajouter une explication plus détaillée dans les prochains jours." -> Ok.
    "Par contre je n'aime pas du tout le terme "byte" dans un livre français." -> Moi non plus; mais comme je l'ai dit plus haut, octet n'est pas exact, et multiplet ne me plaît pas des masses non plus... Celà dit, si on se met d'accord sur un des termes, même si c'est multiplet, je l'utiliserai sans rechigner.
    Pour la taille des pointeurs, ok ça peut être utile pour l'API Win32. Je n'ai pas encore fait de programmation Win32, juste vu des codes d'exemple par-ci par-là... Ca m'a l'air louche de demander des pointeurs sous forme d'entier, quand même...
    Et je n'ai pas trouvé d'archi sur laquelle les pointeurs soient de tailles différentes (quoique j'ai lu sur c.l.c. qu'il existe une archi avec des pointeurs sur objet et des pointeurs sur fonction de tailles différente). Alveric 21 juin 2006 à 16:24 (CEST)Répondre

    Remarque déposée par une ip sur l'espace pédogogique

    [modifier le wikicode]

    Franchement c'est faux mais bon j'ai pas le temps de corriqer il faut supprimer ce morceau de l'article ainsi que le suivant.

    justification :

    la fonction devrait renvoyer pour etre correct un int * et la elle renvoie le void * standard du malloc d'ou l'erreur sur tout on ne connait pas le type tableau * utilise dans sizeof 194.254.167.6 (d · c · b)

    Merci à J.M. Tavernier d'avoir déplacé le commentaire.
    J'ai corrigé le code en question. Par contre, la conversion de la valeur de retour de malloc vers le type int * est bien définie, et n'est pas une erreur (à moins de supposer du C K&R, mais bon c'est hors-sujet).
    Alveric

    arithmetique pointeur

    [modifier le wikicode]

    Shmget 7 avril 2007 à 16:19 (CEST)Répondre


    Le code donne en exemple est plutot inhabituel

       for (p = tableau; p < &tableau[N]; p ++)
       {
            /* ... */
       }
    

    En generale, soit on utilise une table, et un index

       for(i = 0 ; i < N; i++)
       {
           f(tableau[i]);
       }
    

    on notera que les compilateurs modernes generent le meme code


    soit on utilise un pointeur

       p = tableau;
       end = tableau + N;
       while(p < end)
       {
           ....
           p += 1;
       }
    

    Dans la vraie vie, la syntaxe a base de pointeur est utilisee quand on ne connait pas N, par exemple le parsing d'une chaine C, ou que l'on se balade dans une structure plus complexe qu'un simple tableau (une liste, une chaine, un arbre, etc..

    A ce propos, il serait en fait, je pense, plus judicieux d'illustrer l'utilisation des pointeur avec l'implementation d'une liste, plutot qu'un tableau.

       p = head;
       while(p)
       {
           ....
           p = p->next;
       }
    


    dans la section : Utilisation des pointeurs pour passer des paramètres par adresse "En fait, même si la variable ne doit pas être modifiée, on utilise quand même un passage par adresse" il serait peut etre judicieux de mentionner 'const' a cet endroit

    A propos du debat 'octet'/'byte' : meme si il est vrai que, strictement parlant un byte - dans le context des spec du C - n'est pas necessairement un octet, Dans le cadre plus general de l'informatique byte=octet. Et le fait d'utiliser byte au lieu d'octet ne clarifie pas la situation pour un non expert de langue francaise, qui ne verra la qu'un angliscisme un non pas une subtilite semantique.

    En ce qui concerne la taille des pointeurs, on peut effectivement raisonablement positer qu'ils sont de taille fixe pour une architecture donnee. Par contre il serait bon de mentionner les problemes d'alignement: par exemple

       char buffer[10] = "....";
       char* p = &buffer[1];
       int i;
    
       i = *(int*)p;
    

    provoquera un SIGBUS sur un Itanium2, un Sparc ou un PowerPC, alors qu'un x86 ne bronchera pas.

    Exemple Complet sur les boucles, les tableaux et les pointeurs

    [modifier le wikicode]

    Récement j'ai trouvé ça, qui permet de mettre en avant l'utilisation des pointeurs et la structures des boucles:

    #include <stdio.h>
    struct p1{
            int             i;
            struct p1       *next;
    }p1;
    int main(void){
            struct p1 *p;
            struct p1 a[4] =  {{0,&a[1]},{1,&a[2]},{2,&a[3]},{4,&a[0]}},
                      b[6] =  {{1,&b[1]},{3,&b[2]},{4,&b[3]},{9,&b[4]},{36,&b[5]},{49,&b[0]}},
                      *pi[2]= {&a[0],&b[0]};
            int i,j=0;
            do
    
                    for(
                            p       = pi[j],
                            i       = 0
                            ;
                            p->i    += ( i == 0 )
                                    ? p->next->i
                                    : 0
                            ,
                            i       == 0
                            ;
                            p       = p->next,
                            i       = (p == pi[j])
                    );
            while ( printf("%i, %i\n",j,p->i),
                    j       =!j,
                    p->i < 1000
            );
            return 0;
    }
    

    Bien qu'exterieur à la discution précédente, je le justifie par le code produit par la décompilation d'une boucle for.
    Tel que:

    void fn(void){
    /*
    gcc -g code.c
    gdb a.aout
    disassemble fn,+150
    */
     int i;
     for(i =0;
         i<65535;
         i++);
    }
    

    On ne constate pas l'utilisation de l'instruction loop, et n'y retrouve pas le registre conteur: CX(/rcx,ecx)
    En revanche on peut observer un 'je'(jump equal) qui va anviron vers la fin. En tout cas il est après un 'jne' (jump not equal) qui va "vers le haut"
    désolé pour le manque d'exemple, mais le code produit est très long bien que clair
    En assembleur loop

    mov ax,0
    mov cx,150
    boucle:
      inc ax
    loop boucle
    

    La ligne 'inc ax' représente un code quelconque.
    A la fin cx==0

    Pointeur de fonction Tableau de pointeurs de fonctions

    [modifier le wikicode]

    Bonjour,

    Dans wikiversité il y a un travail sur les pointeurs de fonctions qui me semble intéressant.

    http://fr.wikiversity.org/wiki/Introduction_au_langage_C/Pointeurs_de_fonctions

    http://fr.wikiversity.org/wiki/Introduction_au_langage_C/Tableau_de_pointeurs_de_fonctions

    http://fr.wikiversity.org/wiki/Introduction_au_langage_C/Tableau_de_pointeurs_de_fonctions_2

    Il y a trois pages. Cela risque d'encombrer la page sur les pointeurs.

    Merci

    Exemple de fonction 'échange'

    [modifier le wikicode]

    L'exemple n'est pas clair et ajoute une complexité inutile pour une fonction du type échange ..! Vous dites pourtant plus haut :

    On peut évidemment complexifier les expressions à outrance, mais privilégier la compacité au détriment de la clarté et de la simplicité dans un hypothétique espoir d'optimisation est une erreur de débutant à éviter.

    #include <stdio.h>
    /* Ce code échange le contenu de deux variables */
    void echange(int *a, int *b)
    {
            int tmp=0, *t[3]={&tmp,a,b};
            unsigned short int i;
            for (i=0;i<3;i++)
                 *t[i] = *t[(i!=2)*(i+1)];
            /*tmp = *a;  <=> *t[0]=*t[1] */
            /**a = *b;  <=>  *t[1]=*t[2] */
            /**b = tmp; <=>  *t[2]=*t[0] */
    }
    

    Pourquoi ne pas mettre une version claire net et precise ?

    void echange(int *a, int *b) {
        int tmp = *a;
        *a = *b;
        *b = tmp;
    }
    

    Youpitu (discussion) 21 novembre 2018 à 14:13 (CET)Répondre

    À cause d'une modification non pertinente passée inaperçue.
    Cette modification a été annulée.
    -- ◄ David L • discuter ► 25 juillet 2023 à 12:17 (CEST)Répondre

    pointeur et objets

    [modifier le wikicode]

    J'ai peut-être rien compris mais les pointeurs (*) et adresses (&) sont des objets Lvalue (left value) www.rocq.inria.fr/secret/Anne.Canteaut/COURS_C/chapitre3.html

    Ce que je veux dire par là c'est que:

    *(a+3) = 4 <=> a[3] = 4.

    ou encore

    struct msg{

    char *str;

    unsigned long int sz;

    };

    avec la chaine "Hello"

    struct msg *m;

    void *ptr;

    ptr = malloc(sizeof(struct msg)+ (strlen("hello") +1)*sizeof(char));

    m = ptr;

    m->str = (char *)(struct msg *)(m+1);

    strcpy(m->str, "hello");

    ...

    mais on aurai pu faire:

    m = calloc(1, sizeof(struct msg);

    m->str = calloc(strlen("hello")+1, sizeof(char));

    ... Le mieux pour comprendre a[3] <=> *(a+3) c'est ça:

    int main(void){
            int i[4] = { 0, 1, 2, 3 }, j;
            j = 0;
            j = i[3];
            j = 0;
            j = *(i + 3);
            j = 0;
            return 0;
    
    }
    

    et en décompilé:

      0x0000000000001149 <+0>:     endbr64
      0x000000000000114d <+4>:     push   %rbp
      0x000000000000114e <+5>:     mov    %rsp,%rbp
      0x0000000000001151 <+8>:     sub    $0x30,%rsp
      0x0000000000001155 <+12>:    mov    %fs:0x28,%rax
      0x000000000000115e <+21>:    mov    %rax,-0x8(%rbp)
      0x0000000000001162 <+25>:    xor    %eax,%eax
      0x0000000000001164 <+27>:    movl   $0x0,-0x20(%rbp)
      0x000000000000116b <+34>:    movl   $0x1,-0x1c(%rbp)
      0x0000000000001172 <+41>:    movl   $0x2,-0x18(%rbp)
      0x0000000000001179 <+48>:    movl   $0x3,-0x14(%rbp)
      0x0000000000001180 <+55>:    movl   $0x0,-0x24(%rbp) (<=ici j = 0 je crois)
      0x0000000000001187 <+62>:    mov    -0x14(%rbp),%eax
      0x000000000000118a <+65>:    mov    %eax,-0x24(%rbp)
      0x000000000000118d <+68>:    movl   $0x0,-0x24(%rbp) (<=ici j = 0 je crois)
      0x0000000000001194 <+75>:    mov    -0x14(%rbp),%eax
      0x0000000000001197 <+78>:    mov    %eax,-0x24(%rbp)
      0x000000000000119a <+81>:    movl   $0x0,-0x24(%rbp) (<=ici j = 0 je crois)
      0x00000000000011a1 <+88>:    mov    $0x0,%eax
      0x00000000000011a6 <+93>:    mov    -0x8(%rbp),%rdx
      0x00000000000011aa <+97>:    xor    %fs:0x28,%rdx
      0x00000000000011b3 <+106>:   je     0x11ba <main+113>
      0x00000000000011b5 <+108>:   callq  0x1050 <__stack_chk_fail@plt>
      0x00000000000011ba <+113>:   leaveq 
      0x00000000000011bb <+114>:   retq
    

    81.64.1.143 8 mai 2023 à 13:52 (CEST)Répondre

    (outils utilisés: vim, gcc, gdb sous linux) bon moi je suis pas sure, je fais des truc et des bidules...