samedi 28 octobre 2000

Pointeur

POINTEUR

On accède habituellement à une variable par son nom.
Nb1:=Nb2;

On localise l'adresse par le nom de la variable, mais on peut aussi accéder 
   à son contenu directement par son adresse. L'adresse sert à 
localiser    un emplacement en mémoire. Un pointeur est une constante 
ou variable    qui possède l'adresse d'une variable. Le pointeur pointe 
vers la variable    concernée. 

Les pointeurs sont plus rapides et utilisent moins de mémoire que si on utiliserait directement la variable. Les variables utilisent la mémoire locale qui est en quantité minime. Lorsqu'on utilise un pointeur, seul ce dernier est dans la mémoire locale. Les éléments pointés sont eux dans la mémoire globale qui est beaucoup plus grande. On peut aisément se rendre compte de l'importance d'utiliser les pointeurs.



Le type pointer est utilisé en delphi pour désigner un pointeur. On utilise @ pour avoir l'adresse de la variable

var
  ptrNote : Pointer;
  Note : integer;
begin
  ptrNote := @Note;
end;
 

La variable stocke l'adresse l'adresse de ptrNote. C'est un pointeur non typé.
Les pointeurs ne pointent pas obligatoirement vers une adresse, on peut leur assigner une valeur nulle avec le mot «nil». On utilise beaucoup cette affectation afin de vérifier si le pointeur pointe quelque part.

var ptrNote : pointer;
begin
  ptrNote := nil;
end;
 

Le type pointer déclare un pointeur sur    n'importe quoi. Le type
de donnée pointé ne peut être connu    même si
on peut accèder à son contenu. Ce n'est vraiment    pas pratique
dans un programme. On doit utiliser des pointeurs spécifiques    à
un type pour régler le problème. Un pointeur sur un integer,
   sur un real... Au lieu d'utiliser directement le pointeur, on préfèrera
   créer un nouveau type et on déclarera les variables ou les
paramètres    en utilisant ce nouveau type. Pointeur vers s'écrit
en delphi ^, ce qui    équivaut à * du c.  

Langage c Delphi
int *ptrNote;
int Note=74;
ptrNote=&Note;
type
  pNote:=^integer;
var
  Note:pNote;
begin
  Note^:=74;
end;

type
pNote:=^integer;
 

Dans cet exemple, la  variable pNote pointe sur un type integer. On déclare 
ainsi un pointeur sur un integer. pNote := ^long; Ici pNote serait un pointeur 
sur un long. Lorsque ^ est avant un type de donnée (déclaration) 
comme dans les exemples ci-dessus, le ^ signifie pointe sur. Le ^ est avant 
le type de donnée, c'est typique à la déclaration.

variable :=^ <type de donnée>

Lorsqu'on veut effectuer des opérations sur le contenu de la valeur 
pointé par le pointeur, on utilise encore ^. Le ^ est après 
le nom du pointeur comparativement au cas de déclaration qui est situé 
après.

pNote ^:= 54;

Dans cette exemple, on affecte 54 à la valeur contenu à l'adresse
pointé par le pointeur. On effectue ainsi une affectation.

Les éléments pointés par les pointeurs sont gérés dynamiquement et c'est au programmeur qui doit coordonner, le tout. La gestion de l'élément pointé se fait en deux étapes
  • Initialisation
  • Destruction
Un pointeur doit être créé avant de pouvoir l'utiliser. La procédure «new» initialise le pointeur, le nom du pointeur doit être passé en paramètre. Un espace en mémoire lui est ainsi alloué. Des problèmes surviennent aussitôt si cette étape n'est pas faite. Lorsque l'utilisation du pointeur sera terminée, il faudra le détruire afin de remettre l'espace alloué au système et d'augmenter ses performances. La procédure «dispose» détruit l'espace alloué par le pointeur. Le nom du pointeur doit être passé en paramètre.

type
  pNote = ^Integer;
var
  Note : pNote;
begin
  new(Note);       //initialisation du pointeur
  Note^ := 54;     //affectation du pointeur
  dispose(Note);   //destruction du pointeur
end;

Opération sur les pointeurs


Les pointeurs sont limités aux opérations d'affection et de comparaison.

Affectation

Les pointeurs doivent pointer vers le même type avant de pouvoir faire une affectation

var
  PNote : ^Integer;
  PNbEleve :^Integer;
begin
  new(PNote);
  new(PNbEleve);
  PNote := PNbEleve;
  dispose(PNote);
  dispose(PNbEleve);
end;


L'affectation ci-dessus est possible, car les deux pointeurs pointent sur des integers. Étant donnée qu'après cette affectation les deux pointeurs pointent au même endroit, il faut détruire qu' un seul pointeur. Il est aussi possible de copier le contenu d'un pointeur dans un autre pointeur, mais les deux pointeurs doivent pointer sur le même type de donnée.

var
  PNote :^Integer;
  PNbEleve :^Integer;
begin
  new(PNote);
  new(PNbEleve);
  PNote :=PNbEleve;
  PNote^ := 5;
  dispose(PNote);
end;

Comparaison

La comparaison sert à savoir si les pointeurs pointent au même endroit. Les signes «=» et «<>» peuvent être utilisés.

if PNbEleve = PNote then
Si cette ligne de code retourne true (vrai), les deux pointeurs pointent à la même adresse.

Pointeurs d'enregistrements

Ce type de pointeur est tout simplement un pointeur vers un enregistrement. On utilise beaucoup ce type de pointeur. Prenons comme exemple un enregistrement du chapitre 5

type TInfoClient = record
  Prenom:string[20];
  Nom:string[20];
  Adresse:string[30];
  Ville:string[20];
  CodePostal:string[6];
end;
  PInfoClient:TInfoClient;
var 
  InfoClient:PInfoClient;
begin
  New(InfoClient);
  InfoClient^.Prenom := 'Paul';
  InfoClient^.Nom := 'Houle';
  InfoClient^.Adresse := '1024 Des Chênes';
  InfoClient^.Ville := 'Montreal';
  InfoClient^.CodePostal := 'J4S2R4';
  Dispose(InfoClient);
end;

Pour mieux comprendre les pointeurs, nous allons étudier    les 
listes.

Liste

Les listes sont une suite d'éléments mis les uns à la suite des autres. On les utilise constamment dans la vie de tous les jours. En programmation, leurs utilités ne sont plus à faire. Les listes peuvent être utilisées pour y mettre n'importe quel type de donnée. Souvent, on les utilise avec les enregistrements.


Chaque noeud possède un champ information et un champ pointeur qui pointent sur le prochain noeud. Lorsqu'il n'y a pu de noeud, le dernier pointeur pointe sur nulle c'est alors là fin de la liste.

Opération sur les pointeurs

Création


type //Création des informations pour la liste
  TInfoClient = record
  Prenom:string[20];
  Nom:string[20];
  Adresse:string[30];
  Ville:string[20];
  CodePostal:string[6];
end;

Déclaration


type //Déclaration de la liste
  PListClient = ^TListClient;
  TListClient = record
  InfoClient :TInfoClient;
  ProchainNoeud:PListClient;
end;

Construction


var
  PremierClient,TempClient:PListClient;
begin
  PremierClient:=nil; //Initialise le premier pointeur
  new(TempClient); //Crée un noeud temporaire
  TempClient^.InfoClient.Prenom:='Paul';
  TempClient^.InfoClient.Nom:='Prenom';
  TempClient^.InfoClient.Adresse:='23 Nobert';
  TempClient^.InfoClient.Ville:='Longueuil';
  TempClient^.InfoClient.CodePostal:='J3S1H4';
  TempClient^.ProchainNoeud:=PremierClient;
end;
 

La dernière ligne permet d'ajouter le noeud à la liste afin
qu'il pointe à la même adresse que le premier pointeur.



Cette instruction permet de faire pointer PremierClient au début de la liste
PremierClient:=TempClient;
 

Un petit programme illustrant les principes de base des listes est disponible en téléchargement ici.

mercredi 11 octobre 2000

Comment prendre des caractère dans une string?

Comment prendre des caractère dans une string?
function RightStr
   (Const Str: String; Size: Word): String;
begin
 if Size > Length(Str) then Size := Length(Str);
 RightStr := Copy(Str, Length(Str)-Size+1, Size)
end;

function MidStr
   (Const Str: String; From, Size: Word): String;
begin
 MidStr := Copy(Str, From, Size)
end;

function LeftStr
   (Const Str: String; Size: Word): String;
begin
 LeftStr := Copy(Str, 1, Size)
end;
end;

lundi 9 octobre 2000

Fonction et procédure

FONCTION ET PROCÉDURE

Les fonctions et procédures sont des sous-programmes qui exécutent des tâches en particuliers. Ils permettent de rendre le programme plus lisible et de pouvoir les réutiliser sans devoir réécrire la portion de code. La différence entre les procédures et fonctions est que la fonction retourne une valeur. Ils peuvent avoir un, plusieurs ou aucun paramètre. Un paramètre est une valeur que l'on passe à une procédure ou fonction pour qu'elle puisse effectuer des traitements avec cette valeur.

Lorsqu'on a créé notre fonction ou procédure, on peut l'appeler dans notre programme. Ils nécessitent d'être déclarés avant la partie implémentation

Fonction

function <nom de la fonction>(<liste de paramètre>):<type de résultat>;

function CalculerTaxe(PrixSousFinal:real):real;
begin
  result := PrixSousFinal * 0.07 + PrixSousFinal;
end;

On utilise le mot result lorsqu'on veut retourner une valeur dans
une    fonction. Le nom de la fonction était anciennement utilisé
pour retourner    la valeur, on utilise moins cette technique, mais il est
toujours possible de    l'utiliser. 

Code source disponible ici.

Procédure

procedure <nom de la procédure>(<liste de paramètre>);

procedure AfficherErreur(Message_Erreur:string); 
begin 
   messagedlg(Message_Erreur,mtInformation,[mbOk], 0);
end;

Lorsqu'on a aucune valeur à retourner ou qu'on a plus d'une valeur à retourner, on peut utiliser les procédures.

Code source disponible ici

Paramètre

Il existe plusieurs types de paramètre, voici les trois plus utilisés.

Paramètre par valeur

La fonction ou procédure reçoit une copie de la valeur passée en paramètre. La valeur originale ne sera jamais modifiée. Le programme que je vous ai fait inverse deux nombres. Lorsque la procédure est terminée, les deux nombres sont les mêmes qu'avant l'inversion. Les passages par valeur sont souvent utilisés lorsqu'on ne doit pas modifier le contenu des variables ou que l'on doit afficher des variables. Ce type de paramètre requit plus de ressources système et est plus lent.

procedure ChangerValeur(premier,deuxieme:integer);
var
  temp:integer;
begin
  temp:=premier;
  premier:=deuxieme;
  deuxieme:=temp;
end;

La fonction accède à une copie locale des variables passées en paramètre.

Code source disponible ici.

Paramètre par adresse

La fonction ou procédure utilise l'adresse de la variable que le compilateur lui transmet. Lorsqu'on utilise l'adresse de la variable, on accède directement à son contenu. La valeur originale sera donc modifiée. On ajoute le mot «var» devant les paramètres afin d'avoir des paramètres par adresses.

procedure ChangerValeur(var premier,deuxieme:integer);
var
  temp: integer;
begin
  temp:=premier;<
  premier:=deuxieme;
  deuxieme:=temp;
end;

La fonction accède au contenu des emplacements mémoire où sont stockées les variables. C'est beaucoup plus rapide que le passage de paramètre par valeur.

Code source disponible ici.

Paramètre constant

Un paramètre constant ne peut pas être modifié dans la fonction ou procédure. La valeur du paramètre ne pourra changer, c'est comme une constante.

procedure AfficherErreur(const Message_Erreur:string);
begin
  messagedlg(Message_Erreur,mtInformation,[mbOk], 0);
end;

Surchage de méthode (Overloading)

Il peut arriver qu'on doit utiliser la même fonction mais les paramètres ne sont pas du même type. La surchage de méthode comble cette lacune depuis Delphi 4.
Imaginons que nous devons calculer la taxe sur un prix donné. Le prix peut donc être un entier ou un réel. Nous pourrons créer une fonction avec un nom différent pour chaque type de données, mais avec la surchage de méthode, on peut utiliser le même nom de fonction pour chaque type de donnée et même changer le nombre de paramètre. Le compilateur se charge d'appeler la fonction adéquate au paramètre qu'on lui a fourni.

Pour effectuer une surchage de méthode, on doit ajouter le mot overload après la déclaration de la fonction.

function CalculerTaxe(Prix:integer):double;overload;
function CalculerTaxe(Prix:double):double;overload;

Par la suite on définie autant de fonctions qu'on n a déclaré de fonction. On simplie l'écriture de programme en utilisant la surchage.

Code source disponible ici.

Paramètre par défaut

Imaginer que vous employez une fonction ou procédure la majorité du temps les mêmes paramètres, avec Delphi vous pouvez créer des paramètres par défaut. Si vous employez les valeurs par défaut vous omettez les paramètres sinon vous les inscrivez.
La déclaration se fait comme à l'habitude, les paramètres par défaut doivent par contre être placés à la fin de la liste de paramètre.

procedure ShResize(Hauteur,Largeur:double;Taux : double = 2);

Appel de la fonction avec les paramètres par défaut
ShResize(Shape1.Height,Shape1.Width,Taux);

Appel de la fonction sans les paramètres par défaut
ShResize(Shape1.Height,Shape1.Width);