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 :

Utilisation du case of dans un record


Sujet :

Delphi

  1. #1
    Membre éclairé
    Profil pro
    Développeur Java
    Inscrit en
    Mars 2004
    Messages
    624
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Mars 2004
    Messages : 624
    Points : 681
    Points
    681
    Par défaut Utilisation du case of dans un record
    Bonjour,

    Dans un TTreeNode, j'utilise Data pour pointer sur différente données. J'utilise donc un case of dans un record :
    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
    type
      t1 = record
        entier : Integer ;
      end ;
      pt1 = ^t1 ;
     
      t2 = record
        chaine : string ;
      end ;
      pt2 = ^t2 ;
     
      tGlobal = record
        case genre : char of
    	  'I' : eInt : pt1 ;
    	  'S' : eString : pt2 ;
    	end ;
      pGloabl = ^tGlobal ;
    puis un exemple de 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
    procedure xxx ;
    var vt1 : pt1 ;
        vt2 : pt2 ;
        vGlobal : pGlobal ;	
    begin
        { Création d'un entier }
    	New(vt1) ; 
    	vt1^.entier := 1 ;
     
    	New(vGlobal) ;
    	vGlobal^.Genre := 'I' ;
     
    	if vGlobal^.Genre = 'I'
    	then begin
    	    Dispose(vt1) ;
    	    New(vt1) ;
     
    		vt1^.entier := 45 ;
     
    		vGlobal^.eInt := vt1^ ;
     
    		// Ici vGlobal^.eInt.entier = 45
    	end ;
     
    end ;
    ne comprenant pas trop comment ça fonctionne, j'ai lu la FAQ

    Ma question est toute bête comment Delphi fonctionne en mémoire faire cette gymnastique ?
    Car ça ne revient pas au même que :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
      tGlobal = record
    	  eInt : pt1 ;
    	  eString : pt2 ;
    	end ;
    Le case revient en fait à une union en C ?
    Et dans ce cas, en fait genre ne sert qu'à savoir le type de donnée ou sert-il à sélectionner le type de donnée ?.
    Donc, on doit avoir dans tGlobal des données de même type. On ne peut pas avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
       tGlobal = record
        case genre : char of
    	  'I' : eInt : integer ;
    	  'S' : eString : string ;
    	end ;
    Mais peut-on avoir :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
       tGlobal = record
        case genre : char of
    	  'I' : eInt : ^integer ;
    	  'S' : eString : ^string ;
    	end ;
    Est-ce que je me trompe ?

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    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 459
    Points : 24 873
    Points
    24 873
    Par défaut
    En fait, c'est très simple

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    tGlobal = record
        case genre : char of
    	  'I' : eInt : pt1 ;
    	  'S' : eString : pt2 ;
    	end ;
    tu as donc trois membres

    Genre
    eInt
    eString

    mais dans la structure variable, eInt et eString utilise le même espace mémoire, tu ne peux pas utiliser les deux en même temps, et Delphi ne fait aucune gymastique, c'est la compilation qui fait tout, d'ailleur, dans ton code ça se voit, c'est toi qui doit tout géré à la main pour affecter du Int ou du String ...


    dans ton cas, tu aurais pu juste déclarer


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    tGlobal = record
        genre : char;
        value: Pointer;	  
    end ;
    en transtypant à la volée le value ...

    ou le plus simple, comme tu l'as écrit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     tGlobal = record
        case genre : char of
    	  'I' : eInt : integer ;
    	  'S' : eString : string ;
    	end ;
    c'est que si tu modifie I, cela modifiera aussi S (qui est une chaine donc un pointeur, si tu modifie I cela fera pointer sur une zone mémoire non maitrisable)

    PS : un TVarData (PVarData) ou Variant (PVariant), ferait-il pas la même chose, ... structure que tu devrais étudier !

    PS2: Personnellement, contrairement à ce qui est écrit dans le tuto, je trouve l'aide "Partie variable d'enregistrements", très compréhensible, en particulieur la fin qui résume toute l'utilité de cette syntaxe :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Pour chaque instance d'enregistrement, le compilateur alloue assez de mémoire pour contenir tous les champs de la variante la plus volumineuse. Le sélecteur optionnel et les listeConstante (comme Rectangle, Triangle, etc dans le dernier exemple) ne jouent aucun rôle dans la manière dont le compilateur gère les champs ; ils ne sont là que comme commodité pour le programmeur.
    L'autre raison d'utiliser des parties variables, c'est qu'elles vous permettent de traiter les mêmes données comme si elles étaient de différents types, et ce même dans les cas où le compilateur n'autorise pas les transtypages. Si par exemple, vous avez un Real sur 64 bits comme premier champ dans une variante et un Integer sur 32 bits comme premier champ d'une autre variante. Vous pouvez alors affecter une valeur au champ Real puis en lire les 32 premiers bits comme valeur du champ Integer (et par exemple les transmettre à une fonction nécessitant un paramètre entier).
    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 confirmé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

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

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Points : 546
    Points
    546
    Par défaut
    Bonjour,

    en effet dans un record à partie variable, la mémoire allouée est celle de la structure maximale, les champs dans le case of partageant le même espace mémoire.

    En revanche la partie variable ne peut contenir aucun champ nécessitant une finalisation (interface, tableau dynamique, string). La raison est que le compilateur assure la gestion automatique de leur mémoire en utilisant un compteur de référence. Celui-ci est automatiquement incrémenté ou décrémenté lorsque la variable subit certaines opérations (affectation, destruction en fin de portée).
    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    var I:interface; s:string;
    Begin
    {...CODE...}
    End; { décrementation automatique des compteurs de références sur les données pointées par I 
    et s avec appels éventuels de leur destructeur/libérateur (si compteur à zéro) } 
    //P.S. : en réalité j'ai déjà remarqué que le compilateur pouvait optimiser dans
    // certains cas le code pour les strings et ne pas utiliser de compteur de références
    Or que doit faire le compilateur dans ce cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type  tGlobal = record
        case genre : char of
    	  'I' : (eInt : integer) ; // ne pas oublier les parenthèses ;P
    	  'S' : (eString : string) ;
    	end ;
    {...}
    var Global:tGlobal ;
    Begin
    {...CODE...}
    End; { décrementation et libération sur  Global.eString ou pas ?}
    Evidement il ne peut pas savoir (s'il ne fait rien est que Global contenait une string -> fuite mémoire, s'il "finalise" la string est qu'il s'agit en fait d'un entier -> accès aléatoire en mémoire ).

    C'est pour cela que ces types ne sont pas autorisés dans la partie variable d'un enregistrement.

    Une solution est donc d'utiliser des pointeurs vers ces types pour les utiliser dans la partie variable d'un enregistrement (mais attention à la gestion mémoire des types pointés).

    Une autre approche
    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
    type
      TTypeVarRecord = ( vrInteger, vrString );
     
      PVarRecord = ^TVarRecord;
     
      TVarRecord = record
        TypeVarRec : TTypeVarRecord ;
      end;
     
      PVarRecordInteger = ^TVarRecordInteger;
     
      TVarRecordInteger = record
         TypeVarRec : TTypeVarRecord ;
         I : integer ;
      end;
     
      PVarRecordString = ^TVarRecordString;
     
      TVarRecordString = record
        TypeVarRec : TTypeVarRecord ;
        S : string ;
      end;
    mais attention
    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
    var ptrVR : PVarRecord ; ptrVRString : PVarRecordString;
    begin
       New( ptrVRString );
       ptrVRString.TypeVarRec := vrString;
       ptrVRString.S := 'Coucou';
     
       ptrVR := PVarRecord(ptrVRString);
       // note sous Delphi 2006 on peut surcharger les opérateurs de transtypage
       // et bien plus encore :D 
     
       Dispose(ptrVR); // La string n'est pas libéré !!!
       { il faudrait faire
       case ptrVR.TypeVarRec of
          vrInteger : Dispose( PVarRecordInteger(ptrVR) );
          vrString  : Dispose( PVarRecordString (ptrVR) );
       end;}
    end;

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

Discussions similaires

  1. [XL-2010] Utilisation de case à cocher dans un fonction vb
    Par bru.antoine dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 05/10/2012, 15h39
  2. Réponses: 1
    Dernier message: 26/01/2009, 16h07
  3. Utilisation de case à cocher avec VB6 dans Datagrid
    Par seyiv dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 09/05/2007, 15h11
  4. Utilisation de case dans un script
    Par mirlidas dans le forum Linux
    Réponses: 1
    Dernier message: 11/12/2006, 14h46
  5. Réponses: 24
    Dernier message: 24/11/2005, 10h28

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