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

Free Pascal Discussion :

Mini-tutoriel : Différence entre char et chr (auto-analyse de code)


Sujet :

Free Pascal

  1. #1
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut Mini-tutoriel : Différence entre char et chr (auto-analyse de code)
    Introduction

    Suite à la lecture d'un post sur le sujet ( http://www.developpez.net/forums/sho...d.php?t=296299 ) , j'ai vérifié dans mon debugger s'il y avait une différence entre les deux fonctions suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function fchar (c: byte): char;
    begin
    fchar := char(c);
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function fchr (c: byte): char;
    begin
    fchr := chr(c);
    end;
    En quelques secondes je me suis rendu compte que le compilateur avait traduit ces deux fonctions de la meme manière, les instructions en assembleur étant exactement les memes pour les deux fonctions. Ceci confirmait l'analyse de Droggo...
    Citation Envoyé par Droggo
    Pris d'une brutale poussée de courage, je suis allé voir le code généré :

    c'est exactement le même code, ce qui confirme l'impression que j'avais eue en voyant les temps d'exécution très proches, avec l'un ou l'autre plus ou moins rapide selon le test
    J'ai soudain été étonné de voir transparaitre dans les réponses suivantes, que l'analyse d'un binaire ou le debuggage de process n'est pas forcément évident pour beaucoup...

    Prenant à mon tour mon courage à deux mains , je vai vous montrer comment vous pouvez démontrer sans aucun outil (ormis votre cerveau et un compilateur pascal ), que ces deux fonctions sont équivalentes...
    J'espère que ce post vous sera utile ou vous interessera...

  2. #2
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Premiers tests

    La première chose évidente est de tester que les deux fonctions renvoient les memes valeurs ...

    me voici donc en train d'ecrire puis de compiler et lancer ce petit test qui s'amuse à calculer les resultats pour tous les bytes de 1 à 255:
    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
    21
    22
    23
    24
    25
    26
    program test;
     
    // mes fonctions à tester
    function fchar (c: byte): char;
    begin
    fchar := char(c);
    end;
     
    function fchr (c: byte): char;
    begin
    fchr := chr(c);
    end;
     
    // variables du programme
    Var
     i : byte;
     
    // programme principal
    begin
      for i := 1 to 255 do
      if fchar(i)<>fchr(i) then
       writeln('test sur ',i,' valeurs differentes')
      else
       writeln('test sur ',i,' valeurs egales');
      readln;
    end.
    Au niveau des resultats, les fonctions sont donc équivalentes...

    Je pousse le vice jusqu'à changer la variable i en integer et tenter une boucle de 1 à 500...
    Freepascal compile correctement et le test se passe sans erreur...

    Après ces tests de résultats passons à une analyse des entrailles....

  3. #3
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Auto Analyse 1 : adresse des fonctions

    Je décide donc d'analyser le programme par instructions...

    Je modifie le corps principal de mon programme et ecris ce petit code qui me permet de récupérer les adresses en memoire des fonctions fchar et fchr

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // programme principal
    begin
     writeln('adresse de fchar : ', hexstr(integer(@fchar),8));
     writeln('adresse de fchr : ', hexstr(integer(@fchr),8));
     readln;
    end.
    Explications sommaires:
    @fchar représente un pointeur vers fchar, cette expression contient donc l'adresse memoire ou la fonction commence.
    Le transtypage par integer() est là pour permettre l'utilisation de la fonction hexstr de freepascal, qui attend en premier parametre un integer, et qui permet d'ecrire l'adresse trouvée dans un format hexadecimal classique sur 8 chiffres...

    Avec freepascal, et sur mon systeme d'exploitation j'obtiens:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    adresse de fchar : 00401018
    adresse de fchr : 00401025

  4. #4
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 947
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 947
    Points : 5 660
    Points
    5 660
    Par défaut
    Hi,

    Dans ce que tu appelles "ces deux fonctions", une seule notation en est une (chr), l'autre est un transtypage.

    Que le compilateur les traite de la même manière et génère le même code ne rentre pas en compte pour parler de fonction au lieu de transtypage, d'autant qu'il est bien possible que d'autres compilateurs ne feront pas de même, bien que ce soit fort probable.

  5. #5
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Petit moment de reflexion et strategie de demonstration

    Me voici avec les adresses des deux fonctions...
    je calcule combien de byte il y a entre les deux debuts de fonction :attention les chiffres donnés étaient en hexadécimal, je laisse mon programme me donner le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     writeln((integer(@fchr))-(integer(@fchar)));
    Il y a donc 13 bytes entre le debut de ma fonction fchar et le debut de ma fonction fchr...

    Ma fonction fchar fait donc au maximum 13 bytes...

    Pour prouver que mes deux fonctions sont égales, il me suffit donc de dumper 13 bytes de memoire à partir de l'adresse de fchar, et 13 bytes de memoire à partir de fchr, puis de comparer les resultats.... Si je trouve la meme chose, c'est démontré....

  6. #6
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 947
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 947
    Points : 5 660
    Points
    5 660
    Par défaut
    Hi,
    Citation Envoyé par Clandestino
    Petit moment de reflexion et strategie de demonstration

    Me voici avec les adresses des deux fonctions...
    je calcule combien de byte il y a entre les deux debuts de fonction :attention les chiffres donnés étaient en hexadécimal, je laisse mon programme me donner le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     writeln((integer(@fchr))-(integer(@fchar)));
    Il y a donc 13 bytes entre le debut de ma fonction fchar et le debut de ma fonction fchr...

    Ma fonction fchar fait donc au maximum 13 bytes...

    Pour prouver que mes deux fonctions sont égales, il me suffit donc de dumper 13 bytes de memoire à partir de l'adresse de fchar, et 13 bytes de memoire à partir de fchr, puis de comparer les resultats.... Si je trouve la meme chose, c'est démontré....
    Sauf si le compilateur a ajouté des instructions "bidons" pour aligner le code, cas fréquent avec les compilateurs sachant vraiment optimiser à fond (presque à fond, les processeurs modernes sont très difficiles à optimiser totalement).

    Autant prendre un désassembleur et aller voir le code.

  7. #7
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Dump memoire des deux fonctions

    j'ai effectué ce test avec le compilateur freepascal sous windows vista... je vous encourage à tester la meme chose dans d'autres situations...
    Ma demonstration ne s'applique, jusqu'à preuve du contraire qu'à cette configuration...

    J'ai intégré un peu d'assembleur dans mon code, ceci rendant ce mini tuto un peu plus interessant 8-)

    Voici une fonction pour recupérer un byte à une adresse memoire donnée :
    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
    // ma fonction de dump
    Function ReadByteAtAddress( adresse: dword): byte;
    var
    d: dword;
    begin
      asm
        push eax        // sauvegarde de eax sur la pile
        push edi        // sauvegarde d'edi sur la pile
        mov edi, dword ptr[adresse]  // edi reçoit l'adresse de la fonction
        mov eax, dword ptr[edi]      // eax reçoit le dword à cette adresse
        mov d, eax                   // je stocke le resultat dans d
        pop edi         // restauration de edi depuis la pile
        pop eax         // restauration de eax depuis la pile
      end;
      // ici je mets un dword dans un byte, je perds donc les informations de 3 bytes
      // suivants que je lirai ensuite.
      // c'est un choix délibéré de lire byte par byte juste parceque la fonction
      // hexstr me renverrai sinon un resultat à lire de droite à gauche...
      ReadByteAtAddress := d;
    end;
    J'ai pris la précution dans ce bout de code asm de sauvegarder les registres utilisés puis de les retribuer, habitué que je suis à faire celà quand je patche depuis un debuggueur... je ne suis pas certain que celà soit nécessaire depuis un code source à compiler... d'autres sauront sans doute me préciser ce sujet...

  8. #8
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Pogramme final

    Il ne nous reste donc plus qu'à lire deux fois treize bytes et afficher le résultat pour voir ou nous en sommes...

    Ceci peut être fait ainsi :
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    program test;
     
    // mes fonctions à tester
    function fchar (c: byte): char;
    begin
      fchar := char(c);
    end;
     
    function fchr (c: byte): char;
    begin
      fchr := chr(c);
    end;
     
    // ma fonction de dump
    Function ReadByteAtAddress( adresse: dword): byte;
    var
    d: dword;
    begin
      asm
        push eax        // sauvegarde de eax sur la pile
        push edi        // sauvegarde d'edi sur la pile
        mov edi, dword ptr[adresse]  // edi reçoit l'adresse de la fonction
        mov eax, dword ptr[edi]      // eax reçoit le dword à cette adresse
        mov d, eax                   // je stocke le resultat dans d
        pop edi         // restauration de edi depuis la pile
        pop eax         // restauration de eax depuis la pile
      end;
      // ici je met un dword dans un byte, je perds donc les informations de 3 bytes
      // suivants que je lirai ensuite.
      // j'ai choisi de lire byte par byte juste parceque la fonction
      // hexstr me renverrai sinon un resultat à lire de droite à gauche...
      ReadByteAtAddress := d;
    end;
     
    // variables du programme
    Var
     i : integer;
     buffchar : array[1..13] of byte;
     buffchr  : array[1..13] of byte;
     
    // programme principal
    begin
     i:=0;
     
     
     
     writeln('adresse de fchar : ', hexstr(integer(@fchar),8));
     writeln('adresse de fchr : ', hexstr(integer(@fchr),8));
     
     
     writeln;
     
     writeln('  Dump fchar                  Dump fchr');
     writeln;
     writeln('adresse   byte              adresse   byte');
     writeln;
     repeat
      inc(i);
      buffchar[I]:= ReadByteAtAddress(integer(@fchar)+i-1);
      buffchr[I]:= ReadByteAtAddress(integer(@fchr)+i-1);
      writeln(hexstr(integer(@fchar)+i-1,8),'  ', hexstr(buffchar[i],2),
      '                ',
      hexstr(integer(@fchr)+i-1,8),'  ', hexstr(buffchr[i],2));
      until i = 13;
     readln;
    end.
    voici le résultat chez moi:
    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
    adresse de fchar : 00401018
    adresse de fchr : 00401025
     
      Dump fchar                  Dump fchr
     
    adresse   byte              adresse   byte
     
    00401018  55                00401025  55
    00401019  89                00401026  89
    0040101A  E5                00401027  E5
    0040101B  83                00401028  83
    0040101C  EC                00401029  EC
    0040101D  04                0040102A  04
    0040101E  8A                0040102B  8A
    0040101F  45                0040102C  45
    00401020  08                0040102D  08
    00401021  C9                0040102E  C9
    00401022  C2                0040102F  C2
    00401023  04                00401030  04
    00401024  00                00401031  00

  9. #9
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Citation Envoyé par droggo
    Hi,

    Sauf si le compilateur a ajouté des instructions "bidons" pour aligner le code, cas fréquent avec les compilateurs sachant vraiment optimiser à fond (presque à fond, les processeurs modernes sont très difficiles à optimiser totalement).

    Autant prendre un désassembleur et aller voir le code.
    c'est pour celà que je disais ma fonction fait au maximum 13 bytes.
    elle peut en faire moins. J'aurai aussi pu avoir du junk code et ma demonstration n'aurait pas suffit dans l'état...


    Ma démarche ici etait simplement de montrer une technique de programmation qui permet d'auto-analyser son programme, sans avoir recours à un outil externe... J'ai trouvé celà sympatique en le faisant et je me suis pris de l'envie de partager ce bout de code...

    Il est certain que l'analyse du problème avec ollydbg m'a pris beaucoup moins de temps que l'ecriture de ce minitut, mais j'y ai aussi pris moins de plaisir...

    PS... J'ai volontairement mis ce post dans le sous forum freepascal en précisant que c'était sous windows, car une grande partie de ce que je viens d'ecrire n'est pas applicable à des compilateurs 16 bits, et qu'il reste à voir ce que celà peut donner sous d'autres OS...

  10. #10
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 947
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 947
    Points : 5 660
    Points
    5 660
    Par défaut
    Hi,

    Oui, j'avais bien compris, mais je pensais que tu avais en tête une réponse automatique par le programme, genre "les fonctions sont identiques", par comparaison faite par le programme.

    Au temps pour moi, j'ai écrit "les fonctions sont identiques", malgré tout ce que j'en ai di jusqu'ici.
    Donc disons "les codes générés sont identiques"

  11. #11
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    Je viens de faire le test à l'instant sous mon pc sous linux mandriva 2007, compilation avec lazarus...

    J'ai du ajouter simplement la directive spécifiant l'assembleur à utiliser :
    {$ASMMODE intel}

    Voici le résultat :

    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    adresse de fchar : 080480B8
    adresse de fchr : 080480D8
    32 bytes
     
      Dump fchar                  Dump fchr
     
    adresse   byte              adresse   byte
     
    080480B8  55                080480D8  55
    080480B9  89                080480D9  89
    080480BA  E5                080480DA  E5
    080480BB  83                080480DB  83
    080480BC  EC                080480DC  EC
    080480BD  08                080480DD  08
    080480BE  88                080480DE  88
    080480BF  45                080480DF  45
    080480C0  FC                080480E0  FC
    080480C1  8A                080480E1  8A
    080480C2  45                080480E2  45
    080480C3  FC                080480E3  FC
    080480C4  88                080480E4  88
    080480C5  45                080480E5  45
    080480C6  F8                080480E6  F8
    080480C7  8A                080480E7  8A
    080480C8  45                080480E8  45
    080480C9  F8                080480E9  F8
    080480CA  C9                080480EA  C9
    080480CB  C3                080480EB  C3
    080480CC  8D                080480EC  8D
    080480CD  B4                080480ED  B4
    080480CE  26                080480EE  26
    080480CF  00                080480EF  00
    080480D0  00                080480F0  00
    080480D1  00                080480F1  00
    080480D2  00                080480F2  00
    080480D3  8D                080480F3  8D
    080480D4  74                080480F4  74
    080480D5  26                080480F5  26
    080480D6  00                080480F6  00
    080480D7  90                080480F7  90
    Pour Linux aussi donc , freepascal compile le meme code ... Par contre il lui faut 32 bytes pour la meme chose...

  12. #12
    Membre éclairé

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    Points : 773
    Points
    773
    Par défaut
    La fonction fchar s'arrète plus tot que 32 bytes, de manière classique par un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    008048CA  C9   LEAVE
    008048CB  C3   RET
    (voir cliché de la fenetre de désassemblage joint)

    Du code rempli l'intervale entre les deux fonctions
    Le meme code rempli l'intervalle entre la deuxième fonction... et le reste du programme...

    La comparaison aurait pu echouer.... J'ai eu de la chance sur ce coup-ci...
    Images attachées Images attachées  

  13. #13
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 947
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 947
    Points : 5 660
    Points
    5 660
    Par défaut
    Hol,

    Oui, alignement sur 32 bits pour l'entrée d'une fonction, ça fait partie des optimisations pour les processeurs 32 bits.

    Que le code généré soit également le même n'est pas étonnant, on cherche à obtenir le même résultat, et c'est le même compilateur.

    Par contre, que l'alignement soit fait dans la version Linux et pas dans celle pour Windows est un peu plus étonnant.
    Je vais jeter un coup d'oeil.

  14. #14
    Membre confirmé Avatar de Haywire
    Homme Profil pro
    Développeur Java
    Inscrit en
    Mars 2006
    Messages
    462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2006
    Messages : 462
    Points : 573
    Points
    573
    Par défaut
    Merci Clandestino pour ce mini tuto très intéressant .
    Je me pose souvent des questions existentielles dans le même genre donc ça pourra m'être utile .

    ps: Cool c'est mon 200e message et donc ma 1ere étoile !

  15. #15
    Expert confirmé

    Inscrit en
    Août 2006
    Messages
    3 947
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 947
    Points : 5 660
    Points
    5 660
    Par défaut
    Al,

    Vérifié sur Windows : freePascal aligne sur 16 bits !! Hou la honte

    (options: optimisation pour la vitesse, -O2)

Discussions similaires

  1. Réponses: 6
    Dernier message: 11/02/2011, 09h51
  2. Différence entre char ** environ et char * environ[]
    Par al.spec dans le forum Débuter
    Réponses: 1
    Dernier message: 03/10/2010, 16h12
  3. Différence entre char &r='c' et char r='c'
    Par deubelte dans le forum C++
    Réponses: 49
    Dernier message: 09/03/2010, 17h50
  4. différence entre char a[20]="str" et char *a="str"
    Par iBen68 dans le forum Débuter
    Réponses: 4
    Dernier message: 31/10/2009, 03h22

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