HomePage CV Programmation Contact RecentChanges

StructuresComplexesEnC

Après avoir vu les pointeurs ainsi que les fonctions permettant l'allocation dynamique de mémoire, on s'interesse naturelement aux strucures complexes pouvant accueillir des données. Le but est d'allouer de manière dynamique des données à l'aide de la fonction malloc. Cette fonction se trouve dans la librairie standard stdlib qu'on inclura.

Une liste chainée simple

Nous allons essayer de faire une liste chainée simple, ou plutôt une pile de donnée du type "last in, first out". La structure

struct noeud {
	int data;
	struct noeud *prec;
}

La structure est en somme assez simple. Elle est composée d'une donnée quelconque, ici un entier qu'on appelle data, et d'un pointeur vers la structure précédente (N'oublions pas qu'on est dans une pile). Nous devons donc créer une fonction push qui s'occupera d'ajouter en haut de la pile la nouvelle donnée. Mais avant ça, créons plutot un type, Noeud, à l'aide de typedef pour la structure précédente.

typedef struct noeud {
	int data;
	struct noeud *prec;
} Noeud;

La fonction push

Note : l'operateur -> est un raccourci : foo->bar est strictement équivalent à (*foo).bar.

void push (Noeud **dernier, int data) {
	Noeud *nouveau;
	nouveau = malloc(sizeof(Noeud));
	if (!nouveau) return; // En cas d'echec d'allocation
	nouveau->data = data;
	nouveau->prec = *dernier;
	*dernier = nouveau; // C'est une pile, donc on s'interesse uniquement au dernier entré.
}

La fonction view

Tant qu'à faire, et surtout histoire de voir ce qu'il y a dans notre pile, on va faire une fonction qui va afficher les éléments qui sont dans la pile.

void view (Noeud *dernier) {
	while (dernier) {
		printf("La donnée : %d\n", dernier->data);
		dernier = dernier->prec;
	}
}

Ici on n'a pas besoin d'une variable temporaire de type pointeur sur type Noeud à cause de l'appel par valeur. En effet le pointeur dernier est copié et view n'y touche pas. C'est pour cette raison que dans la fonction push on a un double pointeur sur dernier, car on veut le modifier.

La fonction clear

void clear (Noeud *dernier) {
	Noeud *tmp;
	while (dernier) {
		tmp = dernier->prec;
		free(dernier);
		dernier = tmp;
	}
}

Au final

Voici le programme en entier :

#include <stdio.h>
#include <stdlib.h>

typedef struct noeud {
	int data;
	struct noeud *prec;
} Noeud;

void push (Noeud **dernier, int data) {
	Noeud *nouveau;
	nouveau = malloc(sizeof(Noeud));
	if (!nouveau) return;
	nouveau->data = data;
	nouveau->prec = *dernier;
	*dernier = nouveau;
}

void view (Noeud *dernier) {
	while (dernier) {
		printf("La donnée : %d\n", dernier->data);
		dernier = dernier->prec;
	}
}

void clear (Noeud *dernier) {
	Noeud *tmp;
	while (dernier) {
		tmp = dernier->prec;
		free(dernier);
		dernier = tmp;
	}
}

int main(void) {
	Noeud *pile = NULL;
	push(&pile, 8);
	push(&pile, 7);
	push(&pile, 55);
	view(pile);
	clear(pile);
	view(pile);

	return 0;
}

[:ListeChaineeDouble?]

Une liste chainée double

C'est quasiment la même chose que les listes chainées simples à part que chaque structure pointe vers la structure précedente ET la structure suivante, si cette dernière existe. Si elle n'existe pas, vous avez plusieurs choix possibles :

Etant donné que l'on travaille sur des choses très basiques, largement définies dans la glib, on peut faire à peu près n'importe quoi. A vous de voir.

#############         -->#############
#  donnees  #        /   #  donnees  #
#-----------#       /    #-----------#
#  suivant  #------/     #  suivant  #--------> etc
#-----------#            #-----------#
# precedent #      ------# precedent #
#############<----/      #############

La structure

Pour changer un peu, nous allons prendre un type de données plus complexe que le simple entier data vu précédement. Je définis donc une structure data qui contient les données. Elle est suivie de la structure Noeud qui représente les briques de la future liste chainée.

Vu qu'on va travailler avec des pointeurs sur des tableaux de caractères (char *nom et char *prenom), il vaut mieux créer tout de suite une fonction qui va s'occuper de dupliquer les chaines, histoire qu'on puisse attribuer facilement des chaines aux pointeurs nom et prenom de la structures data.

Toujours dans le même soucis des données, viens la fonction qui va copier une structure data dans une autre.

On peut maintenant attaquer sans trop de mal la fonction push de tout à l'heure. J'ai décidé de ne pas déplacer le pointeur racine. Il pointera toujours sur la première brique. On aurait très bien pu, et ça aurait rejoint l'exemple de la liste chainée simple, copier l'adresse de la dernière brique ajoutée dans racine. Cependant, les fonctions clear et view auraient été différentes. Peu importe.

Le tout

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define taille sizeof(char)*256

typedef struct data {
	char *nom;
	char *prenom;
	unsigned int age;
} Data;

typedef struct noeud {
	Data fiche;
	struct noeud *prev;
	struct noeud *next;
} Noeud;

void push (Noeud **racine, Data fiche) {
	Noeud *cursor = *racine;
	Noeud *nouveau;
	nouveau = malloc(sizeof(Noeud));
	if (!nouveau) return;
	// On met les données dans nouveau
	nouveau->fiche = fiche;
	// Si nouveau est le premier véritable élément de la liste
	if (!*racine) 
		*racine = nouveau;
	// sinon on link
	else {
		while (cursor->next)
			cursor = cursor->next;
		nouveau->prev = cursor;
		cursor->next = nouveau;
	}
}	

void view (Noeud *racine) {
	Data fiche;
	while (racine) {
		fiche = racine->fiche;
		printf("Le nom: %s\tPrenom: %s\tAge: %d\n", fiche.nom, fiche.prenom, fiche.age);
		printf("-----------\n");
		racine = racine->next;
	}
}

void clear (Noeud **racine) {
	Noeud *tmp;

	while (*racine) {
		tmp = (*racine)->next;
		free(((*racine)->fiche).nom);
		((*racine)->fiche).nom = NULL;
		free(((*racine)->fiche).prenom);
		((*racine)->fiche).prenom = NULL;
		free(*racine);
		*racine = tmp;
	}
}

char *lire_ligne(FILE *flux) {
	char *p  = malloc(taille);
	char *final = NULL;
	char *ret = NULL;

	if (p != NULL) {
		fgets(p, taille - 1, flux);
		ret = strchr(p, '\n');
		if (ret != NULL)
			*ret = '\0';
		final = malloc(sizeof(char)*(strlen(p)+1));
		strcpy(final, p);
		free(p);
		p = NULL;
	}
	return final;
} 

int main(void) {
	Noeud *racine = NULL;
	Data fiche;
	// pour la saisie
	char *c;

	do {
		free(c);
		c = NULL;
		printf("Entrez l'age :\n");
		scanf("%d", &(fiche.age));
		getchar(); // à cause du \n de scanf...

		printf("Entrez un nom :\n");
		fiche.nom = lire_ligne(stdin);

		printf("Entrez un prenom :\n");
		fiche.prenom = lire_ligne(stdin); 


		push(&racine, fiche);

		printf("Voulez vous entrer une nouvelle entrée ? [o/n]\n");
		c = lire_ligne(stdin);

	} while (*c == 'o');

	view(racine);
	clear(&racine);
	view(racine);

	return 0;
}

CategorieLearning