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 :

BUG dans le langage Delphi


Sujet :

Langage Delphi

  1. #21
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Merci sjrd pour ce lien. En fait quand j'y pense je suis tombé un peu dans le pire des cas de figure... car j'avais à la base une violation d'accès pour une rautre raison mais assez difficile à trouver, en effet j'ai du ajouter pas mal de code de log pour retrouver la source du fait que j'ai méthodes de frames construites dynamiquement qui appellent des callback (méthodes appartenant à l'instance appelante étant aussi le parent) qui reconstruisent et rapellent elles-même récursivement mais indirectement les méthodes de ces frames... enfin bref c'est assez complexe; et là où c'est tordu qu'après avoir corriger mon fameux bug je me retrouvais encore avec des problèmes de violations d'accès aléatoires qui étaient du cette fois au code de log que j'avais rajouté pour débuger le premier problème !
    Et ces violations d'accès aléatoires étaient du à un string passé en const qui devenait "invalide" après avoir appelé une méthode callback, c'est là où le lien n'explique pas tout. Pourquoi la zone mémoire devient-elle invalide alors que je recopie une chaîne de caractères de même taille, il pourrait s'agir de la même zone mémoire, comme lorsque je passe la string en 'var'....

    Enfin toujours est-il que j'ai compris et que dorénavant je ferais attention à cela.

    Paul TOTH> En fait je sais bien que dans 99% des cas (sinon plus) la référence ne sert à rien. En fait l'idéale serait que le compilateur au moment de la compilation ait un mécanisme qui détecte qu'une modification de la constante peut-être possible, et dans ce cas garder une référence en précisant le type d'accès. Ainsi il n'y aurait copie que s'il y a réellement modification de la variable pointée alors que le scope de l'accès en const est toujours actif... Après effectivement ce type de mécanisme est complexe à réaliser (algo d'optimisation de code) mais au moins on aurait un langage pour ainsi dire plus "safe". Je n'ai peut-être pas été très clair quand je disais qu'il n'est pas logique que la correction du bug se fait en retirant "const", en fait je sais moi pourquoi cela corrige le problème car je sais en interne ce que fait le compilateur. Mais quand je dis d'un point de vue algorithmique j'entend pas là en rapport avec le descriptif de la doc. de Delphi où 'const' n'est censé qu'indiquer que le fait que la variable ne pourra être modifiée dans la méthode et que cela permet au compilateur "d'optimiser le code". Un développeur qui ne connait pas le fonctionnement interne du Delphi (ce qui n'est normalement pas utile) ne saura pas expliquer ce comportement, voilà ce que je trouve illogique (même si l'expérience m'a appris depuis longtemps qu'un bon codeur doit connaitre le fonctionnement interne du compilo).

    Ton exemple est amusant et je n'ai pas Delphi sous la main pour tester, et j'ignore ce que cela renvoie au second appel, j'imagine "TObject" car 'ClassName' est utilisable comme sur une classe et non pas seulement une instance mais j'en suis pas sûr. Par contre je trouve qu'avec un Txxx le problème est plus simple à apréhender car tout développeur sait tout de même que les objets sont manipulés qu'avec des pointeurs en Delphi, alors qu'avec un "string", type dynamique interne au compilateur c'est déjà plus inattendu comme problème (car on ne détruit pas explicitement la chaîne de caractères)

  2. #22
    Membre habitué Avatar de phplive
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2003
    Messages : 179
    Points : 150
    Points
    150
    Par défaut
    bjr

    Citation Envoyé par ZZZzzz2 Voir le message
    En fait l'idéale serait que le compilateur au moment de la compilation ait un mécanisme qui détecte qu'une modification de la constante peut-être possible, et dans ce cas garder une référence en précisant le type d'accès. Ainsi il n'y aurait copie que s'il y a réellement modification de la variable pointée alors que le scope de l'accès en const est toujours actif...
    Cela nécessiterait une compréhension en profondeur du programme qui est hors de porter du compilateur ... Si tu variables risque d'être modifiée utilise var au moins là tu es sûr que son compteur de référence sera incrémenté (pour les ansistring et widestring)

    Maintenant c'est vrai que le type string est une sorte d'hybride tantôt shortstring tantôt ansistring : j'aurais préféré un véritable objet TString dérivé de TInterfacedObject par ex plus quelques fonctions "magiques" au niveau du compilo pour autoriser la manipulation de cet objet comme un string actuel.


    Remarque y'a pas qu'avec les strings qu'on peut avoir ce genre d'effet de bord : avec les interfaces aussi

    Ex :

    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
    101
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      ITest = interface
      ['{CD383E23-C08B-420C-997A-B9B764A50A76}']
        function GetText : String;
        procedure SetText(const Value : String);
     
        property Text : String read GetText write SetText;
      end;
     
      TTest = class(TInterfacedObject,ITest)
      private
        FText : String;
      protected
        function GetText : String;
        procedure SetText(const Value : String);
      public
        constructor Create(const Text : String);
        property Text : String read GetText write SetText;
      end;
     
     
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    { TTest }
     
    constructor TTest.Create(const Text: String);
    begin
      FText := Text;
    end;
     
    function TTest.GetText: String;
    begin
      Result := FText;
    end;
     
    procedure TTest.SetText(const Value : String);
    begin
      FText := Value;
    end;
     
     
    procedure ShowText1(const Test : ITest);
    begin
      ShowMessage('ShowText1 Test.Text='+Test.Text);
    end;
     
     
     
    procedure ShowText2(const Test : ITest);
    var
      Item : ITest;
    begin
      // Affecte une référence vers Test
      Item := Test;
      ShowMessage('ShowText2 Item.Text='+Item.Text);
      // Libère la référence vers Test
      Item := nil;
     
     
      ShowMessage('ShowText2 Test.Text='+Test.Text);
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      Obj : ITest;
    begin
      // Tout est OK
      ShowText1(TTest.Create('ESSAI1'));
     
      Obj := TTest.Create('ESSAI2');
      // Tout est OK
      ShowText2(Obj);
     
      // Violation d'accès car la variable const Test est détruite prématurément
      // dans ShowTest2
      ShowText2(TTest.Create('ESSAI3'));
    end;
     
    end.
    Amusant non ?

  3. #23
    Membre éprouvé Avatar de Yurck
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    682
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 15
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2005
    Messages : 682
    Points : 912
    Points
    912
    Par défaut
    Bien j'ai pris le temps de lire et vos messages et les liens qui y étaient attachés.

    Je ne vois toujours pas comment on peut dire qu'il ne s'agit pas là d'un bogue.

    Je ne parle pas du fait que l'on puisse modifier le contenu d'un objet transmis en const car là il n'y a pas d'ambiguité c'est normal, mais le fait qu'il n'affiche pas 'Message2' me surprend au plus haut point.

    Je vais prendre le temps de vous re re re lire.

    bonne journée

  4. #24
    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 448
    Points
    28 448
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    Ton exemple est amusant et je n'ai pas Delphi sous la main pour tester, et j'ignore ce que cela renvoie au second appel, j'imagine "TObject" car 'ClassName' est utilisable comme sur une classe et non pas seulement une instance mais j'en suis pas sûr. Par contre je trouve qu'avec un Txxx le problème est plus simple à apréhender car tout développeur sait tout de même que les objets sont manipulés qu'avec des pointeurs en Delphi, alors qu'avec un "string", type dynamique interne au compilateur c'est déjà plus inattendu comme problème (car on ne détruit pas explicitement la chaîne de caractères)
    en fait l'exemple pourrait s'écrire plus simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    begin
     ShowMessage(obj.ClassName);
     obj.Free;
     ShowMessage(obj.ClassName);
    end;
    car un objet constant sous Delphi reste modifiable, c'est juste la variable pointeur qui est protégée (l'intérêt est donc très limité )

    Dans le cas d'un string const la procédure ne peux pas modifier la chaîne, mais celle-ci n'est pas protégée pour autant comme le montre ton exemple...à la réflexion le compilateur pourrait probablement incrémenter le pointeur de référence pour résoudre ce problème...et le décrémenter au sortir de la procédure; ça ne pénaliserait pas vraiment l'exécution et ça garantirait que cette version de la chaîne soit conservée malgré l'effet callback

    d'ailleurs le callback n'est pas nécessaire pour montre le bug
    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
     
    var
     s: string;
     
    procedure TForm1.bug;
    begin
     s:='autre chose';
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
     s:='quelque chose';
     test(s);
    end;
     
    procedure TForm1.test(const s: string);
    begin
     ShowMessage(s);
     bug;
     ShowMessage(s);
    end;
    et voici un correctif très simple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
     safe:string;
    begin
     s:='quelque chose';
     safe:=s;
     test(s);
    end;
    Et on constate dans ce cas que c'est bien la valeur initiale qui est conservée...ce qui est un autre effet de bord

  5. #25
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 577
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 577
    Points : 25 225
    Points
    25 225
    Par défaut
    Citation Envoyé par Yurck Voir le message
    Je ne vois toujours pas comment on peut dire qu'il ne s'agit pas là d'un bogue.
    Il faudrait revenir à ce qu'est une string !!! un pointeur avant tout !
    Il faut juste décortiquer le process
    Je suis de l'avis de PHPLive et CapJack, c'est l'implicite du langage qui peu amener un programmeur a faire une erreur flagrante qu'il ne ferait pas si il avait la conscience forte de manipuler des pointeurs ... faisant en ce moment des DLL, je me rend bien compte de ce qu'est une réallocation de chaine (Voir ICI mon sujet sur les PChar et la durée de vie dans une DLL)

    Si l'on modifie en cours la valeur passé à S, c'est à dire FText , il n'y a en rien un bogue, il y a juste une erreur de programmation ou le contournement sauvage de certaines règles, car si l'on modifie finalement la mémoire pointée par FText, cela réalloue une nouvelle chaine dans FText, or S pointe sur l'ancienne zone mémoire !C'est juste du grand n'importe quoi ! Si l'on manipulait du PChar, avec des Malloc manuel ça se verrait tout de suite que c'est abhérant !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    procedure TForm1.TestShow;
    begin
       SetText( 'Message1' );
       FTest.Show( FText, SetText );
       ShowMessage(FText); // Affiche Message2 !!! 
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    procedure TTest.Show( const AText: string; SetParentText: TTestEvent);
    begin
       ShowMessage( AText );
       SetParentText( 'Message2' ); // AText et FText ne pointe plus sur la même mémoire !
       ShowMessage( AText ); // Ici, on pourrait avoir une VA effectivement !
    end;

    Pour les interfaces, en général, il est inutile de les transmettre par const car il n'y a pas de duplication (et que de toute façon c'est la référence de l'interface qui sera protégée et non ses propriétés !) et rappelons que l'intéret du const avec les string c'est évité la duplication des variables locales (avec les globales c'est le compteur qui joue) en plus de la notion de "protection" d'écriture ... en général, je ne m'en préoccupe pas spécialement ...

  6. #26
    Membre habitué Avatar de phplive
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2003
    Messages : 179
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Pour les interfaces, en général, il est inutile de les transmettre par const car il n'y a pas de duplication
    Sauf pour des raisons de performances : car avec const le compilateur n'appelle plus les méthodes _AddRef() et _Release() et ne fait plus de test pour savoir s'il doit libérer l'objet (si on gère la libération de l'objet lorsque le compteur de réf arrive à zéro) ce qui sur des boucles assez importantes se ressent qd même.

    Maintenant c'est clair que la sécurité c'est de bannir const : c'est plus lent (enfin c'est relatif) mais c'est plus sûr (c'est certain)

  7. #27
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    en fait l'exemple pourrait s'écrire plus simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    begin
     ShowMessage(obj.ClassName);
     obj.Free;
     ShowMessage(obj.ClassName);
    end;
    car un objet constant sous Delphi reste modifiable, c'est juste la variable pointeur qui est protégée (l'intérêt est donc très limité )

    Dans le cas d'un string const la procédure ne peux pas modifier la chaîne, mais celle-ci n'est pas protégée pour autant comme le montre ton exemple...à la réflexion le compilateur pourrait probablement incrémenter le pointeur de référence pour résoudre ce problème...et le décrémenter au sortir de la procédure; ça ne pénaliserait pas vraiment l'exécution et ça garantirait que cette version de la chaîne soit conservée malgré l'effet callback

    d'ailleurs le callback n'est pas nécessaire pour montre le bug
    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
     
    var
     s: string;
     
    procedure TForm1.bug;
    begin
     s:='autre chose';
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
     s:='quelque chose';
     test(s);
    end;
     
    procedure TForm1.test(const s: string);
    begin
     ShowMessage(s);
     bug;
     ShowMessage(s);
    end;
    et voici un correctif très simple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
     safe:string;
    begin
     s:='quelque chose';
     safe:=s;
     test(s);
    end;
    Et on constate dans ce cas que c'est bien la valeur initiale qui est conservée...ce qui est un autre effet de bord

    Je crois avoir compris un truc : la chaîne de caractère et son compteur de référence reste toujours unique en mémoire (il n'y a donc jamais de copie de ces éléments), seul les références sont dupliqués. Du coup comme il reste une référence à la sortie de la méthode l'ancien contenu n'est pas perdu. Ce que je comprend moins en revanche c'est que les références de la variable globale et celle passé en 'const' à la méthode sont visiblement différentes. Comme quoi le Delphi doit bien faire quelquechose au niveau des références quand on passe un argument en 'const'... mais alors pourquoi celui-ci ne voit-il pas qu'il reste une référence sur la zone mémoire sans devoir en ajouter une autre explicitement. C'est précisément ce que je n'explique pas, et ce qui a entrainé mes erreurs dans mon appli.

  8. #28
    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 448
    Points
    28 448
    Par défaut
    Citation Envoyé par ZZZzzz2 Voir le message
    Je crois avoir compris un truc : la chaîne de caractère et son compteur de référence reste toujours unique en mémoire (il n'y a donc jamais de copie de ces éléments), seul les références sont dupliqués.
    je ne suis pas sur d'avoir compris ce que tu as dit

    en fait un string c'est une structure qui contient 3 éléments. Un compteur d'usage, une longeur, des caractères et un 0 terminal...donc 4 éléments

    la variable de type string est un pointeur sur le 3ième élément (les caractères), à l'offset -4 et -8 on trouve le compteur de référence et la taille...le 0 terminal est en fin de chaine évidemment.

    quand on affecte à une variable string une autre string, le compteur de référence de la première est décrémenté (sauf si la chaine est vide = nil), si ce compter tombe à zéro, la zone mémoire est libérée, puis le compteur de la seconde chaine est incrémenté et les deux variables pointent sur la même zone mémoire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    var
     s1,s2:string;
    begin
    // s1 = s2 = nil
     s1:='hello';
    // si pointe sur le "h" qui est précédé de l'entier 6 soit  "length(s1)" et de 1 le compteur de référence
     s2:=s1;
    // s2 et s1 pointent tous les deux sur le "h" et le compteur passe à 2
     s1[2]:='a';
    // vu que le compteur de rérérence est > 0, Delphi crée une nouvelle chaine sur laquelle s1 va pointer tandis que le compteur de référence de la chaine pointée par s2 passe à 1
    // la nouvelle chaine pointe sur une chaine avec compteur à 1, length à 6 et les caractères "hallo"
    end;
    ce code explique pourquoi le fait d'utiliser "safe" force le passage du compteur de référence à 2, et du coup la modification de "s" ne fait que décrémenter le compteur en allouant une nouvelle chaine pour "s". L'ancienne adresse de "s" qui est égale à "safe" est toujours valide dans le paramètre de la fonction.

    sans "safe", le compteur est à 1, la chaine est libérée et une nouvelle est créée pour contenir le nouveau texte....le paramètre pointe sur une ancienne adresse qui n'est plus un string.

    Citation Envoyé par ZZZzzz2 Voir le message
    Du coup comme il reste une référence à la sortie de la méthode l'ancien contenu n'est pas perdu. Ce que je comprend moins en revanche c'est que les références de la variable globale et celle passé en 'const' à la méthode sont visiblement différentes.
    le compteur est à la même adresse que la chaine (enfin 4 octets avant en mémoire - ou 8 je ne sais jamais dans quel sens sont le compteur et la taille ) donc quand on modifie "s", on change l'adresse sur laquelle il pointe, tandis que l'adresse du paramètre ne change pas.



    Citation Envoyé par ZZZzzz2 Voir le message
    Comme quoi le Delphi doit bien faire quelquechose au niveau des références quand on passe un argument en 'const'... mais alors pourquoi celui-ci ne voit-il pas qu'il reste une référence sur la zone mémoire sans devoir en ajouter une autre explicitement. C'est précisément ce que je n'explique pas, et ce qui a entrainé mes erreurs dans mon appli.
    c'est justement parcequ'il ne fait rien que la chaine n'est pas conservée

  9. #29
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    je ne suis pas sur d'avoir compris ce que tu as dit

    en fait un string c'est une structure qui contient 3 éléments. Un compteur d'usage, une longeur, des caractères et un 0 terminal...donc 4 éléments

    la variable de type string est un pointeur sur le 3ième élément (les caractères), à l'offset -4 et -8 on trouve le compteur de référence et la taille...le 0 terminal est en fin de chaine évidemment.

    quand on affecte à une variable string une autre string, le compteur de référence de la première est décrémenté (sauf si la chaine est vide = nil), si ce compter tombe à zéro, la zone mémoire est libérée, puis le compteur de la seconde chaine est incrémenté et les deux variables pointent sur la même zone mémoire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    var
     s1,s2:string;
    begin
    // s1 = s2 = nil
     s1:='hello';
    // si pointe sur le "h" qui est précédé de l'entier 6 soit  "length(s1)" et de 1 le compteur de référence
     s2:=s1;
    // s2 et s1 pointent tous les deux sur le "h" et le compteur passe à 2
     s1[2]:='a';
    // vu que le compteur de rérérence est > 0, Delphi crée une nouvelle chaine sur laquelle s1 va pointer tandis que le compteur de référence de la chaine pointée par s2 passe à 1
    // la nouvelle chaine pointe sur une chaine avec compteur à 1, length à 6 et les caractères "hallo"
    end;
    ce code explique pourquoi le fait d'utiliser "safe" force le passage du compteur de référence à 2, et du coup la modification de "s" ne fait que décrémenter le compteur en allouant une nouvelle chaine pour "s". L'ancienne adresse de "s" qui est égale à "safe" est toujours valide dans le paramètre de la fonction.

    sans "safe", le compteur est à 1, la chaine est libérée et une nouvelle est créée pour contenir le nouveau texte....le paramètre pointe sur une ancienne adresse qui n'est plus un string.



    le compteur est à la même adresse que la chaine (enfin 4 octets avant en mémoire - ou 8 je ne sais jamais dans quel sens sont le compteur et la taille ) donc quand on modifie "s", on change l'adresse sur laquelle il pointe, tandis que l'adresse du paramètre ne change pas.





    c'est justement parcequ'il ne fait rien que la chaine n'est pas conservée
    Mais alors en quoi le fait de passer 'const' fait-il gagner du temps au compilateur ? car d'après la doc. lorsqu'on passe un argument sans préciser 'const' le compilateur "copie" le string, il faut comprendre par là qu'il ne fait rien d'autre que d'augmenter de 1 le compteur de référence sur cette chaîne en entrant dans la fonction, et décrémenter de 1 en sortant, d'où finalement une "surcharge" CPU presque négligeable par rapport à une vraie copie de string. Dans ce cas quel intérêt y-a-t-il à utiliser "const" si ce n'est de risquer d'avoir des problèmes ? Ce qui me surprend c'est qu'il me semble pourtant que durant certains traitements récursifs le gain apporté par le passage à 'const' était franchement sensible...

    Hum, ça remet en cause mes croyances... ne faut-il pas simplement bannir l'utilisation du "const" dans ce cas (sauf que cela permet de s'assurer qu'on ne modifie pas inutilement le string passé en argument et risquer ainsi de plomber les perfs) ? Y-a-t-il réellement des cas où le compilateur gagne en utilisant 'const' lorsqu'on ne modifie pas le string dans la méthode où il est passé en argument ?

  10. #30
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Il faudrait revenir à ce qu'est une string !!! un pointeur avant tout !
    Il faut juste décortiquer le process
    Je suis de l'avis de PHPLive et CapJack, c'est l'implicite du langage qui peu amener un programmeur a faire une erreur flagrante qu'il ne ferait pas si il avait la conscience forte de manipuler des pointeurs ... faisant en ce moment des DLL, je me rend bien compte de ce qu'est une réallocation de chaine (Voir ICI mon sujet sur les PChar et la durée de vie dans une DLL)

    Si l'on modifie en cours la valeur passé à S, c'est à dire FText , il n'y a en rien un bogue, il y a juste une erreur de programmation ou le contournement sauvage de certaines règles, car si l'on modifie finalement la mémoire pointée par FText, cela réalloue une nouvelle chaine dans FText, or S pointe sur l'ancienne zone mémoire !C'est juste du grand n'importe quoi ! Si l'on manipulait du PChar, avec des Malloc manuel ça se verrait tout de suite que c'est abhérant !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    procedure TForm1.TestShow;
    begin
       SetText( 'Message1' );
       FTest.Show( FText, SetText );
       ShowMessage(FText); // Affiche Message2 !!! 
    end;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    procedure TTest.Show( const AText: string; SetParentText: TTestEvent);
    begin
       ShowMessage( AText );
       SetParentText( 'Message2' ); // AText et FText ne pointe plus sur la même mémoire !
       ShowMessage( AText ); // Ici, on pourrait avoir une VA effectivement !
    end;

    Pour les interfaces, en général, il est inutile de les transmettre par const car il n'y a pas de duplication (et que de toute façon c'est la référence de l'interface qui sera protégée et non ses propriétés !) et rappelons que l'intéret du const avec les string c'est évité la duplication des variables locales (avec les globales c'est le compteur qui joue) en plus de la notion de "protection" d'écriture ... en général, je ne m'en préoccupe pas spécialement ...

    Excuse-moi mais si on code en Delphi c'est justement pour ne pas revenir à l'âge de pierre à coder avec des 'char*'... j'ai commencé en C ce qui ne m'empêche pas d'être surpris par ce comportement, les pointeurs c'est uen chose (que beaucoup de monde connait ici), mais les compteurs de référence une autre qu'il ne faut pas oublier non plus...

    Que veux-tu dire avec les duplications de variables locales ? les string qu'elles soient locales ou non sont gérées avec des compteurs de référence je suppose...

  11. #31
    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 448
    Points
    28 448
    Par défaut
    pour voir les conséquences il suffit d'afficher le code assembleur

    voici ce que ça donne sous Delphi 5

    ... en fait c'est dans la procédure que se situe la différence, la chaine étant devenue locale, Delphi insère une gestion d'exception pour gérer la durée de vie de la chaine

    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
     
    procedure pconst(const s:string);
    begin
     // ret
    end;
     
    procedure pvar(var s:string);
    begin
    // ret
    end;
     
    procedure p(s:string);
    begin
    // push ebp
    // mov ebp,esp
    // push ebp
    // push @1
    // push dword ptr fs:[eax]
    // mov fs:[eax],esp
    // xor eax,eax
    // pop edx
    // pop ecx
    // pop ecx
    // mov fs:[eax],edx
    // push @2
    // ret
    // @1:
    // jmp @HandleFinally
    // jmp p + $1E
    // @2
    // pop ebp
    // ret
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
     s:string;
    begin
     s:='hello';
     pconst(s);
    // mov eax,[ebp-4]
    // call pconst
     pvar(s);
    // lea eax,[ebp-4]
    // call pvar
     p(s);
    // mov eax,[ebp-4]
    // call p
    end;
    on voit aussi qu'en mode "var" c'est @s qui est envoyé en paramètre, du coup la chaine peut changer, être réallouée, on s'en fiche, car on pointera toujours sur l'adresse qui est pointée par s

    alors qu'en mode "const" on envoie "@s^" si je puis dire, donc l'adresse pointée par s, soit une copie de sa valeur. Du coup, si on modifie "s", le paramètre reste avec l'ancienne valeur.

    ce n'est donc pas une question de compteur de référence sur ce coup là

    une conséquence du mode "const" aussi c'est que l'accès à la chaine se fait par un pointeur direct alors qu'en mode "var" on a un pointeur sur un pointeur

    on peut également mettre cela en évidence par le code assembleur
    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
     
    var
      i: Integer;
     
    procedure pconst(const s:string);
    begin
      i := ord(s[1]);
    // movzx eax,[eax] -> accès direct via le "pointer" eax
    // mov[i],eax
    // ret
    end;
     
    procedure pvar(var s:string);
    begin
      i := ord(s[1]);
    // mov eax,[eax] -> lecture du pointeur pointé par eax
    // movzx eax,[eax] -> lecture de la chaine via ce pointeur
    // mov[i],eax
    // ret
    end;

  12. #32
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    pour voir les conséquences il suffit d'afficher le code assembleur

    voici ce que ça donne sous Delphi 5

    ... en fait c'est dans la procédure que se situe la différence, la chaine étant devenue locale, Delphi insère une gestion d'exception pour gérer la durée de vie de la chaine

    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
     
    procedure pconst(const s:string);
    begin
     // ret
    end;
     
    procedure pvar(var s:string);
    begin
    // ret
    end;
     
    procedure p(s:string);
    begin
    // push ebp
    // mov ebp,esp
    // push ebp
    // push @1
    // push dword ptr fs:[eax]
    // mov fs:[eax],esp
    // xor eax,eax
    // pop edx
    // pop ecx
    // pop ecx
    // mov fs:[eax],edx
    // push @2
    // ret
    // @1:
    // jmp @HandleFinally
    // jmp p + $1E
    // @2
    // pop ebp
    // ret
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
     s:string;
    begin
     s:='hello';
     pconst(s);
    // mov eax,[ebp-4]
    // call pconst
     pvar(s);
    // lea eax,[ebp-4]
    // call pvar
     p(s);
    // mov eax,[ebp-4]
    // call p
    end;
    on voit aussi qu'en mode "var" c'est @s qui est envoyé en paramètre, du coup la chaine peut changer, être réallouée, on s'en fiche, car on pointera toujours sur l'adresse qui est pointée par s

    alors qu'en mode "const" on envoie "@s^" si je puis dire, donc l'adresse pointée par s, soit une copie de sa valeur. Du coup, si on modifie "s", le paramètre reste avec l'ancienne valeur.

    ce n'est donc pas une question de compteur de référence sur ce coup là

    une conséquence du mode "const" aussi c'est que l'accès à la chaine se fait par un pointeur direct alors qu'en mode "var" on a un pointeur sur un pointeur

    on peut également mettre cela en évidence par le code assembleur
    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
     
    var
      i: Integer;
     
    procedure pconst(const s:string);
    begin
      i := ord(s[1]);
    // movzx eax,[eax] -> accès direct via le "pointer" eax
    // mov[i],eax
    // ret
    end;
     
    procedure pvar(var s:string);
    begin
      i := ord(s[1]);
    // mov eax,[eax] -> lecture du pointeur pointé par eax
    // movzx eax,[eax] -> lecture de la chaine via ce pointeur
    // mov[i],eax
    // ret
    end;


    Eh bien, tu as ainsi presque explicité tout ce qui se passe. J'y vois un peu plus clair même si j'ai du mal à comprendre le fait que le "string" devient locale plutôt que juste d'incrémenter le compteur de référence de la chaîne au début de la procédure, et de décrémenter à la fin de la procédure. A quoi correspond en fait "cette gestion de variable locale" ? Ce que je comprend par contre c'est que c'est qu'il y a déjà ce traitement supplémentaire par rapport à un argument passé en 'const', mais s'il ne s'agit que de ça la surcharge CPU n'est vraiment pas énorme par rapport à l'utilisation de 'const'.

    Pour 'var' il fonctionne exactement comme attendu finalement.

    En fait pour parfaitement comprendre tu aurais du séparer dans la gestion de la variable local ce qui s'exécute au début et ce qui s'exécute à la fin de la procédure.

  13. #33
    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 448
    Points
    28 448
    Par défaut
    le string "devient local" puisque les modifications de sa valeur restent locale

    dans mon code, la variable locale n'étant pas modifiée, rien ne se passe...mais si on venait la modifier, Delphi en ferait une copie locale qui serait automatiquement libérée en fin de procédure...d'où la gestion d'exception locale.

  14. #34
    Membre habitué Avatar de phplive
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2003
    Messages : 179
    Points : 150
    Points
    150
    Par défaut
    Bjr

    Citation Envoyé par ZZZzzz2 Voir le message
    la chaîne de caractère et son compteur de référence reste toujours unique en mémoire.
    Disons que la châine effectivement reste inchangée mais le compteur de réf évolue.

    Citation Envoyé par ZZZzzz2 Voir le message
    (il n'y a donc jamais de copie de ces éléments), seul les références sont dupliqués.
    oui


    Citation Envoyé par ZZZzzz2 Voir le message
    Du coup comme il reste une référence à la sortie de la méthode l'ancien contenu n'est pas perdu.
    Oui

    Citation Envoyé par ZZZzzz2 Voir le message
    Ce que je comprend moins en revanche c'est que les références de la variable globale et celle passé en 'const' à la méthode sont visiblement différentes.
    Non c'est bien la même adresse qui est passée au paramètre

    Citation Envoyé par ZZZzzz2 Voir le message
    Comme quoi le Delphi doit bien faire quelquechose au niveau des références quand on passe un argument en 'const'...
    Non hormis le fait de ne pas modifier le compteur de références avec les chaînes longues

    Citation Envoyé par ZZZzzz2 Voir le message
    mais alors pourquoi celui-ci ne voit-il pas qu'il reste une référence sur la zone mémoire sans devoir en ajouter une autre explicitement.
    Parce que justement on lui dit explicitement avec const de ne pas se souvenir des références




    Lorsqu'on affecte une chaîne codée en dur à S avec l'instruction suivante :

    S := 'quelque chose';

    on obtient une structure qui ressemble à :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                 |               |               |                           |
                 |0 0 0 0 0 0 0 1|0 0 0 0 0 0 0 D|q u e l q u e   c h o s e 0|
                 |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
     
                 |_______________|_______________|___________________________|
                   Compteur Réf       Longueur     Chaîne + zéro terminal
                     = 1           = 13 = #D     ^
                                                 |
                                                 |
    S -> @000000010 _____________________________|
    S contient un pointeur vers l'adresse @0000010 (par ex) qui contient la structure ci-dessus en mémoire allouée par Delphi.




    Si maintenant on affecte une nouvelle chaîne codée en dur à S,

    S := 'autre chose';

    il faut bien comprendre que Delphi ne modifie pas directement le texte dans la structure pointée !


    A la place il créé une nouvelle structure en mémoire à une nouvelle adresse avec un compteur de réf à 1.
    Il décrémente la compteur de réf de l'ancienne adresse : s'il passe à zéro Delphi libère la mémoire allouée.
    Donc le pointeur de S change.



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                 |               |               |                       |
                 |0 0 0 0 0 0 0 1|0 0 0 0 0 0 0 B|a u t r e   c h o s e 0|
                 |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
     
                 |_______________|_______________|_______________________|
                   Compteur Réf       Longueur     Chaîne + zéro terminal
                      = 1          = 11 = #B     ^
                                                 |
                                                 |
    S -> @00000200 ______________________________|

    C'est d'ailleurs facilement vérifiable en utilisant la fonction :


    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
    function GetStrInfo(Name : ansistring; Ptr : Pointer) : string;
    begin
      if Ptr <> nil then
      begin
        Result := Format('Nom : %s  Adresse : %p  Taille : %d  RefCount : %p',[
          Name,
          Ptr,
          PInteger(integer(Ptr)-4)^,
          pointer(PInteger(integer(Ptr)-8)^)]);
      end
      else
      begin
        Result := Name+' : chaîne vide';
      end;
    end;
    Que tu peux utiliser 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
    var
      s : String;
     
    procedure Test;
    var
      infoavant, infoapres : String;
    begin  
      s := 'une chose';
      infoavant := GetStrInfo('s',Pointer(s));
     
      s := 'une chose';
      infoapres := GetStrInfo('s',Pointer(s));
     
      s := '';
    end;

  15. #35
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Merci phph, en fait j'avais compris tout cela avec le code assembleur que nous a donné "Paul TOTH" Mais là je crois qu'on ne peut pas faire plus clair ;-). Le seul truc qui reste un peu flou pour moi c'est le coup de la variable locale à 'gérer en plus' lorsqu'on passe un argument sans préciser 'const', j'entend bien qu'elle est locale à la fonction et que si on la modifie on ne modifie pas le string d'origine, mais on aurait eu le même résultat si on avait simplement incrémenté le compteur de référence lors du passage (ou en entrant dans la méthode) et décrémenté en sortant de la méthode ? Alors qu'est-ce qui change, peut-être est-ce là le mécanisme de gestion (durée de vie, etc...) des variables de Delphi...

  16. #36
    Membre éprouvé
    Avatar de CapJack
    Homme Profil pro
    Prof, développeur amateur vaguement éclairé...
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Prof, développeur amateur vaguement éclairé...
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 988
    Points
    988
    Par défaut
    Citation Envoyé par ZZZzzz2
    Ce que je comprend moins en revanche c'est que les références de la variable globale et celle passé en 'const' à la méthode sont visiblement différentes.
    Citation Envoyé par phplive Voir le message
    Non c'est bien la même adresse qui est passée au paramètre
    Je suis d'accord avec tout le reste, mais pas avec ça : comme l'a fort clairement expliqué Paul TOTH, avec un var c'est par définition l'adresse de la variable chaîne qui est transmis (@a), donc un pointeur sur un pointeur sur l'adresse du descripteur, alors qu'avec const, c'est le contenu de la variable chaîne (@a^), donc directement le pointeur sur le descripteur, qui est transmis.

    Ce qui explique qu'il n'y ait pas de différence notable de performance, car ce n'est pas une instruction assembleur de plus qui doit se sentir.

    Par contre entre const et "pas-const", la différence vient sans doute du fait que dans le premier cas le compilateur ne fait rien, alors que dans le deuxième cas le garbage collector est fortement sollicité, et les séquences de caractères copiées, en cas d'affectation...

    PS : je m'aperçois que je n'étais pas très clair dans mes interventions. En plus, j'avais oublié qu'il y avait aussi la longueur dans le descripteur. C'est vieux, tout ça.

  17. #37
    Membre habitué Avatar de phplive
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2003
    Messages : 179
    Points : 150
    Points
    150
    Par défaut
    Citation Envoyé par CapJack Voir le message
    Je suis d'accord avec tout le reste, mais pas avec ça : comme l'a fort clairement expliqué Paul TOTH, avec un var c'est par définition l'adresse de la variable chaîne qui est transmis (@a), donc un pointeur sur un pointeur sur l'adresse du descripteur, alors qu'avec const, c'est le contenu de la variable chaîne (@a^), donc directement le pointeur sur le descripteur, qui est transmis.
    C'est vrai ! T'Oh !

    Je me suis mal exprimé : je me focalisais plutôt sur le passage de paramètres avec const. Il faut être à la fois clair et précis et je m'aperçois que ce n'est pas si facile


    Enfin pour ne rien arranger on dirait qu'avec les variables locales Delphi ne génère pas systèmatiquement des chaînes longues (ansistring) mais parfois des shortstring

    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
     
    var
      s : string; // variable globale
     
    procedure test;
    var
      s1 : string;
    begin 
      // Ici s1 est une shortstring et ne gère pas le comptage de réf
      s1:='autre chose'; 
     
      // Maintenant s1 est une ansistring avec gestion du compteur de ref qui   
      // pointe sur la même chaîne que S
      s1 := s;
    end;

    A confirmer ...

  18. #38
    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 448
    Points
    28 448
    Par défaut
    plait-il ?

    à ma connaissance Delphi utilise par défaut toujours des AnsiString...par contre elles sont compatibles, Delphi sait copier l'une vers l'autre sans problème.

  19. #39
    Membre habitué Avatar de phplive
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    179
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2003
    Messages : 179
    Points : 150
    Points
    150
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    procedure test;
    var
      s1 : string;
    begin 
      // Ici s1 est une shortstring et ne gère pas le comptage de réf
      s1:='autre chose'; 
    end;
    Ben ici le compteur de réf de s1 contient FFFFFFFF Bizare !
    Donc j'en déduis que
    soit: s1 est une shortstring et que c'est ainsi que Delphi l'identifie bien que
    la variable locale soit déclarée en string voir ansistring peu importe
    soit : l'integer situé à l'offset -8 par rapport à l'adresse pointée par
    la châine n'est pas le compteur de réf
    soit : je me plante complètement


    Quelqu'un peut il m'en dire plus ?

    Décidément les strings n'ont pas fini de faire parler d'elles

  20. #40
    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 448
    Points
    28 448
    Par défaut
    Citation Envoyé par phplive Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    procedure test;
    var
      s1 : string;
    begin 
      // Ici s1 est une shortstring et ne gère pas le comptage de réf
      s1:='autre chose'; 
    end;
    Ben ici le compteur de réf de s1 contient FFFFFFFF Bizare !
    Donc j'en déduis que
    soit: s1 est une shortstring et que c'est ainsi que Delphi l'identifie bien que
    la variable locale soit déclarée en string voir ansistring peu importe
    soit : l'integer situé à l'offset -8 par rapport à l'adresse pointée par
    la châine n'est pas le compteur de réf
    soit : je me plante complètement


    Quelqu'un peut il m'en dire plus ?

    Décidément les strings n'ont pas fini de faire parler d'elles
    non c'est normal

    en fait c'est "autre chose" qui a un compteur à -1 car c'est une constante

    tant que tu ne modifie pas s1, il pointe sur cette constante

    d'autre part, si s1 était un ShortString, l'offset -8 ne correspondrait à rien ! un ShortString c'est une "array[0..255] of char" dont l'élément 0 contient la longueur de la chaine...sauf si on spécifie la taille string[8] c'est une array[0..8] of char

Discussions similaires

  1. event dans une dll delphi a recuperer dans un autre langage
    Par titou640 dans le forum API, COM et SDKs
    Réponses: 12
    Dernier message: 30/09/2011, 12h05
  2. Tout le langage Delphi dans un document.
    Par ornitho dans le forum Langage
    Réponses: 4
    Dernier message: 10/12/2008, 14h52
  3. Bug dans Delphi 7 ?
    Par Teddy dans le forum Delphi
    Réponses: 8
    Dernier message: 26/06/2007, 19h29
  4. Bug dans delphi 2006? Include()
    Par the big ben 5 dans le forum Delphi .NET
    Réponses: 5
    Dernier message: 08/11/2006, 13h42
  5. Dans quel langage a été écrit le compilateur Delphi ?
    Par maamar1979 dans le forum Langage
    Réponses: 1
    Dernier message: 08/07/2006, 09h43

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