title: Manipulation de pointeurs number: 4 —
Le but de ce TP est de manipuler les pointeurs, il existe d’autre solutions parfois plus efficace mais elles ne seront pas discutés ici.
Rappel:
size_t
représente ununsigned int
ouunsigned long
en fonction de la plate-forme utilisé. Si ce type vous pose problème utilisez un des typesunsigned
de votre choix.
De la même manière, un maximum de prototypes sont écrits avec des
const
, pour vous habituer à les lires. Si ceconst
vous embête, ignorez-le !
Comme pour les TPs précédents, prévoyez des main pour tester vos fonctions.
Écrire la fonction swap
qui inverse la valeur de ses paramètres entiers.
Cette fonction ayant été écrite en cours, il vous est demandé de ne pas la
copier/coller mais bien de la réécrire tout seul.
En utilisant swap
, écrire la fonction order
qui s’assure que ses deux
paramètres entiers sont, à la fin de l’exécution, dans l’ordre croisant.
On veut écrire une fonction qui retourne en même temps le minimum et le maximum d’un tableau. On se propose d’utiliser les pointeurs pour arriver à ce résultat (vous pouvez faire le parallèle avec retourner un tableau).
Écrire la fonction void min_max(int l, int t[], int *min, int *max)
telle que
après l’appel min
(resp. max
) représentent le minimum (resp. le maximum) du
tableau.
NULL
, améliorer cette fonction de manière à ce qu’elle
retourner que le min ou que le max.(r)Écrivez, en n’utilisant que la notation pointeur, les fonctions :
char *stpcpy(char *dst, const char *src)
, qui copie la chaine source dans
la destination. Cette fonction retourne un pointeur sur le 0 terminal de la
destination. Remarque: il s’agit d’une variante de strcpy
très utile.
size_t strlen(const char *s)
, qui retourne la taille de la chaine s
.
int strcmp(const char *s1, const char *s2)
, qui compare les chaines s1
et
s2
et retourne un nombre négatif si s1
avant s2
dans l’ordre
lexicographique, positif si s1
est après s2
, zéro s’il s’agit des mêmes
chaines.
char *strcat(char *s1, const char *s2)
, qui concatène la chaine s2
à la
chaine s1
et retourne un pointeur sur s1
.
Écrivez la fonction array_zero
qui étant donné un pointeur (char*
) et un
nombre d’octets, remplis ce tableau de zéro. Peut on l’utiliser avec tous les
types de tableaux ? A quoi faut-il faire attention pour que cela marche.
Remarque: la même fonction avec un paramètre de type
void*
est définie dansstring.h
(oustrings.h
) sous le nom debzero
. Il est cependant déconseillé de s’en servir depuis 2001, la fonction memset (cf exercice suivant) doit lui être préférée. Depuis 2008 cette fonction a été retirée de la norme.
Il est enfin tant de jouer avec le type polymorphe (ou amorphe :) ) void *
.
Au cas où je n’en ai pas (ou pas assez) parlé en cours. Le type polymorphe
void *
est compatible avec tous les types de pointeurs (en lecture comme en écriture). Il n’est cependant pas possible d’utiliser l’opérateur*
sur ce type, vous ne pouvez pas ni lire ni écrire dans le néant. Vous ne pouvez pas non plus faire d’arithmétique de pointeurs survoid *
puisqu’il n’y a pas de taille au néant. Mais à quoi sert-il ? Il sert a passer des arguments génériques. En général la premiere chose à faire dans une fonction qui reçoit des paramètres typés parvoid *
est de les “transformer” en quelque chose de plus pratique, par exemple,char *
voireunsigned char *
. Par exemple :
void foo(void *v)
{
unsigned char *c = v;
c[0] = 0;
}
// Je vous l'accorde cette fonction est plus qu'inutile ...
// elle est limite dangereuse !
Écrivez les fonctions (consultez le manuel pour en savoir plus) :
void* memcpy(void *dst, const void *src, size_t len)
, qui copie la zone
pointé par src dans dst sur len octets. Elle retourne le pointeur dst.int memcmp(void *src, void *dst, size_t len)
, qui compare les zones
mémoires src
et dst
sur une longueur maximale len
. (voir strcmp
).void *memset(void *src, int c, size_t len)
. La fonction memset()
écrit
len
octets de la valeur c
(convertie vers un unsigned char
) vers la
zone de mémoire src. Cette fonction retourne son premier argument.void swab(const void *src, void *dest, size_t nbytes)
. Cette
fonction copie n
-octets de src
vers dest
en permutant les octets pairs
et les impairs.memset
,
qui tire parti de cette caractéristiques. Au final sous quelle condition
cette version est plus efficace.Nous nous proposons de réaliser un allocateur mémoire simpliste (et inefficace). Un allocateur, en général, est constitué de deux fonctions une qui alloue de la mémoire (si elle est disponible), et une autre qui restitue la mémoire allouée.
Dans le cadre de l’exercice nous ajouterons des restrictions qui n’auraient pas
lieu d’être dans un système généraliste. Tout d’abord la mémoire que nous
allouerons sera limitée à des longs, de plus, ces entiers ne pourrons pas
contenir la valeur minimale LONG_MIN
(définie dans limits.h
). En effet,
nous nous servirons de cette valeur pour définir qu’une case mémoire est
disponible. Enfin la mémoire elle même sera réservée (une fois n’est pas
coutume) dans une tableau global (variable globale), nommé pool
.
UNUSED
à une valeur choisie judicieusement
(LONG_MIN
).void release(long *ptr, size_t length)
, qui remplit la
zone pointée (le tableau quoi) ptr par la valeur UNUSED.void intialize_pool()
qui initialise pool
à UNUSED
.
Pensez à réutiliser ce que vous avez déjà écrit (et ce, sans copier/coller).*allocate(size_t length)
qui recherche dans le
tableau pool la première zone contigüe contenant au moins length UNUSED
d’affilée. Si il n’existe pas une telle zone la fonction retournera NULL
,
sinon le pointeur vers le début de cette zone sera retourné; de plus la zone
sera remplie de zéros.Pour aller plus loin, on se propose de réaliser la fonction int
lower_triangular_matrix(int n, int m, long *matrix[])
, qui remplira le tableau
matrix par n
lignes de tailles variant de 1
à m
. Cette fonction retourne
zéro si tout c’est bien passé autre chose sinon. Si n
est supérieur à m
nous répéterons des lignes de tailles m
.
Pour tester cette fonction, réalisez la fonction
print_lower_triangular_matrix
… et testez :)