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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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 chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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 chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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 967
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 967
    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 chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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 967
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 3 967
    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 chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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...

  8. #8
    Membre chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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...

  9. #9
    Membre chevronné

    Inscrit en
    Avril 2003
    Messages
    284
    Détails du profil
    Informations forums :
    Inscription : Avril 2003
    Messages : 284
    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

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