• Aucun résultat trouvé

Tableaux et Matrices

N/A
N/A
Protected

Academic year: 2022

Partager "Tableaux et Matrices"

Copied!
8
0
0

Texte intégral

(1)

Conception de structures de donn´ ees

Cours 1

Tableaux, pointeurs, allocation dynamique

11 f´evrier 2013

Struct 1/32

Avant de commencer

page web du cours :

http://monge.univ-mlv.fr/~pivoteau/STRUCT/index.html B planning, dates d’Exam

B transparents de cours, sujets de TD

B sujets de TP, programmes, quelques corrections B bibliographie et lien utiles

7 s´eances de cours B lundi, 10h30 - 12h30

environ 14 s´eances de TD/TP B TD en demi-groupes

B TP en quarts de groupe, `a rendre.

contrˆole des connaissances :

B contrˆole continu : TPs, partiel (mi-semestre) B exam fin de semestre

B projet (avril - juin)

Struct 2/32

Tableaux et Matrices Pointeurs

But du cours

Ce n’est pas un cours de programmation !

... mˆeme si il y aura de la programmation quand mˆeme.

L’objectif est d’apprendre `a CONCEVOIRdes structures de donn´ees et les algorithmes qui les manipulent.

... autrement dit : il faut apprendre `a r´efl´echir.

Chaque COURS sera en partie :

de l’algorithmique (des structures de donn´ees) ;

de l’implantation dans un langage de programmation (C) ; En TD : les bases et les exemples fondamentaux.

En TP : implantation des structures vues en cours et en TD et utilisation pour r´esoudre diff´erents probl`emes.

B Tout ce qui est vu en TD (fini ou non) doit ˆetre su.

B Tous les TPs sont `a finir et `a rendre.

Tableaux et Matrices Pointeurs

Rappels :

Tableaux et Matrices

(2)

Tableaux statiques

B Un “objet” statique est un objet dont l’emplacement en m´emoire est r´eserv´e lors de la compilation (et pas `a l’ex´ecution).

B La taille d’un tableau statique doit donc ˆetreconnue lors de la compilation (C Ansi) ; c’est :

soit une constante enti`ere : 4, 18, 150, ...

soit une constante symbolique : #define N 1000 B en C 99 : variable length arrays. la taille n’a plus besoin d’ˆetre connue `a la compilation !

Mais on ne peut toujours pas d´eclarer : int tab[];

B Le type des ´el´ements du tableau doit ˆetre sp´ecifi´e lors de la d´eclaration :

type nomTab[taille];

Struct 5/32

D´ eclarer et initialiser un tableau (statique)

#define TAILLE 100 ...

int i;

int n=10;

int tab0[n]; /* !! n n’est pas constant (ok en C 99)*/

int tab1[TAILLE];

char tab2[20];

short tab3[]={0,1,2,3,4,5,6,7,8,9}; /* taille = 10 */

double tab4[]={12.2,3.0,8.1,4.777}; /* taille = 4 */

char mot[]="Toto"; /* {’T’,’o’,’t’,’o’,’\0’} */

for(i=0; i<=TAILLE-1; i++) tab1[i]=i*n;

for(i=0; i<20; i++) tab2[i]=’a’+i;

Struct 6/32

Tableaux et Matrices Pointeurs

Parcourir un tableau

En C, tousles tableaux commencent `a l’indice 0.

Dans un tableau de longueur n, on peut acc´eder aux cases d’indice 0 `a n−1.

Une tentative d’acc`es `a une case d’indice ≥n provoquera un r´esultat al´eatoire et risque en particulier de provoquer une segmentation fault.

for(i=0;i<TAILLE;i++) printf("%d ", tab1[i]);

for(i=TAILLE-1;i>=0;i--) printf("%d ", tab1[i]);

Dans le cas d’une chaˆıne de caract`eres :

soit on connaˆıt sa taille et on peut proc´eder de mˆeme ; soit on utilise lasentinelle ’\0’pour stopper le parcours.

for(i=0; mot[i]!=’\0’; i++) putchar(mot[i]);

Tableaux et Matrices Pointeurs

Tableaux et fonctions

Attention : TABLEAU = POINTEUR! !

la variable nom tableau contient l’adresse de la premi`ere case du tableau.

B Cons´equence : un tableau pass´e en param`etre d’une fonction peut ˆetre modifi´e! !

MAIS : une fonction ne peut pas renvoyer un tableau statique cr´e´e dans cette fonction.

Pourquoi ? Parce que l’espace r´eserv´e (allou´e) au tableau dans la fonction est d´etruit `a la sortie de la fonction.

Par contre, on peut passer en param`etre de la fonction un tableau d´ej`a d´eclar´e et le modifier.

(3)

Exemple

void afficheTab(int tab[], int lgTab){

int i;

for(i=0;i<lgTab;i++) printf("%d ",tab[i]);

printf("\n");

}

void modifTab(int tab[], int i, int elem){

tab[i]=elem;

}

int main (int argc, char *argv[]){

int tab[]={0,1,2,3,4,5,6,7,8,9}; /* taille = 10 */

afficheTab(tab,10);

modifTab(tab,5,-1);

afficheTab(tab,10);

return 0;

}

Struct 9/32

D´ eclarer et initialiser une matrice

Matrice = Tableau `a 2 dimensions.

Matrice = Tableau dont les cases sont des tableaux.

Bcomme pour un tableau classique, on indique sa taille lors de la d´eclaration(taille constante) :

int matrice[2][3];

Bcomme pour un tableau classique, on peut l’initialiser lors de la d´eclaration...

int matrice[2][3] = { { 1 , 2 , 3 }, { 4 , 5 , 6 } }; B ... ou apr`es

matrice[0][0] = 1;

matrice[0][1] = 2;

matrice[0][2] = 3;

matrice[1][0] = 4;

...

Struct 10/32

Tableaux et Matrices Pointeurs

Parcourir une matrice

int i,j;

int matrice[3][5];

for(i=0;i<3;i++) for(j=0;j<5;j++)

matrice[i][j]=i+j;

0 1 2 3 4 1 2 3 4 5 2 3 4 5 6

0 1 2 1 2 3 2 3 4 3 4 5 4 5 6

ou ?

B Convention :

type nomMatrice[nb lignes][nb colonnes];

B matrice[i][j] → case sur la ie ligne, dans la je colonne .

Tableaux et Matrices Pointeurs

Pointeurs

(4)

Rappels

Vision abstraite :

B Un pointeur est une variable “pointant”

vers un emplacement en m´emoire.

En pratique :

B Un pointeur est une variable qui contient l’adresse m´emoire d’une variable.

B Donc : pointeur = adresse m´emoire (entier).

L’espace m´emoire point´e peut ˆetre allou´e : de fa¸con statique(d´ej`a vu) :

int i = 3; et pourquoi pas : int *p;

int *p = &i; *p = 3; ? ?

ou dynamiquement.

int *q = (int *) malloc( sizeof(int) );

*q = 4;

Struct 13/32

Rappels - suite

2 op´erateurs sp´ecifiques :

R´ef´erencement :&donne l’adresse d’une variable, (un pointeur sur cette variable) :

char c = ’a’;

char *p = &c; /* p est de type (char *) */

D´er´ef´erencement, indirection :*donne la valeur point´ee par un pointeur (la valeur `a l’adresse correspondante) :

printf("%c", *p); /* affiche a */

Exemples (Kernighan et Ritchie) : int x = 1, y = 2, z[10];

int *pi;

pi = &x;

y = *pi; /* y=*(&x) */

*pi = 0;

pi = &z[0]; /* pi=z */

Struct 14/32

Tableaux et Matrices Pointeurs

1

x

2

pi y

z

1

x

1

pi y

z

0

x

1

pi y

z

0

x

1

pi y

z

1. pi = &x; 2. y = *pi;

3. *pi = 0; 4. pi = &z[0];

Tableaux et Matrices Pointeurs

D’autres exemples

D´eclarations :

int i=6, b, *p, *pi, t[10]={0};

struct {int x, y;} *q; /* pointeur sur struct. anonyme */

int *t1[10]; /* tableau de pointeurs sur entier */

int (*t2)[10]; /* pointeur sur tableau d’entiers */

void *g; /* pointeur g´en´erique */

Affectations et arithm´etique : pi = &i;

p = t;

t = p; /* NON ! pas d’affectation de tableau. */

*p = 6; /* m´emoire non allou´ee ! */

p = i;

p = &t[3];

*pi += 1; /* incr´emente *pi de la taille du type point´e*/

b = *t+1; /* b=t[0]+1 */

b = *(t+1); /* b=t[1] ... attention aux priorit´es !*/

(5)

Rappel ? Priorit´ es des op´ erateurs

Cat´egorie d’op´erateurs Op´erateurs Assoc.

( ) [ ] . −> G→D

unaires − ++ −− ! ˜ ∗ & sizeof (type) D→G

mult., div., mod. * / % G→D

addition, soustraction + − G→D

binaires de d´ecalage << >> G→D

relationnels < <= > >= G→D

de comparaison == ! = G→D

et binaire & G→D

ou exclusif binaire ˆ G→D

ou binaire | G→D

et logique && G→D

ou logique || G→D

conditionnel ?: D→G

affectation = + = −= ∗= etc... D→G

virgule , G→D

Struct 17/32

void * et NULL

Il est possible de d´efinir un pointeur sur void , c’est-`a-dire sur quelque chose qui n’a pas de type pr´ed´efini. Un

pointeur de typevoid * est un pointeur : universel,

g´en´erique, polymorphe.

On utilisevoid * quand on ne connait pas (`a priori) le type point´e.

B Par exemple, le type de retour de malloc est void *.

! Pas d’indirection (*), ni d’arithm´etique sur un void *.

Un pointeur doit de pr´ef´erence ˆetre typ´e !

Il existe un pointeur qui ne pointe sur rien : NULL.

int *p = NULL;

Struct 18/32

Tableaux et Matrices Pointeurs

Passage de param` etres par r´ ef´ erence

En C, les param`etres des fonctions sont pass´es par valeur.

C’est une COPIEde la valeur de l’argument qui est manipul´ee dans la fonction : donc on ne peut pas modifier une variable pass´ee en param`etre d’une fonction.

Solution : Utiliser des pointeurs sur les variables `a modifier, c-`a-d passer les param`etres par r´ef´erence par exemple :

void swap(int a, int b){ int tmp = a;

a=b; =>

b=tmp;

}

int a=1,b=2;

swap(a,b); /* NON: a=1, b=2 */

swap2(&a,&b); /* OUI: a=2, b=1 */

void swap2(int *a, int *b){ int tmp = *a;

*a=*b;

*b=tmp;

}

Tableaux et Matrices Pointeurs

Pointeurs et tableaux

... mais lorsque l’on passe un tableau en param`etre d’une fonction on peut le modifier ! Pourquoi ? Parce que le nom du tableau est en fait un pointeur vers la premi`ere case du tableau (l’adresse de d´epart du tableau).

Si on d´eclare un tableau statique et un pointeur ainsi : int tab[10];

int *p;

alors l’affectation :p=&tab[0] est ´equivalente `ap=tab et *pd´esigne tab[0].

les crochets sont une simplification d’´ecriture : tab[i] ⇔ *(tab+i)

Il existe tout de mˆeme une diff´erence entre pointeur et tableau : un nom de tableau n’est pas une variable, on ne peut donc rien affecter `a un nom de tableau, contrairement

`a un pointeur : tab = p alors que p = tab.

(6)

L’op´ erateur sizeof()

L’op´erateur sizeof() : Renvoie la taille en octets de l’argument (soit un type, soit une variable ou une expression)

short A; char B[5][10];

sizeof A →2 sizeof B →50 sizeof 4.25 →8

sizeof "abcd" →5 sizeof(float) →4 sizeof(double) →8 void fnc (int tab[]){

printf("%ld, %ld \n", sizeof(tab), sizeof(tab[0])); } int main (int argc, char *argv[]){

int t1[]={1,2,3,4,5};

int *t2= (int *)malloc(20*sizeof(int));

printf("%ld, %ld \n", sizeof(t1), sizeof(t1[0]));

printf("%ld, %ld \n", sizeof(t2), sizeof(t2[0]));

fnc(t1);

...

Struct 21/32

Allocation dynamique

#include <stdlib.h>

On a vu qu’allouer de la m´emoire statique avait un int´erˆet limit´e.

Comment faire pour r´eserver de l’espace au moment de l’ex´ecution ? B Il faut allouer la m´emoiredynamiquement.

void *malloc (size t size);

• Alloue sizeoctets ;

• Renvoie un pointeur sur la m´emoire allou´ee ;

• La zone de m´emoire n’est pas initialis´ee.

long *a = (long *)malloc( sizeof(long) );

int n = 100;

long *tab = (long *) malloc(n * sizeof(long));

struct z {int f,g;}; struct z *p;

p=(struct z *)malloc(sizeof(struct z)); /* ptr surstruct z*/

Struct 22/32

Tableaux et Matrices Pointeurs

Allocation dynamique - suite

void *calloc (size t nb, size t size);

Alloue et remplit de 0 la m´emoire n´ecessaire pournb´el´ements desize octets ; renvoie un pointeur sur la zone allou´ee.

Attention, ce n’est pas la mˆeme signature quemalloc! int n = 100;

long *tab = (long *) calloc(n, sizeof(long)); /*nfois 0 */

void *realloc (void *p, size t size);

R´eduit (ou augmente) la taille du bloc de m´emoire point´e par p

`a une taille de sizeoctets ; conserve les sizepremiers octets `a l’adressep. Le reste de la nouvelle zone n’est pas initialis´e.

int * tab;

tab = (int *) calloc ( 2, sizeof(int) );

tab[0] = 33; tab[1] = 55;

tab = (int *)realloc(tab, 3 * sizeof(int) );

tab[2] = 77;

Tableaux et Matrices Pointeurs

Allocation dynamique - suite

! Attention, il peut toujours se produire des errors lors de

l’allocation dynamique de m´emoire : il faut TOUJOURS v´erifier que le pointeur retourn´e lors de l’allocation n’est pas NULL !

int *tab1, *tab2;

tab1 = (int *) malloc( 1000*sizeof(int) );

if( tab1 == NULL ){

fprintf(stderr,"allocation rat´ee !");

exit(EXIT_FAILURE);

}

if( ( tab2 = (int *)malloc(1000*sizeof(int)) ) == NULL ){ fprintf(stderr,"allocation rat´ee !");

exit(EXIT_FAILURE);

}

(7)

Fonction pour l’allocation dynamique

void alloue(int *p, int nb){

if( (p=(int *)malloc(nb*sizeof(int))) == NULL ) error("L’allocation a ´echou´e !");

}

void alloue2(int **p, int nb){

if( (*p=(int *)malloc(nb*sizeof(int))) == NULL ) error("L’allocation a ´echou´e !");

}

int *alloue3(int nb){ int *p;

if((p=(int *)malloc(nb*sizeof(int)))==NULL) error("alloc. rat´ee !");

return p;

}

int main(void){

int *tab=NULL, *tab2=NULL, *tab3=NULL;

alloue(tab,1000);

alloue2(&tab2,1000);

tab3=alloue3(1000);

}

Struct 25/32

Lib´ eration de la m´ emoire

La m´emoire n’´etant pas infinie, lorsqu’un emplacement m´emoire n’est plus utilis´e, il est important de lib´erer cet espace.

La fonction void free( void *p ) :

• lib`ere l’espace m´emoire point´e par p

• qui a ´et´e obtenu lors d’un appel `a malloc, calloc ourealloc

• Sinon, ou si il a d´ej`a ´et´e lib´er´e avec free(), le comportement est ind´etermin´e.

• Attention, freene met pas le pointeur p `a NULL.

int i, int *tab=(int *)calloc(1000, sizeof(int));

free(tab);

int *tab2;

alloue2(&tab2,1000);

for (i=0;i<1000;i++)

*(tab2+i)=i;

afficheTab(tab,1000); /* affiche (parfois) 0 1 2 3 ... 999 */

Struct 27/32

Tableaux et Matrices Pointeurs

Copie de m´ emoire, de tableaux

#include <string.h>

void *memcpy (void *dest, const void *src, size t n) Copien octets depuis la zone m´emoire src vers la zone m´emoire dest. Les deux zones ne doivent pas se chevaucher. Si c’est le cas, utiliser plutˆot memmove.

Ces deux fonctions renvoient un pointeur sur dest.

int lg=20;

int t1[]={1,2,3,4,5};

int *t2=(int *)malloc(lg*sizeof(int)); if (t2==NULL) error("...");

int *t3=(int *)malloc(lg*sizeof(int)); if (t3==NULL) error("...");

for(i=0;i<lg;i++) t2[i]=i;

memcpy(t3, t2, lg*sizeof(int));

memmove(t3+10, t3, 10*sizeof(int));

Tableaux et Matrices Pointeurs

Copie de m´ emoire (suite)

Exemple d’utilisation de void * et memcpy pour ´ecrire une fonction d’´echange universelle :

void swap3(void *a, void *b, size_t size){

if ( (void *tmp = malloc(size)) == NULL ) error(...);

memcpy(tmp,a,size);

memcpy(a,b,size);

memcpy(b,tmp,size);

free(tmp);

}

int main(void){

int k = 3, j = 4;

printf("%d, %d \n", k, j);

swap3(&k,&j,sizeof(int));

printf("%d, %d \n", k, j);

}

(8)

Le cas des chaˆınes de caract` eres

#include <string.h>

On a vu que les chaˆınes de caract`eres sont des tableaux, donc des pointeurs... avec quelques subtilit´es.

Attention, les chaˆınes de type char * initialis´ees avec des " "

sont statiques et constantes : on ne peut pas les modifier.

char s1[] = "toto";

char *s2 = "titi";

s1[0] = ’x’; /* OK */

s2[0] = ’y’; /* NON! */

Elles peuvent ´egalement ˆetre d´eclar´ees comme des char * et allou´ees dynamiquement (malloc(), ...).

char *s3 = (char *)malloc( 5*sizeof(char) ); if (s3==NULL) error("...");

s3[0] = ’t’; /* OK */

printf(s3) /* NON! */

Struct 30/32

Chaˆınes de caract` eres - suite

#include <string.h>

printf, scanf: <stdio.h>

size t strlen (const char *s);

Renvoie longueur de la chaˆıne de caract`eres s, sans compter le caract`ere nul ’\0’ final.

char *strcpy (char *dest, const char *src)

Copie la chaˆıne point´ee par src (y compris le ’\0’final) dans la chaˆıne point´ee par dest. Les 2 chaˆınes ne doivent pas se chevaucher et destdoit ˆetre assez grande pour la copie. Renvoie un pointeur sur la chaˆıne dest.

char *strdup (const char *s)

Renvoie un pointeur sur une nouvelle chaˆıne de caract`eres qui est dupliqu´ee depuis s. La m´emoire est obtenue par malloc(), et peut (doit) donc ˆetre lib´er´ee. Renvoie un pointeur sur la chaˆıne dupliqu´ee, ou NULL s’il n’y avait pas assez de m´emoire.

Struct 31/32

Tableaux et Matrices Pointeurs

Chaˆınes de caract` eres - suite

#include <string.h>

char *strcat (char *dest, const char *src)

Ajoute la chaˆıne src `a la fin de la chaˆıne dest en ´ecrasant le

’\0’`a la fin de dest, puis en ajoutant un nouveau ’\0’ final.

Les chaˆınes ne doivent pas se chevaucher, et destdoit ˆetre assez grande pour le r´esultat. Renvoie un pointeur sur dest.

int strcasecmp(const char *s1, const char *s2);

Compare les chaˆınes chaine s1 et chaines2et renvoie un entier : - n´egatif, si chaine s1 est inf´erieure `a chaine s2;

- nul, si chaine s1est ´egale `a chaine s2;

- positif, si chaine s1 est sup´erieur `a chaines2.

char * strchr(const char *s, int c)

Recherche le caract`ere c dans la chaine s et renvoie la position de la 1ere occurrence ou de la derni`ere occurrence (strrchr).

Variantes avec la longueur : strncpy, strncat, strncmp, ...

Références

Documents relatifs

[r]

[r]

Annexe 12: Facture d’avoir N° 50 (à compléter et rendre avec la copie) Annexe 13 : Pièces de caisse. Annexe 14 :

Syntaxe de l’adressage indirect par registre.- Comme nous l’avons d´ej` a vu, pour d´esigner une valeur constante on ´ecrit cette valeur (en hexad´ecimal), pour une valeur se

Syntaxe de l’adressage indirect par registre en langage symbolique.- Comme nous l’avons d´ej` a vu, en langage symbolique, pour d´esigner une constante on ´ecrit celle-ci

A chaque ´ etape, on tire une boule dans chacune des urnes et on la met dans l’autre urne.. Tracer le graphe de la chaˆıne et ´ ecrire sa matrice

Pour “fabriquer” un espace vectoriel, il faut d’abord se donner un ensemble E (dont les ´el´ements seront appel´es vecteurs), puis donner deux op´erations, l’addition des

[r]