9.1 Une implantation sommaire de la fonction printf
Dans cette section, on se propose d'implanter la fonction printf dans
une architecture Intel x86 dans laquelle le passage des paramètres
se fait par une pile.
Par ailleurs, on suppose ne disposer que d'une seule fonction externe
dont le prototype est int putchar(int); et qui écrit dans la
sortie standard un caractère dont le code ascii est passé en
paramètre. Par exemple, pour afficher le caractère ~ on peut
utiliser le code suivant :
#include<stdio.h>
int main(){
char c = '~' ;
putchar( c) ;
return 0 ;
}
L'objectif est d'implanter la fonction de prototype :
void mprintf(const char *format, ...)
permettant d'afficher sur la sortie standard :
-
des caractères ascii ;
- des chaînes de caractères ;
- des entiers machines dans les bases décimale et binaire ;
- des entiers machines de Gauss nommés i.e. des nombres
complexes dont les parties réelles et imaginaires sont des entiers
machines et auxquels on associe une chaîne de caractères.
Cette fonction a un paramètre obligatoire et un nombre variable de
paramètres.
Le paramètre obligatoire est constitué par une chaîne de
caractères qui est composée de caractères ordinaires et
de 0, 1 ou plusieurs directives. Un caractère ordinaire est un
caractère ascii à l'exception du caractère %. Une directive
commence par le caractère % et peut être de plusieurs formes :
-
la directive %c indique que l'on souhaite afficher un caractère ;
- la directive %s indique que l'on souhaite afficher une
chaîne de caractères ;
- la directive %d indique que l'on souhaite afficher un entier en
base décimale ;
- la directive %b indique que l'on souhaite afficher un entier en
base binaire (cette base est indiquée par la lettre
minuscule b : l'entier décimal 2 est affiché comme 10b) ;
- la directive %Gd indique que l'on souhaite afficher un entier de
Gauss en base décimale (par exemple 2 + 2 I) ;
- la directive %Gb indique que l'on souhaite afficher un entier
de Gauss en base binaire (par exemple 10b + 10b I) ;
- toute autre lettre suivant un % est affichée (ce qui permet
d'afficher le caractères ascii %).
Questions
-
Donnez la définition d'une fonction
void PrintInt(int i,int b) qui écrit sur la sortie
standard l'entier i dans la base bÎ{2,10}.
- Donnez la définition d'une fonction
void PrintString(char *s) qui écrit sur la sortie standard
la chaîne de caractères associée à s.
- Donnez la définition de la fonction de prototype
int *parpt(int,...); qui prend en entrée un entier
indiquant la position d'un paramètre
dans la liste des paramètres1
et qui retourne un pointeur sur ce paramètre.
- Donnez la définition de la fonction
mprintf.
Indications.
Rappelons que les paramètres d'une fonction sont passés par la
pile. Cette pile est composée de cellules dont la taille en octet
correspond au type int, elle croit vers les adresses
décroissantes et elle a la structure suivante :
| 0000 |
··· |
| |
seconde variable locale |
| |
première variable locale |
| |
ancien pointeur de contexte |
| |
adresse de retour |
| |
premier paramètre |
| |
second paramètre |
| FFFF |
··· |
Ainsi si foo est la première variable locale automatique
définie dans la fonction appelée et que ptr est un
pointeur sur cette variable, ptr+1 pointe sur l'ancien pointeur
de contexte.
De plus, lors de l'appel d'une fonction, ses paramètres sont
empilés du dernier au premier et on note qu'un paramètre de type
-
entier occupe une cellule ;
- pointeur occupe une cellule ;
- caractère occupe une cellule ;
- entier de Gauss occupe 3 cellules, les champs sont empilés
du dernier au premier.
Pour tout commentaire : Alexandre Sedoglavic.