Cours de C les types structurés et allocation dynamique

Alignement

● adresse d’une structure = adresse de son premier champ
● pour les autres champs, le compilateur fait de l’alignement pour avoir, selon l’implémentation:
– des adresses multiples de la taille des données
– des adresses multiples de la taille des pointeurs
– …
● taille variable suivant l’ordre des champs:
struct ta {
struct to {
char a; int a;
int b; sizeof(to)=12 char b; sizeof(ta)=8
char c; char c;
char d; char d;
}; };
● ne jamais essayer de deviner l’adresse d’un champ ou la taille d’une structure:
– noms des champs
– sizeof

Initialisation

● struct to t={val0,val1,…,valn-1};
struct to t={‘a’,145,’y’,’u’};
● seulement lors de la déclaration
int main(int argc,char* argv[]) {
struct to t;
t={‘a’,145,’y’,’u’};
printf(« %c %d %c %c\n »,t.a,t.b,t.c,t.d); return 0;
}
$>gcc -Wall -ansi struct.c struct.c: Dans la fonction « main »:
struct.c:33: erreur d’analyse syntaxique avant le jeton « { »

Opérations

● l’affectation fonctionne
int main(int argc,char* argv[]) {
struct to z={‘a’,145,’y’,’u’};
struct to t=z;
printf(« %c %d %c %c\n »,t.a,t.b,t.c,t.d); return 0;
}
● attention: si un champ est un pointeur, on ne copie que l’adresse
● pas d’opérateur de comparaison
⇨ à écrire soi-même

Structures en paramètres

● les structures sont passées par valeur!
– non modifiables
– peuvent occuper beaucoup de place sur la pile (tableaux)
– temps de recopie
● toujours les passer par adresse
struct array { struct array {
int t[100000]; int t[100000];
int size; int size;
}; };
void f(struct array a) { void f(struct array* a) {
int i; int i;
for (i=0;i<a.size;i++) { for (i=0;i<(*a).size;i++) {
} printf(« %d\n »,a.t[i]); } printf(« %d\n »,(*a).t[i]);
} }
attention au parenthésage: (*a).t[i] ≠ *a.t[i]
● passage d’adresse =
– gain de temps
– gain d’espace sur la pile
– on peut modifier la structure
● notation simplifiée: (*a).size = a->size
void f(struct array* a) { void f(struct array* a) {
int i; int i;
for (i=0;i<(*a).size;i++) { for (i=0;i<a->size;i++) {
} printf(« %d\n »,(*a).t[i]); } printf(« %d\n »,a->t[i]);
} }

Retourner une structure

● on peut, mais c’est une opération de copie sur la pile
● mêmes problèmes que pour le passage par valeur
● à éviter absolument
● il faudra utiliser de l’allocation dynamique

Les unions

union toto { type1 nom1; type2 nom2; …
typeN nomN;
};
zone mémoire que l’on peut voir soit comme un type1, soit comme un type2, etc.
● s’utilisent comme les union toto {
char a;
structures float b;
};
void foo(union toto* t) {
t->a=’z’;
}

● taille = taille du plus grand champ
● au programmeur de savoir quel champ doit être utilisé  union toto {
char a;
char s[16];
};
int main(int argc,char* argv[]) {
union toto t;
strcpy(t.s, »coucou »);
t.a=’$’;
printf(« %s\n »,t.s);
return 0;
}
$>./a.out
$oucou
};
● peuvent être utilisées anonymement dans une structure union student { char login[16] int id;
● utiles quand on doit manipuler des informations exclusives les unes des autres
struct student {
char name[256];
union {
char login[16];
int id;
};
};

Unions complexes

● on peut mettre des structures (anonymes) dans les unions
union color {
/* RGB representation */
struct {
unsigned char red,blue,green;
};
/* 2 colors: 0=black 1=white */
char BandW;
};
on peut utiliser soit red, blue et green, soit BandW

Quel(s) champ(s) utiliser ?

● encapsulation dans une structure avec un champ d’information
struct color {
/* 0=RGB 1=black & white */
char type;
union {
/* RGB representation */
struct {
unsigned char red,blue,green;
};
/* 2 colors: 0=black 1=white */
char BandW;
};
};

Les énumérations

● enum nom {id0,id1,…,idn-1};
● si on a une variable de type enum nom, elle pourra prendre les valeurs idi
enum gender {male,female};
void init(enum gender *g,char c) {
*g=(c==’m’)?male:female;
}
● valeurs de type int
● par défaut, commencent à 0 et vont de 1 en 1
● on peut les modifier
enum color {
blue=45,
green, /* 46 */
red, /* 47 */
yellow=87,
black /* 88 */
};
● on peut avoir plusieurs fois la même
valeur
enum color {
blue=45, BLUE=blue, Blue=blue,
green/* =46 */, GREEN=green, Green=green
};
int main(int argc,char* argv[]) {
printf(« %d %d %d %d %d %d\n »,
blue,BLUE,Blue,green,GREEN,Green); return 0;
}
$>./a.out
45 45 45 46 46 46

Contrôles des valeurs

● contrairement aux espoirs du programmeur, pas de contrôle!
enum gender {male=’m’,female=’f’};
enum color {blue,red,green};
int main(int argc,char* argv[]) {
enum gender g=’z’;
enum color c=g;
/* … */
return 0;
}
● seul contrôle: avec -Wall, gcc indique s’il manque des cas dans un switch (quand il n’y a pas de default)
enum color {blue,red,green,yellow};
void foo(enum color c) {
switch (c) { /* … */ break;
case blue:
case red: /* … */ break;
$>gcc -Wall -ansi enum.c }
}
enum.c: Dans la fonction « foo »:
enum.c:24: AVERTISSEMENT: valeur d’énumération « green » n’est pas
traitée dans le switch
enum.c:24: AVERTISSEMENT: valeur d’énumération « yellow » n’est pas
traitée dans le switch

Déclaration de constantes

● si on veut juste déclarer des constantes, on peut utiliser une énumération anonyme
enum {Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};
char* names[]={« Monday », »Tuesday », »Wednesday », »Thursday », »Friday », « Saturday », »Sunday »};
void print_day(int day) {
printf(« %s\n »,names[day]);
}
int main(int argc,char* argv[]) {
print_day(Saturday);
return 0;
}

Combinaison union/enum

● quand on utilise une union, c’est plus propre de décrire les alternatives avec une énumération:
enum cell_type {EMPTY,BONUS,MALUS,PLAYER,MONSTER};
struct cell {
enum cell_type type;
union {
Bonus bonus;
Malus malus;
Player player;
Monster monster;
};
};

typedef

● typedef type nom;
● permet de donner un nom à un type, simple ou composé
● pratique pour éviter de recopier les mots-clés struct, union et enum
typedef signed char sbyte;
typedef unsigned char ubyte;
typedef struct cell Cell;
typedef enum color Color;

Cours gratuitTélécharger le cours complet

Télécharger aussi :

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *