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

Langage Delphi Discussion :

Nil^ dans GifImage.pas


Sujet :

Langage Delphi

  1. #1
    Membre habitué
    Profil pro
    Inscrit en
    Février 2008
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 141
    Points : 142
    Points
    142
    Par défaut Nil^ dans GifImage.pas
    Bonjour,

    En parcourant le code des librairies installées chez nous, je suis tombé, dans le code de l'unité GifImage.pas, sur la ligne suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      FColors := GetPaletteEntries(Palette, 0, 256, nil^);
    Comprenez ma perplexité ! Trouver cette ligne au sein d'un projet qui compile parfaitement...

    Quel sens peut-on accorder à ce truc? Est-ce licite? Si oui dans quels cas?

    Il est bien possible - je ne suis pas allé vérifier à l'exécution - que cette ligne ne soit jamais exécutée au cours de l'exécution de notre programme.

    Merci d'avance,

  2. #2
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 446
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 446
    Points : 5 867
    Points
    5 867
    Par défaut
    salut

    effectivement c'est déroutant mais nil est une variable pointeur comme une autre

    autre exemple pour que tu comprenne mieux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    pal  : PLogPalette;
       begin
        ...    
       GetPaletteEntries(HPalette,0,0,pal^);
    si tu regarde le code tu t'aperçois que le code est presque le même
    la seul différence est que l'on a déclarer un type pointeur different

    je suppose que syntaxiquement cela est correct
    car getpalletteEntries attend une variable non typé,
    par contre en construction c'est un peu douteux vu que c'est une valeur de retour non typé qui doit être affecter

    @+ Phil

  3. #3
    Membre habitué
    Profil pro
    Inscrit en
    Février 2008
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 141
    Points : 142
    Points
    142
    Par défaut
    Citation Envoyé par anapurna Voir le message
    nil est une variable pointeur comme une autre
    Je suis d'accord avec ce point ; ce à quoi je m'attendais, cependant, c'est que l'instruction "nil^" plante à l'exécution.

    Ce n'est manifestement pas toujours le cas :
    J'ai testé le code suivant, qui essaye cette bidouille avec
    • la constante Nil^
    • LPtr1^, où LPtr1 est un pointeur non typé valant NIL
    • LPtr2^, où LPtr2 est un pointeur, typé, valant NIL


    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
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    procedure TestProc1( out AVar );
    begin
      try
        WriteLn( 'TestProc1 : out');
        if @AVar = NIL
          then Writeln( '  @arg = NIL' )
          else Writeln( '  @arg <> NIL' );
     
        WriteLn(Integer(AVar));
      except
        WriteLn( '  exception caught' );
      end;
    end;
     
    procedure TestProc2( var AVar );
    begin
      try
        WriteLn( 'TestProc2 : var');
        if @AVar = NIL
          then Writeln( '  @arg = NIL' )
          else Writeln( '  @arg <> NIL' );
     
        WriteLn(Integer(AVar));
      except
        WriteLn( '  exception caught' );
      end;
    end;
     
    procedure TestProc3( const AVar );
    begin
      try
        WriteLn( 'TestProc3 : const');
        if @AVar = NIL
          then Writeln( '  @arg = NIL' )
          else Writeln( '  @arg <> NIL' );
     
        WriteLn( Integer(AVar) );
      except
        WriteLn( '  exception caught' );
      end;
    end;
     
    procedure TestProc4( AVar : Integer );
    begin
      try
        WriteLn( 'TestProc4 : argument normal');
        if @AVar = NIL
          then Writeln( '  @arg = NIL' )
          else Writeln( '  @arg <> NIL' );
     
        Integer(AVar) := 1;
      except
        WriteLn( '  exception caught' );
      end;
    end;
     
    procedure TestNilPtr;
      var
        LPtr1 : POINTER;
        LPtr2 : ^Integer;
    begin
      TRY
        TestProc1( NIL^ );
        TestProc2( NIL^ );
        TestProc3( NIL^ );
        TestProc4( Integer(NIL^) );
      EXCEPT
        WriteLn('*** exception rattrapee dans TestNilPtr' );
      END;
     
      LPtr1 := NIL;
      TRY
        TestProc1( LPtr1^ );
        TestProc2( LPtr1^ );
        TestProc3( LPtr1^ );
        TestProc4( Integer(LPtr1^) );
      EXCEPT
        WriteLn('*** exception rattrapee dans TestNilPtr' );
      END;
     
      LPtr2 := NIL;
      TRY
        TestProc1( LPtr2^ );
        TestProc2( LPtr2^ );
        TestProc3( LPtr2^ );
        TestProc4( Integer(LPtr2^) );
      EXCEPT
        WriteLn('*** exception rattrapee dans TestNilPtr' );
      END;
     
      TRY
        TestProc1( LPtr2 );
        TestProc2( LPtr2 );
        TestProc3( LPtr2 );
        TestProc4( Integer(LPtr2) );
      EXCEPT
        WriteLn('*** exception rattrapee dans TestNilPtr' );
      END;
     
    end;
    ça me produit le log suivant (j'exécute dans le main de mon programme la procédure TestNilPtr):
    TestProc1 : out
    @arg = NIL
    exception caught
    TestProc2 : var
    @arg = NIL
    exception caught
    TestProc3 : const
    @arg = NIL
    exception caught
    *** exception rattrapee dans TestNilPtr
    TestProc1 : out
    @arg = NIL
    exception caught
    TestProc2 : var
    @arg = NIL
    exception caught
    TestProc3 : const
    @arg = NIL
    exception caught
    *** exception rattrapee dans TestNilPtr
    TestProc1 : out
    @arg = NIL
    exception caught
    TestProc2 : var
    @arg = NIL
    exception caught
    TestProc3 : const
    @arg = NIL
    exception caught
    *** exception rattrapee dans TestNilPtr
    TestProc1 : out
    @arg <> NIL
    0
    TestProc2 : var
    @arg <> NIL
    0
    TestProc3 : const
    @arg <> NIL
    0
    TestProc4 : argument normal
    @arg <> NIL
    C'est comme si, lorsqu'on passe un paramètre var/const/out non typé, l'instruction NIL^ (ou LPtr^) n'était pas évaluée.

    C'est d'ailleurs à peu près ce que dit l'assembleur :
    au moment du test avec LPtr1, le code assembleur produit est :

    NilPtr.pas.99: TestProc1( LPtr1^ );
    00408F65 8B45FC mov eax,[ebp-$04] //on met la valeur du pointeur sur la pile, mais on ne le déréférence pas
    00408F68 E807FBFFFF call TestProc1
    NilPtr.pas.100: TestProc2( LPtr1^ );
    00408F6D 8B45FC mov eax,[ebp-$04]
    00408F70 E81BFCFFFF call TestProc2
    NilPtr.pas.101: TestProc3( LPtr1^ );
    00408F75 8B45FC mov eax,[ebp-$04]
    00408F78 E82FFDFFFF call TestProc3
    alors que l'appel suivant donne:

    NilPtr.pas.102: TestProc4( PInteger(LPtr1)^ );
    00408F7D 8B45FC mov eax,[ebp-$04]
    00408F80 8B00 mov eax,[eax] //ici, on déréférence
    00408F82 E845FEFFFF call TestProc4
    Est-ce que quelqu'un connait la sémantique propre à donner à "NIL^", où à l'évaluation d'un paramètre non typé passé en const/var/out? Par "propre", je veux dire plus certifiée que mes tests à l'aveugle.

    Dans ce cas, j'ai bêtement essayé de passer des pointeurs explicites ; est-ce un cas où l'on peut passer des arguments sans qu'ils soient évalués?

    Merci,

  4. #4
    Membre chevronné

    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    1 519
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 1 519
    Points : 2 153
    Points
    2 153
    Billets dans le blog
    1
    Par défaut
    Normalement les paramètres de direction out ne sont justement pas évalués et l'aide de Delphi 7 précise que "la mémoire utilisée par le paramètre est immédiatement libérée avant que le contrôle du programme ne passe à la procédure".

  5. #5
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 446
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 446
    Points : 5 867
    Points
    5 867
    Par défaut
    salut


    lorsque que tu ne type pas ton paramettre dans une fonction
    le compilo ne peut pas l'evaluer puisqu'il ne sait pas a l'avance ce que tu va fournir

    le seul avantage à cela c'est que le compilo ne perd pas de cycle a verifie si la variable et du bon type

    @+ PHIL

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    Citation Envoyé par LeGEC Voir le message
    Bonjour,

    En parcourant le code des librairies installées chez nous, je suis tombé, dans le code de l'unité GifImage.pas, sur la ligne suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      FColors := GetPaletteEntries(Palette, 0, 256, nil^);
    Comprenez ma perplexité ! Trouver cette ligne au sein d'un projet qui compile parfaitement...

    Quel sens peut-on accorder à ce truc? Est-ce licite? Si oui dans quels cas?

    Il est bien possible - je ne suis pas allé vérifier à l'exécution - que cette ligne ne soit jamais exécutée au cours de l'exécution de notre programme.

    Merci d'avance,
    Ce type de code est lié à la déclaration de l'API Windows dans Delphi.

    le dernier paramètre de GetPaletteEntries et déclaré en "var PaletteEntries" dans Delphi, donc une variable quelconque. En réalité la fonction de l'API attend un pointeur, en cela "var" est valable car il prend l'adresse de la variable en paramètre. Mais la fonction accepte aussi un pointeur nul, en cela "var" pose un problème.

    Le mot clé "var" en Delphi permet d'éviter l'usage du "@", il est tout indiqué pour une paramètre obligatoire. Dans le cas présent l'auteur de GifImage a recours à un pointeur vide déféré "nil^" dont Delphi prendra l'adresse @(nil^) ... ce qui donne "nil"

  7. #7
    Membre habitué
    Profil pro
    Inscrit en
    Février 2008
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 141
    Points : 142
    Points
    142
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    Le mot clé "var" en Delphi permet d'éviter l'usage du "@", il est tout indiqué pour une paramètre obligatoire. Dans le cas présent l'auteur de GifImage a recours à un pointeur vide déféré "nil^" dont Delphi prendra l'adresse @(nil^) ... ce qui donne "nil"
    D'accord. Ca m'a l'air un peu fumeux comme syntaxe.

    Je viens de m'apercevoir d'un autre cas particulier :

    on connaissait la règle :
    dans le cas d'un paramètre CONST, typé ce coup-ci, si la taille de la variable est inférieure à la taille d'un registre, Delphi passe en fait directement la valeur de la variable - et non pas une référence sur la variable.

    En pratique (toujours pour essayer de comprendre le comportement de Delphi dans ces cas foireux):
    • si je passe "PInteger(NIL)^" à une fonction qui attend un "const AArg : integer" en argument, le programme plante avant l'appel à la fonction - parce que Delphi cherche à déréférencer le pointeur nil pour accéder à la valeur à passer ;
    • si en revanche le type attendu est plus gros, comme par exemple "const AArg : string", PString(NIL)^ passera ...
    • dans le cas de paramètres VAR ou OUT, le compilateur a l'air d'appliquer la bidouille que tu dis, et remplace @(NIL^) par NIL...


    Plus ça va, plus je me demande comment ils spécifient leur langage pour écrire leur compilateur
    Ils l'écrivent directement en assembleur ou quoi?

    Merci en tout cas pour vos précisions, je vais marquer ce sujet comme résolu.

  8. #8
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    Citation Envoyé par LeGEC Voir le message
    D'accord. Ca m'a l'air un peu fumeux comme syntaxe.

    Je viens de m'apercevoir d'un autre cas particulier :

    on connaissait la règle :
    dans le cas d'un paramètre CONST, typé ce coup-ci, si la taille de la variable est inférieure à la taille d'un registre, Delphi passe en fait directement la valeur de la variable - et non pas une référence sur la variable.
    oui enfin c'est surtout le cas inverse qui est intéressant si tu passes un tableau en CONST, Delphi envoie un pointeur car il sait que le tableau ne sera pas modifié...sinon il est obligé d'en faire une copie pour préserver l'original

    Citation Envoyé par LeGEC Voir le message
    En pratique (toujours pour essayer de comprendre le comportement de Delphi dans ces cas foireux):
    • si je passe "PInteger(NIL)^" à une fonction qui attend un "const AArg : integer" en argument, le programme plante avant l'appel à la fonction - parce que Delphi cherche à déréférencer le pointeur nil pour accéder à la valeur à passer ;
    • si en revanche le type attendu est plus gros, comme par exemple "const AArg : string", PString(NIL)^ passera ...
    • dans le cas de paramètres VAR ou OUT, le compilateur a l'air d'appliquer la bidouille que tu dis, et remplace @(NIL^) par NIL...

    ben voila, dans le cas d'un STRING il ne cherchera pas à déférencer la valeur du pointeur mais la placera directement, comme ça la procédure peut adresser tous les caractères de la chaine...dans le cas d'un entier, il serait dommage d'envoyer un pointeur alors que seule la valeur pointée est intéressante; du coup il cherche à lire sa valeur.

    Mais l'astuce "nil^" permet de contourner une déclaration qui contraint l'usage d'une variable alors que la fonction accepte un pointeur qui peut être nul. Tu pourrais aussi redéclarer la fonction comme cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetPaletteEntries(Palette: HPALETTE; StartIndex, NumEntries: UINT;
      PaletteEntries: PPaletteEntry); external 'gdi32.dll';
    auquel cas, nil est acceptable

    Citation Envoyé par LeGEC Voir le message
    Plus ça va, plus je me demande comment ils spécifient leur langage pour écrire leur compilateur
    Ils l'écrivent directement en assembleur ou quoi?
    ben le but de Delphi est tout de même de produire du code machine à la fin

  9. #9
    Membre habitué
    Profil pro
    Inscrit en
    Février 2008
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 141
    Points : 142
    Points
    142
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    Tu pourrais aussi redéclarer la fonction comme cela :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetPaletteEntries(Palette: HPALETTE; StartIndex, NumEntries: UINT;
      PaletteEntries: PPaletteEntry); external 'gdi32.dll';
    auquel cas, nil est acceptable
    Tu as raison, c'est peut-être ça que je trouve choquant : Delphi propose une interface "jolie" de fonctions des dll windows, qui essaye de masquer l'utilisation des pointeurs, puis se retrouve à créer des artefacts syntaxiques, que je trouve pour le moins douteux, pour permettre de contourner ses propres embellissements...

  10. #10
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    Citation Envoyé par LeGEC Voir le message
    Tu as raison, c'est peut-être ça que je trouve choquant : Delphi propose une interface "jolie" de fonctions des dll windows, qui essaye de masquer l'utilisation des pointeurs, puis se retrouve à créer des artefacts syntaxiques, que je trouve pour le moins douteux, pour permettre de contourner ses propres embellissements...
    oui et non, le mot clé "var" n'existe pas en C, je le trouve très pratique...et dans 90% des cas tu veux connaitre les valeurs des couleurs en même temps que leur nombre...mais au cours des versions de Delphi il y a eu quelques ajustements comme ça ou des paramètres "var" ont disparu au profit des pointeur pour permettre le nil.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Variable surlignée dans PhpEclipse, pas dans PDT
    Par budtucker dans le forum Eclipse PHP
    Réponses: 1
    Dernier message: 27/09/2007, 12h57
  2. comment insérer une image dans mon .pas
    Par korntex5 dans le forum Langage
    Réponses: 3
    Dernier message: 04/05/2006, 12h54
  3. Erreur JS dans IE pas firefox
    Par zevince dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 02/03/2006, 18h22

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