Cours C 3 Tableaux fonctions et passages d adresses
Les tableaux
Tableaux statiques
B Un “objet” statique est un objet dont l’emplacement en mémoire est réservé lors de la compilation (et pas `a l’exécution).
B La taille d’un tableau statique doit donc ˆetre connue 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’être connue `a la compilation !
Mais on ne peut toujours pas déclarer : int tab[];
B Le type des éléments du tableau doit ˆetre spécifié lors de la déclaration :
type nomTab[taille];
int t[N];
double cosinus[360];
char vowel[6]={'a','e','i','o','u','y'};
char* name[]={"Smith","Doe","X"};
Les tableaux
void init(int value[]) { int i;
for (i=0;i<N;i++) { value[i]=i;
}
}
void test_sizeof(int t[]) {
printf("sizeof(t)=%d bytes\n",sizeof(t)); } $>./a.out
sizeof(t)=4 bytes
m: 60 bytes
int main(int argc,char* argv[]) { int m[15];
test_sizeof(m);
printf("m: %d bytes\n",sizeof(m)); return 0;
}
…
Tableaux à une dimension
Les variables que nous avons utilisées jusqu’à présent ne pouvaient contenir qu’une seule valeur à un moment donné. On les appelle des variables scalaires. Il existe en programmation de nombreuses structures de données plus ou moins élaborées pouvant contenir plusieurs valeurs. La plus simple et la plus répandue d’entre elles est le tableau. L’élément mathématique équivalent au tableau en programmation est la matrice. Comme elle, un tableau peut avoir un nombre quelconque de dimensions. Nous allons tout d’abord voir l’équivalent du vecteur mathématique, le tableau à une dimension. Un exemple de déclaration est le suivant : float tab[5];
Comme pour une variable :
…
– connaître la taille grâce à une constante
– passer la taille en paramètre
– utiliser un élément marqueur comme le '\0' pour les chaînes
Débordement
$>./a.out
33 33
33 33
33 33
33 33
33 33
33 33
33 33
33 33
2293600 33
2009000225 33
Tableaux à n dimensions
Tableaux à n dimensions
t[0][0] t[0][1] t[0][2] t[1][0] t[1][1] t[1][2]
@+0 @+4 @+8 @+12 @+16 @+20
– pareil en théorie, pas en pratique:
for (i=0;i<2;i++) for (j=0;j<3;j++)
for (j=0;j<3;j++) for (i=0;i<2;i++)
...t[i][j]... ...t[i][j]...
Tableaux à n dimensions
$>./a.out
4 seconds
32 seconds
Les fonctions
– un nom
– des paramètres
– un type de retour
float average(int t[]) {
float sum=0;
int i;
for (i=0;i<N;i++) {
sum=sum+t[i];
}
return sum/N;
}
– en valeur de retour: pas de valeur de retour
void print_sum(int a,int b) { /* ... */
}
– en paramètre: pas de paramètres (facultatif) char get_upper_char(void) {
char get_upper_char() {
/* ... */ /* ... */
} }
void print_sum(float a,float b) { printf("%f+%f=%f\n",a,b,a+b); int print_sum2(float a,float b) { printf("%f+%f=%f\n",a,b,a+b);
} }
$>gcc -Wall -ansi function.c
function.c: In function `print_sum2':
function.c:25: warning: control reaches end of non-void function
Valeurs de retour
– printf, scanf
$>gcc -Wall -ansi function.c
function.c: In function `main':
function.c:8: void value not ignored as it ought to be
Le cas de main
– return quitte le programme et
– renvoie le code de retour du programme
Paramètres de main
Nombre de paramètres, y compris l'exécutable
Tableau de chaînes contenant les paramètres
$>./a.out AA e "10 2"
arg #0=./a.out
arg #1=AA
arg #2=e
arg #3=10 2
Écrire une fonction
int minimum(int a,int b) { return (a<b)?a:b; }
int main(int argc,char* argv[]) { int min=minimum(4,5); printf("min=%d\n",min);
return 0;
}
Les paramètres
Définir le prototype
– mettre un commentaire
– renvoyer un code d'erreur
– afficher un message et quitter le programme
Le commentaire
/**
* Copies the array 'src' into the 'dst' one.
* 'dst' is supposed to be large enough.
*/
void copy(int src[],int dst[]);
Le code d'erreur
int init(int t[],int size) {
if (size<=0) return 0;
int i;
for (i=0;i<size;i++) t[i]=0;
return 1;
}
– on doit toujours pouvoir distinguer un cas d'erreur d'un cas normal
Le code d'erreur
/**
* Returns the length of the given
* string or -1 if HULL.
*/
int length(char* s) {
if (s==HULL) return -1;
int i;
for (i=0;s[i]!='\0';i++);
return i;
}
-1 : on ne peut pas savoir si on a une erreur ou si le minimum est -1
Le code d'erreur
int quotient(int a,int b,int *res) {
if (b==0) return 0;
*res=a/b;
return 1;
}
L'interruption du programme
– plus de mémoire
– erreurs d'entrées-sorties
– mauvais paramètres passés au programme
List* new_List(int n,List* next) {
List* l=(List*)malloc(sizeof(List));
if (l==NULL) {
fprintf(stderr,"Not enough memory !\n");
exit(1);
} /* On met un message parlant */
l->n=n; /* et non pas quelque chose */
l->next=next; /* d'inutile comme "Error" */
return l;
}
Tests d'erreur
else inutiles
⇨ améliore la lisibilité
int get value(char* s) {
_
if (s==HULL) return -1;
if (!isdigit(s[0])) return -1;
int ret=s[0]-'0';
int i=1;
while (isdigit(s[i])) {
ret=ret*10+s[i]-'0';
i++;
}
return ret;
}
Esthétique des fonctions
– commentaires
– noms explicites
– indentation
Passage d'adresse
Passage d'adresse
int foo=14; add_3(foo);
foo
a=a+3; printf("foo=%d\n",foo);
17 a
...
14 foo 14 foo
Passage d'adresse
⇨ donner l'adresse de foo pour pouvoir modifier cette zone mémoire-là
– &abc = adresse mémoire de la variable abc
– comme dans scanf, qui a besoin de savoir où stocker les résultats de la saisie
Passage d'adresse
void copy(int a,int *b);
*a différent dans char *a et float *a
Passage d'adresse
Passage d'adresse
foo BF73
*a=*a+3; printf("foo=%d\n",foo);
BF73 a
Passage d'adresse
– quand on doit modifier un paramètre
– quand on doit retourner plusieurs valeurs
– quand on doit retourner une ou plusieurs valeurs + un code d'erreur
Modifier une variable
/**
* Reads from the given file the first
* character that is not a new line. Returns
* it or -1 if the end of file is reached. If
* there are new lines, '*n' is updated.
*/
int read_char(FILE* f,int *n) {
int c;
while ((c=fgetc(f))!=EOF) {
if (c=='\n') (*n)++;
else return c;
}
return -1;
}
Retourner plusieurs valeurs
/**
* x/y is the irreducible fraction
* so that x/y = a1/b1 + a2/b2
*/
void add_fractions(int a1,int b1,
int a2,int b2,
int *x,int *y) {
*x=a1*b2+a2*b1;
*y=b1*b2;
int p=gcd(*x,*y);
*x=*x/p;
*y=*y/p;
} /**
* Returns the minimum of
* the given array. Its
* position is stored in '*pos'.
*/
float get minimum(float t[],
_
int *pos) {
int i;
*pos=0;
float min=t[0];
for (i=1;i<N;i++) {
if (t[i]<min) {
min=t[i];
*pos=i;
}
}
return min;
}
Avec un code d'erreur
Passage d'adresse
Passage par adresse
void foo(char s[]) { printf("(gis)\n",s); void foo(char* s) { printf("(gis)\n",s); void foo(char *c) { *c='$';
} } }
float* s : s est un tableau de float
float *s : *s est un float passé par adresse
Retourner une adresse
$>gcc -Wall -ansi var.c
var.c: Dans la fonction « uppercase »:
var.c:14: AVERTISSEMENT: fonction
retourne l'adresse d'une variable
locale
$> ./a.out
¨÷ÿ¿Õí•
ABC est sur la pile, et se fait écraser lors de l'appel à printf
Retourner une adresse
– on ne peut pas retourner un tableau
– il faudra utiliser de l'allocation dynamique
Tableaux et pile
$>./a.out
Segmentation fault
Fonction récursive
void hanoi(int n,char src,
char tmp,char dest) {
if (n==1) {
printf("%c --> %c\n",src,dest);
return; $>./a.out
A --> C
A --> B
C --> B
A --> C
B --> A
B --> C
A --> C
}
hanoi(n-1,src,dest,tmp);
printf("%c --> %c\n",src,dest); hanoi(n-1,tmp,src,dest);
}
int main(int argc,char* argv[]) { hanoi(3,'A','B','C');
return 0;
}
Fonction récursive
Fonction récursive
cas d'erreur
int sum(Tree* t,int *s) { if (t==NULL) return ERROR; *s=*s+t->value;
if (t->left!=NULL) { sum(t->left,s); int sum_(Tree* t) {
if (t==NULL) return 0;
return t->value+sum_(t->left)
+sum_(t->right);
}
}
if (t->right!=NULL) {
sum(t->right,s);
}
return OK;
} int sum(Tree* t,int *s) {
if (t==NULL) return ERROR;
*s=sum_(t);
return OK;
}
Fonctions sur les chaînes
char* strcpy(char* dest,const char* src);
char* strcat(char* dest,const char* src);
size_t strlen(const char* s);
– ne doivent pas être HULL
– dest doit être assez large
Fonctions sur les chaînes
void get_email(char* first,
char* last,
char* email) { strcpy(email,first); strcat(email,"."); strcat(email,last); strcat(email,"@univ-mlv.fr"); } #define N 20
void tile(char* pattern,char* res) { int i,n=(N-1)/strlen(pattern); res[0]='\0';
for (i=0;i<n;i++) { strcat(res,pattern);
}
}
int main(int argc,char* argv[]) { char t[N];
tile("pencil",t); printf("gis\n",t); return 0;
}
$>./a.out John Doe
$>./a.out
pencilpencilpencil
Comparer des chaînes
0 si a=b
>0 si a>b
$>./a.out
0 -1 1 -1
Fonctions mathématiques
double cos(double x); double sin(double x);
double tan(double x); double sqrt(double x);
double exp(double x); double log(double x);
double log10(double x); double pow(double x,double y);
$>gcc -Wall -ansi math.c
/home/igm/paumier/tmp/ccTs2vJ2.o: dans la fonction « main »:
/home/igm/paumier/tmp/ccTs2vJ2.o(.text+0x20): référence indéfinie vers « cos »
collect2: ld a retourné 1 code d'état d'exécution
$>gcc -Wall -ansi math.c -lm
Nombres aléatoires
– donne un n entre 0 et RAND MAX
_
– initialise le générateur avec une graine
time_t time(time_t*); dans time.h
Nombres aléatoires
#include #include #include
int main(int argc,char* argv[]) {
int i;
srand(time(HULL));
for (i=0;i<10;i++) {
printf("%d ",rand()%20);
}
printf("\n");
return 0;
} $>./a.out
0 10 5 5 9 5 7 6 7 11
$>./a.out
7 19 5 16 11 18 12 0 2 15