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

Delphi Discussion :

SetLength Access violation


Sujet :

Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    août 2003
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2003
    Messages : 114
    Points : 60
    Points
    60
    Par défaut SetLength Access violation
    Hi

    I have a problem when I want to change the size of a dynamic array but if the data is fill with value other than 0 I have an error message Access Violation

    I do a simple unit to reproduce this error

    Step by step, the error append when SetLength(VarB,VarSize) is call

    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
     
    unit Unit1;
     
    interface
     
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
     
    type
      TBPointReal      = array of double;
     
      TForm1 = class(TForm)
        Button1: TButton;
        procedure InitVar;
        procedure AddVar(VarSize: integer);
        procedure Button1Click(Sender: TObject);
        procedure FormActivate(Sender: TObject);
     
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
        VarA,VarB    : TBPointReal;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
    procedure TForm1.AddVar(VarSize: integer);
    begin
      SetLength(VarA,VarSize);
      SetLength(VarB,VarSize);
    end;
     
    procedure TForm1.InitVar;
    begin
      FillChar(VarA,SizeOf(VarA),0);
      FillChar(VarB,SizeOf(VarB),999);
    end;
     
    procedure TForm1.FormActivate(Sender: TObject);
    begin
      AddVar(4);
      InitVar;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      AddVar(8);
    end;
     
    end.
    Why this code is not correct

    Thankyou

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 218
    Points : 21 553
    Points
    21 553
    Par défaut
    Bonjour,

    Vous pouvez écrire en français, c'est un peu tout l'intérêt de Developpez.com d'avoir une communauté francophone.

    C'est FillChar qui est la source de la future exception, confusion entre VarA qui est la référence sur le tableau et VarA[0] qui est l'emplacement mémoire du premier élément
    SizeOf renvoie d'ailleurs 4 ou 8 selon 32/64Bits car c'est juste la taille de la référence (un pointeur) contrairement à SizeOf(VarA[0]) qui retournera toujours 8 la taille d'un Double
    En fait, le InitVar altère la référence et non le contenu du tableau, résultat au prochain SetLength, l'opération est effectuée sur une adresse totalement fausse, sans compter que 999 me semble bien elévé, je dirais 255 au plus car c'est un Byte ou AnsiChar)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.InitVar;
    begin
      FillChar(VarA[0], SizeOf(Double) * Length(VarA), 0); // Inutile après un SetLength
      FillChar(VarB[0], SizeOf(Double) * Length(VarB), 999); // Valeur 999 fausse tronqué à 255 soit $FF, ça ferait en fait un Double en $FFFFFFFFFFFFFFFF soit probablement NaN.
    end;
    A Noter que le SetLength initialise le tableau par défaut à Zéro, à l'instar des membres d'une classe donc inutile de le refaire.
    Pour initialiser 999 dans chaque Double, FillChar ne fonctionnera pas car cela travaille à l'octet et non à la valeur, une boucle classique sera donc à utiliser
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    août 2003
    Messages
    114
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : août 2003
    Messages : 114
    Points : 60
    Points
    60
    Par défaut
    Merci pour ces explications
    Je voulais optimiser pour eviter une boucle !

    Cordialement

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 218
    Points : 21 553
    Points
    21 553
    Par défaut
    FillChar fait aussi une boucle qui écrit par lot de 8 octets la combinaison calculée de la valeur en entrée
    La boucle sur un tableau de Double sera de performance équivalente
    On manipule la mémoire par Registre du CPU donc pas de miracle.

    Récemment, j'ai fait le test d'ailleurs sur un objet lent dans un programme dont j'ai la charge de l'optimisation, le SetLength était long sur un tableau de 1Mo bien plus long que les tests que j'avais fait pour un GetMem sur un 1Mo, en fait c'est le ZeroMemory (un FillChar 0 donc) qui prend du temps, la majeur partie en fait.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

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

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

    Informations forums :
    Inscription : novembre 2002
    Messages : 8 221
    Points : 26 620
    Points
    26 620
    Par défaut
    en même temps il faudrait un tableau de taille tout de même extrêmes conséquente pour que ce soit sensible

    sinon on peut imaginer une copie du type

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Tab[0] := 999; // 1 élément
    Move(Tab[0], Tab[1], SizeOf(Double)); // 2 éléments
    Move(Tab[0], Tab[2], 2 * SizeOf(Double)); // 4 éléments
    Move(Tab[0], Tab[4], 4 * SizeOf(Double)); // 8 éléments
    ...
    en doublant la taille de la copie à chaque fois et en terminant sur une copie limitée aux nombres restant à la fin

    là j'ai mis un code en dur, mais on doit pouvoir le rendre paramétrable avec un "while 2 * Count < TotalLen do" et un move Final pour la fin du tableau
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  6. #6
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mai 2002
    Messages
    3 147
    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 147
    Points : 5 266
    Points
    5 266
    Par défaut
    bonsoir

    j'avais trouvé ceux-ci sur le net
    l'auteur n'est autres que Henri Gourvest
    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
     
    procedure FillDouble(var Dest; Count: integer; Value: double); register;
    {$IFNDEF USEASM} // 2687 ms
    var
      i  : integer;
      p : PDouble;
    begin
      p := @dest;
      for i := 1 to count do
      begin
         p^ := Value;
         inc(integer(p), 8);
      end;
    end;
    {$ELSE} // 5656 ms
    asm
      test edx, edx
      jz @@End
      fld qword ptr [ebp+8]
      @@Loop:
      fst qword ptr [eax]
      lea eax, eax+8
      dec edx
      jnz @@Loop
      ffree st(0)
      @@End:
    end;
    {$ENDIF}
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      c: cardinal;
      i: integer;
      buffer: array[1..1000] of double;
    begin
      c := GetTickCount;
      for i := 1 to 1000000 do
         FillDouble(buffer, length(buffer), 1.1);
      memo1.Lines.Add(inttostr(GetTickCount - c));
    end;
    Nous souhaitons la vérité et nous trouvons qu'incertitude. [...]
    Nous sommes incapables de ne pas souhaiter la vérité et le bonheur, et sommes incapables ni de certitude ni de bonheur.
    Blaise Pascal
    PS : n'oubliez pas le tag

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    12 218
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 12 218
    Points : 21 553
    Points
    21 553
    Par défaut
    Attention pour FillDouble,
    Si buffer fonctionne pour un tableau statique, c'est toujours buffer[0] pour un tableau dynamique.

    Et Inc(P); est suffisanr pour déplacer le pointeur, cela gère automatiquement le décalage pour un pointeur typé.
    Surtout pour conserver un pointeur 64Bits on ne sait jamais

    Autre façon de l'écrire

    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
    procedure FillDouble(var Dest; Count: Integer; Value: Double); register;
    var
      P, PE: PDouble;
    begin
      P := @Dest;
      PE := P;
      Inc(PE, Count);
      {$POINTERMATH ON}
      while P < PE do
      {$POINTERMATH OFF}
      begin
        P^ := Value;
        Inc(P);
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mai 2002
    Messages
    3 147
    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 147
    Points : 5 266
    Points
    5 266
    Par défaut
    salut

    tiens je ne connaissais pas cette directive {$POINTERMATH ON}
    Merci pour cette petite astuce
    Nous souhaitons la vérité et nous trouvons qu'incertitude. [...]
    Nous sommes incapables de ne pas souhaiter la vérité et le bonheur, et sommes incapables ni de certitude ni de bonheur.
    Blaise Pascal
    PS : n'oubliez pas le tag

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

Discussions similaires

  1. Depuis EDI DELPHI : Access Violation
    Par powerlog dans le forum EDI
    Réponses: 1
    Dernier message: 03/08/2005, 16h59
  2. Access violation avec fseek
    Par baleine dans le forum C
    Réponses: 7
    Dernier message: 18/03/2005, 16h41
  3. Réponses: 7
    Dernier message: 22/02/2005, 13h07
  4. [DELPHI][PROECEDURES STOCKES] Access violation
    Par All Jinx dans le forum Bases de données
    Réponses: 6
    Dernier message: 14/05/2004, 15h57
  5. Réponses: 3
    Dernier message: 22/05/2002, 09h37

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