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++); urn 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: // urn n movl n(%ebp), %eax 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 ; urn 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) // urn dst movl dst(%ebp), %eax 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 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