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 :

Violation de la Mémoire ?!


Sujet :

Delphi

  1. #1
    Membre émérite Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Par défaut Violation de la Mémoire ?!
    Bonjour, j'ai bon me tourner la tete dans tous les sens, je ne vois pas mon probleme dans ce code.
    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
    unit LogActions;
     
    interface
     
    uses SysUtils, Classes, SyncObjs;
     
    function GetLogs: TStrings;
     
    implementation
     
    var
      CS: TCriticalSection;
      FileName: String;
     
    function GetLogs: TStrings;
    var
      LogFile: TextFile;
      Ligne: String;
    begin
      CS.Acquire;
     
      if not FileExists(FileName) then
      begin
        Result.Text := '(vide)';
        Exit;
      end
      else
      begin
        AssignFile(LogFile, FileName);
        Reset(LogFile);
      end;
     
      while not EoF(LogFile) do
      begin
        ReadLn(LogFile, Ligne);
        Result.Add(Ligne);
      end;
     
      CloseFile(LogFile);
     
      CS.Release;
    end;
     
    initialization
      if not Assigned(CS) then CS := TCriticalSection.Create;
      FileName := 'logs.txt';
     
    finalization
      CS.Free;
     
    end.
    Enfait, il parcours bien a chaque ligne, et arrivé à la derniere, il plante en me donnant comme erreur une violation de la mémoire.

  2. #2
    Expert confirmé
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Par défaut
    Salut

    Je te propose plutot ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    procedure GetLogs(Log: TStrings; Filename: TFilename);
    begin
      if not FileExists(Filename) then
        Log.Text := '(Vide)'
      else
        Log.LoadFromFile(Filename);
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      GetLogs(Memo1.Lines, 'c:\MonFichier.log');
    end;
    @+

  3. #3
    Membre émérite Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Par défaut
    Merci beaucoup, j'ai finalement opté pour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function GetLogs: TStrings;
    begin
      CS.Acquire;
     
      Result := TStringList.Create;
     
      Result.Text := '(Vide)';
     
      if FileExists(Filename) then
        Result.LoadFromFile(Filename);
     
      CS.Release;
    end;
    Merci beaucoup encore ^^

  4. #4
    Membre émérite
    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
    Par défaut
    Ben oui, l'objet Result de GetLogs n'était instancié à aucun moment... il est étrange que le code ne plantait pas dès la première référence à Result.

    Par contre dans la deuxième version, il ne faudra pas, après usage, oublier de libérer l'objet renvoyé par la fonction, afin d'éviter une consommation inutile de mémoire.

  5. #5
    Membre émérite Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Par défaut
    Détails un peu plus stp ça m'interesse ^^

  6. #6
    Membre émérite
    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
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var Logs : TStringList;
    ...
    Logs := GetLogs;
    // Tout ce que je dois faire avec Logs
    ...
    Logs.Free;
    En effet, à chaque fois que GetLogs est appelée, un nouvel objet est créé, qu'il faut donc libérer. C'est le genre d'erreur qu'on fait facilement, et qui provoque les fameuses "fuites de mémoire".

    En outre, dans un grand projet, on finit par ne plus savoir quoi instancie quoi et quoi doit libérer quoi, ce qui finit par rendre le projet impossible à maintenir correctement.

    Sauf nécessité absolue, on s'interdit d'instancier des objets à l'intérieur d'une fonction ou d'une procédure, en vue d'un retour vers l'extérieur de la fonction ou de la procédure.

    Imagine ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Memo1.Lines := GetLogs;
    C'est un code qui fonctionne, mais tout à fait épouvantable, puisqu'il n'y a aucun moyen de libérer l'objet créé par GetLogs. Si ce code est appelé en boucle, tous ces objets vont s'accumuler dans la mémoire, jusqu'à saturation.

    La bonne méthode est de transmettre en paramètre un objet déjà instancié servant de receveur. Il n'est même pas nécessaire de le passer en paramètre var, d'ailleurs, à cause de la "pointeurisation" automatique des objets, ce qui permet des raccourcis de code.

    Le code de Cl@udius est donc la bonne méthode, qu'on peut compléter comme suit :

    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
    procedure GetLogs(Log: TStrings; Filename: TFilename);
    begin
      CS.Acquire;
      if not FileExists(Filename) then
        Log.Text := '(Vide)'
      else
        Log.LoadFromFile(Filename);
      CS.Release
    end;
     
    // Type d'utilisation 1 : objet déjà instancié 
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      GetLogs(Memo1.Lines, 'c:\MonFichier.log');
    end;
     
    // Type d'utilisation 2 : objet que j'instancie puis libère au même bloc
    // rendant ainsi le code plus propre et plus lisible (un .Create pour un .Free)
    procedure TForm1.Button2Click(Sender: TObject);
    var Logs : TStringList;
    begin
     Logs := TStringList.Create;
      GetLogs(Logs, 'c:\MonFichier.log');
     ... // Je fais ce qui me plaît avec Logs.
     Logs.Free;
    end;
     
    // Type d'utilisation 3 : utilisation d'un objet TStringList déclaré dans la fiche,
    // Instancié une fois au démarrage dans FormCreate, 
    // libéré à la sortie dans FormClose. Je ne tape pas le code, trop long.

  7. #7
    Membre émérite Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Par défaut
    Très intéréssant. Peut tu m'expliquer mieux pourquoi on ne peux pas controler l'objet lorsqu'on fait Memo1.Lines := GetLogs; ?
    N'y a t'il pas pointeurisation et donc plus tard, lors de la destruction de Memo1.Lines, n'y a til pas désalocation de la mémoire ?

    Lorsqu'on réafecte la valeur, il n'y a pas destruction de l'ancienne ?

  8. #8
    Membre émérite
    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
    Par défaut
    Parce que Memo1.Lines est une propriété, dont l'affectation se traduit par une copie de l'objet, comme la plupart des affectations propriété:=variable, la VCL étant ainsi conçue (en fait le tableau associé à la propriété Lines n'est détruit qu'à la libération de l'objet TMemo lui-même, jamais par une affectation).

    Les affectations variable:=variable, par contre, effectuent seulement une copie de pointeur.

    Oui, je sais... tu peux commencer à comprendre à quel point la transformation du Pascal en Pascal Objet par Borland a fait l'objet de choix parfois très discutables.

    Raison de plus pour être prudent et faire un code bien symétrique au niveau instanciation/libération, ce qui limitera les éternelles questions sur, soit les fuites de mémoire (trop d'instanciations ou pas assez de libérations), soit les violations d'accès (trop de libérations ou pas assez d'instanciations).

  9. #9
    Membre émérite Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Par défaut
    Merci beaucoup. Tres intéréssant ce petit cours ^^ Je vais voir pour effectuer ces modifications

    Merci encore

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

Discussions similaires

  1. Violation d'accès mémoire TIdFTP
    Par ac/dc dans le forum Composants VCL
    Réponses: 3
    Dernier message: 23/08/2011, 11h59
  2. TStringList et Violation d'accès mémoire
    Par Weirdy dans le forum Langage
    Réponses: 2
    Dernier message: 15/06/2011, 10h23
  3. Affichage d'images : Violation d'adresse mémoire
    Par BnouK dans le forum Débuter
    Réponses: 5
    Dernier message: 14/05/2009, 10h07
  4. violation d'acces mémoire
    Par yesil08 dans le forum C++Builder
    Réponses: 15
    Dernier message: 03/04/2008, 15h25
  5. Violation accès mémoire
    Par BenjaminLustrement dans le forum C
    Réponses: 9
    Dernier message: 09/06/2006, 10h07

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