Examen de juin 2010 - Ensiwiki

publicité
Logiciel de Base :
examen de deuxième session
ENSIMAG 1A
Année scolaire 2009–2010
Consignes générales :
– Durée : 1h. Tous documents et calculatrices autorisés.
– Vous serez noté sur la précision de vos réponses : on attend des réponses courtes donnant
exactement l’information demandée.
– Barème indicatif : chaque question sur 2 points.
Consignes relatives à l’écriture de code C et assembleur Pentium :
– Pour chaque question, une partie des points sera affectée à la clarté du code et au respect des
consignes ci-dessous.
– Pour les questions portant sur la traduction d’une fonction C en assembleur, on demande d’indiquer en commentaire chaque ligne du programme C original avant d’écrire les instructions
assembleur correspondantes.
– Pour améliorer la lisibilité du code assembleur, on utilisera systématiquement des constantes
(directives .set ou .equ) pour les déplacements relatifs à %ebp (i.e. paramètres des fonctions
et variables locales). Par exemple, si une variable locale s’appelle var en langage C, on y fera
référence avec var(%ebp).
– Sauf indication contraire dans l’énoncé, on demande de traduire le code C en assembleur de
façon systématique, sans chercher à faire la moindre optimisation : en particulier, on stockera
les variables locales dans la pile (pas dans des registres), comme le fait le compilateur C
par défaut.
– On respectera les conventions de gestions des registres Intel vues en cours, c’est à dire :
– %eax, %ecx et %edx sont des registres scratch ;
– %ebx, %esi et %edi ne sont pas des registres scratch.
Question 1 Comment sont représentées en mémoire les chaînes de caractères en C ? A quoi
correspond ch dans l’exemple suivant : char *ch = "toto" ?
En C les chaînes sont des tableaux de caractères terminés par le caractère ’\0’. ch est un
pointeur sur le premier caractère de la chaîne.
Question 2 Comment est représentée une chaîne vide en C ? Quelle est la différence avec une
chaîne "nulle" ?
Une chaîne vide est un tableau d’un seul caractère : ’\0’. Une chaîne nulle est représentée par
un pointeur NULL.
1
Question 3 Écrire en C une fonction unsigned taille(char *ch) qui renvoie la taille de la
chaîne non nulle ch.
unsigned taille(char *ch)
{
unsigned n;
for (n = 0; ch[n] != ’\0’; n++);
return n;
}
Question 4 Traduire la fonction taille de la question précédente en assembleur Pentium.
On demande une traduction systématique sans chercher à optimiser le code.
taille:
.set ch, 8
.set n, -4
enter $4, $0
// n = 0
movl $0, n(%ebp)
for_taille:
// ch[n] != ’\0’
movl n(%ebp), %eax
movl ch(%ebp), %edx
cmpb $0, (%edx, %eax)
je fin_for_taille
// n++
addl $1, n(%ebp)
jmp for_taille
fin_for_taille:
// return n
movl n(%ebp), %eax
leave
ret
Question 5 Écrire en C une fonction char *concat(char *dst, char *src) qui renvoie la
chaîne résultant de la concaténation de src à la fin de dst. Le contenu de la chaîne résultat
est stocké dans dst, et on suppose que l’espace réservé pour dst est suffisant pour contenir la
concaténation des deux chaînes (il n’y a donc pas besoin d’allouer de mémoire). Les chaînes src
et dst ne sont pas nulles.
char *concat(char *dst, char *src)
{
unsigned n = taille(dst);
2
for (unsigned i = 0; src[i] != ’\0’; i++, n++) dst[n] = src[i];
dst[n] = ’\0’;
return dst;
}
Question 6 Traduire la fonction concat de la question précédente en assembleur Pentium.
On demande une traduction systématique sans chercher à optimiser le code.
concat:
.set dst, 8
.set src, 12
.set n, -4
.set i, -8
enter $8, $0
// n = taille(dst)
pushl dst(%ebp)
call taille
addl $4, %esp
movl %eax, n(%ebp)
// i = 0
movl $0, i(%ebp)
for_concat:
// src[i] != ’\0’
movl src(%ebp), %edx
movl i(%ebp), %ecx
cmpb $0, (%edx, %ecx)
je fin_for_concat
// dst[n] = src[i]
movl src(%ebp), %edx
movl i(%ebp), %ecx
movb (%edx, %ecx), %al
movl dst(%ebp), %edx
movl n(%ebp), %ecx
movb %al, (%edx, %ecx)
// i++
addl $1, i(%ebp)
// n++
addl $1, n(%ebp)
jmp for_concat
fin_for_concat:
// dst[n] = ’\0’
movl dst(%ebp), %edx
movl n(%ebp), %ecx
movb $0, (%edx, %ecx)
// return dst
movl dst(%ebp), %eax
leave
ret
3
Soit un exemple d’utilisation de la fonction concat :
...
char *dst = malloc(1000);
strcpy(dst, "titi"); // copie "titi" dans dst
char *res = concat(dst, "toto");
...
Question 7 Comment s’appelle la zone mémoire où sont alloués les 1000 octets réservés pour
dst par la fonction malloc ? Où est stockée la chaîne "toto" lors de l’exécution du programme ?
malloc alloue dans le tas (heap en anglais). Les constantes de type chaîne (ou autre) sont
stockées dans la zone data.
Question 8 Comment sont passés les paramètres à la fonction concat ? Plus précisément, on
veut savoir ce qui est copié dans la pile juste avant d’exécuter call concat.
On copie dans la pile les adresses des chaînes (pas leur contenu !). Pour dst, c’est l’adresse de
début de la zone réservée dans le tas qui est passée. Pour "toto", c’est l’adresse du premier
caractère de la constante dans la zone data.
Question 9 Expliquer en quelques lignes ce que fait le code assembleur (optimisé) ci-dessous.
// void fct(int tab[])
fct:
.set tab, 8
enter $0, $0
pushl %ebx
movl $0, %ecx
movl $0, %edx
movl tab(%ebp), %ebx
etiq1:
cmpl $0, (%ebx, %ecx, 4)
je etiq2
jl etiq3
movl (%ebx, %ecx, 4), %eax
movl %eax, (%ebx, %edx, 4)
addl $1, %edx
etiq3:
addl $1, %ecx
jmp etiq1
etiq2:
movl $0, (%ebx, %edx, 4)
popl %ebx
leave
ret
Cette fonction supprime les entiers strictement négatifs du tableau d’entiers signés passé en
paramètre. Le tableau est terminé par l’entier 0.
4
Question 10
Pourquoi copie-t’on le registre %ebx dans la pile au début de cette fonction fct ?
Le registre %ebx n’est pas un registre scratch : on doit donc le sauvegarder avant de l’utiliser
dans fct, car la fonction appelante a pu laisser des valeurs importantes dedans.
Question 11
Donner le code C équivalent à cette fonction fct.
void fct(int tab[])
{
int i, j;
for (j = i = 0; tab[i] != 0; i++)
if (tab[i] > 0)
tab[j++] = tab[i];
tab[j] = 0;
}
5
Téléchargement