1 - Types de données et déclarations
2 - Les structures
3 - Tableaux et chaînes de caractères
4 - Tests conditionnels
5 - Boucles d'itérations
6 - Fonctions
7 - Déclarations complexes
2 - Les structures
3 - Tableaux et chaînes de caractères
4 - Tests conditionnels
5 - Boucles d'itérations
6 - Fonctions
7 - Déclarations complexes
1 - Types de données et déclarations
1.1 - Types :
void : type non défini
enum : entier 16 bits [-32768,
32767]
int : entier 16 bits [-32768, 32767]
short ou short int : entier 16
bits [-32768, 32767]
unsigned int : entier 16 bits [0,
65535]
long ou long int : entier 32
bits [-2147483648, 2147483647]
unsigned long : entier 32 bits [0,
4294967295]
float : flottant 32 bits [3.4E-38,
3.4E+38]
double : flottant double précision
64 bits [1.7E-308, 3.4E+308]
long double : flottant 80 bits
[3.4E-4932, 1.1E+4932]
char : caractère 8 bits [-128, 127]
unsigned char : caractère 8 bits
[0, 255]
On peut ajouter à cette liste l’opérateur d’indirection * pour définir des
pointeurs.
Les pointeurs peuvent être :
near : si le pointeur doit se
limiter à adresser une zone mémoire contiguë de 64Ko.
Un pointeur near a une taille de 16 bits, soit un mot, ou encore 2 octets.
far : si le pointeur peut adresser
toute l’étendue de 1Mo (adresse réelle sur 20 bits).
Un pointeur far a une taille de 32 bits, soit un double mot, ou encore 4 octets.
1.2 - Enumérations : enum
enum jour {Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi, Dimanche};
Regroupe un ensemble de constantes, représentées par un entier. Le
premier élément vaut 0.
Exemple faisant appel à l'énumération jour :
enum jour Day; /* Day est une
énumération de type jour */
Day = Vendredi;
Day = 4; /* Ne marche pas en C++ qui
est plus contraignant au niveau
des concordances de types. Ici on tente d'affecter un entier à une variable de type enum */
1.3 - Déclaration :
< type de la variable à déclarer > < nom de la variable
>;
unsigned char Byte; /* Byte est un
caractère non signé : 1 octet de 0 à FFh */
typedef unsigned char Byte; /* idem
mais Byte est maintenant un nouveau type */
Byte octet;
2 - Les structures
struct [ étiquette ] { déclarations } [ variables ]
2.1 - Nous pouvons définir simplement l’étiquette :
struct personne
{
char nom[20];
char prenom[20];
char tel[15];
int age;
}
struct personne paul;
2.2 - Nous pouvons ne pas définir d’étiquette :
On est obligé de déclarer les variables nécessaires en fin de
structure puisqu’il n’y a pas d’étiquette.
struct
{
char nom[20];
char prenom[20];
char tel[15];
int age;
} laurent, beatrice;
2.3 - Nous pouvons définir des variables en même temps que
l’étiquette :
Il est alors possible de déclarer de nouvelles variables du type de la
structure définie.
struct personne
{
char nom[20];
char prenom[20];
char tel[15];
int age;
} laurent, beatrice;
struct personne paul;
2.4 - Définir une Structure en tant que nouveau type : typedef
typedef struct personne
{
char nom[20];
char prenom[20];
char tel[15];
int age;
} Personne ;
Personne paul, laurent, beatrice;
Personne est alors un nouveau type. Il évite en faite d’avoir à
préciser struct personne lors d'une déclaration.
typedef permet de créer cette nouvelle définition de type (typedefine).
Ou plus simplement :
typedef struct
{
char nom[20];
char prenom[20];
char tel[15];
int age;
} Personne ;
Nous pouvons aussi imaginer une structure contenant une structure :
typedef struct
{
char nom[20];
char prenom[20];
char tel[15];
int age;
struct bureau
{
int poste;
char tel[15];
char fax[15];
}
} Personne ;
2.5 - Accès par référence
Si on accède à cette structure par son nom, et donc par référence
à ce nom :
Personne inconnu;
inconnu.nom /* accède au champ nom */
inconnu.tel /* accède au champ tel */
(inconnu.bureau).tel /* accède au
champ tel
de la structure bureau */
2.6 - Accès par pointeur
Si on accède à cette structure par un pointeur sur cette structure :
Personne *ptr;
ptr->nom /* accède au champ nom */
ptr->tel /* accède au champ tel */
(ptr->bureau).tel /* accède au
champ tel de la structure bureau */
Dans ce dernier cas, nous accédons à la structure Personne par un
pointeur, mais bureau est bien une structure et non un pointeur sur une structure.
On accède donc logiquement au champ tel de la structure bureau par
référence et non par pointeur.
Notons au passage que ptr->tel pourrait aussi s'écrire (*ptr).tel puisque *ptr correspond au nom
de la structure.
2.7 - Utilité des structures : les listes chaînées
Les structures sont utiles dès qu'il est possible de créer un bloc
regroupant des informations pertinentes, pour stocker ou organiser ces informations.
L'exemple le plus simple et le plus significatif est celui d'une
gestion de liste chaînée. Une liste chaînée est une structure permettant de stocker
des données dont la taille croit, décroît selon les besoins, comme un fichier contenant
des fiches d'un répertoire téléphonique.
Au lieu de créer un tableau en mémoire dont la taille est plus ou
moins à définir à sa déclaration et dont l'encombrement mémoire est figé, les listes
chaînées apportent souplesse d'utilisation, rigueur de l'algorithme et occupation
optimale de l'espace mémoire.
On travaille sur des listes à l'aide de pointeur. Tout ce que l'on
connaît de la liste, c'est le pointeur qui va pointer son premier élément. Cet
élément aura lui même un champ pointeur que l'on fera pointer sur le suivant et ainsi
de suite, pour accéder jusqu'au dernier.
La seule contrainte de ces listes est que l'on ne peut accéder
directement à un élément. On est obligé de repartir du pointeur de tête et de
parcourir les éléments de la chaîne jusqu'à trouver l'élément recherché.
typedef struct
{
char nom[20];
char prenom[20];
char tel[15];
} Fiche;
Cette structure déjà rencontrée plus haut représente l'élément de
base de notre liste chaînée. Cet élément maillon de la chaîne porte le nom de
CELLULE. La cellule telle qu'elle est décrite ici renferme tous les champs d'informations
utiles, mais ne permet pas d'accéder à la cellule suivante. Pour ce faire il faut lui
ajouter un champs pointeur sur une structure de type Fiche
typedef struct
{
char nom[20];
char prenom[20];
char tel[15];
Fiche *suivant; /* le champ suivant est un pointeur
sur une structure */
} Fiche;
Un problème se pose dans cette déclaration : on déclare suivant
comme un pointeur sur la structure Fiche alors que cette structure est en cours de déclaration. Le
compilateur n'en connaît pas encore l'existence et générera une erreur du genre
"undefined symbol suivant" : le champ suivant ne peut être défini puisque le type Fiche n'est pas (encore)
défini.
On procédera alors de la sorte :
typedef struct cellule
{
char nom[20];
char prenom[20];
char tel[15];
struct cellule *suivant;
} Fiche;
A l'instant ou le champ suivant est déclaré le compilateur connaît struct cellule.
Rappelons que Fiche équivaut à struct cellule grâce à la présence du typedef. Mais si struct cellule est connu
dans le corps de la structure, Fiche ne l'est pas.
Voici un exemple de programme commenté:
#include<stdio.h>
#include<alloc.h>
#include<string.h>
typedef struct cellule
{
char nom[20];
char prenom[20];
char tel[15];
struct cellule *suivant;
} Fiche;
void main(void)
{
Fiche *Tete; /* tete de la liste chaînée */
Fiche *Cell1; /* Pointeur sur la première cellule */
Fiche *Cell2; /* Pointeur sur la seconde cellule */
/* Attention : nous avons déclaré des pointeurs sur des structures.
Seuls ces pointeurs sont crées. Les structures qu'ils pointent ne le sont pas encore.
Nous devons allouer de la mémoire pour que ces structures aient une existence en mémoire
*/
Cell1 = (Fiche*)malloc(sizeof(Fiche));
Cell2 = (Fiche*)malloc(sizeof(Fiche));
/* malloc alloue un bloc de mémoire de taille spécifiée, ici sizeof(Fiche) c'est à
dire la taille en octets de la structure fiche, et retourne un pointeur sur la zone
mémoire allouée. Le pointeur retourné étant générique (void*) , puisque l'on
peut allouer de la mémoire pour n'importe quel type de donnée, il faut prendre
l'habitude de faire une conversion de type, appelée CASTING. */
/* Ainsi (Fiche*) converti le pointeur générique retourné par la fonction malloc en un pointeur
sur le type de donnée Fiche. Cell1 et Cell2 sont donc des pointeurs sur des structures ayant
une existence en mémoire. */
strcpy(Cell1->nom,"PAPIN");
strcpy(Cell1->prenom,"Jean-Pierre");
strcpy(Cell1->tel,"0102030405");
strcpy(Cell2->nom,"PROST");
strcpy(Cell2->prenom,"Alain");
strcpy(Cell2->tel,"00324123456789");
/* Les champs d'informations utiles sont remplis. On va maintenant
effectuer le chaînage entre la cellule 1 et la cellule 2 */
Cell1->suivant=Cell2;
/* La cellule 2 est la dernière de la liste. Elle ne pointe sur aucune
cellule suivante. Le champ suivant de la cellule 2 doit donc contenir NULL, valeur
prédéfinie pour les pointeurs nuls. On se servira de cette valeur dans les algorithmes
pour tester l'arrivée en fin de liste chaînée. */
/* On n'est pas sensé connaître les adresse de chaque cellule. On
n'est même pas sensé connaître le nombre de cellules. On connaît seulement le pointeur
sur la première cellule appelé Tete */
Tete=Cell1;
/* Accédons aux cellules : */
printf("Fiche1 :%s\n",tete->prenom);
printf("Fiche1 :%s\n",tete->tel);
printf("Fiche2 :%s\n",tete->suivant->prenom);
printf("Fiche2 :%s\n",tete->suivant->tel);
/* il reste à libérer la mémoire précédemment allouée par malloc
*/
free(Cell1);
free(Cell2);
/* fin du programme */
}
2.8 - Schéma de fonctionnement
Tete Cell1 Cell2
Champs : N P T Suiv N P T Suiv
NULL
Ce programme n'a aucune utilité pratique dans la mesure ou les deux
fiches ont été créées, remplies et parcourues à la main. En réalité on passera par
des fonctions, si possible récursives (fonctions s'appelant elles même) puisque la
récursivité est particulièrement adaptée à ce genre de travaux dont les actions sont
principalement :
INSERTION D'UNE NOUVELLE CELLULE DANS LA CHAINE
Assure l'allocation mémoire, le remplissage des champs et le chaînage
dans la liste.
- Insérer en début de chaîne
- Insérer en fin de chaîne
- Insérer dans la chaîne selon un critère donné ( ordre croissant,
alphabétique... )
SUPPRESSION D'UNE CELLULE DANS LA CHAINE
Recherche de la cellule à supprimer, modifie le chaînage entre la
cellule qui précède et la cellule qui suit la cellule supprimée et enfin libère
l'espace mémoire alloué à la cellule que l'on supprime.
- Suppression dans la chaîne d'une chaîne donnée, selon un critère
sur un champ.
OPERATIONS SUR LA CHAINE
- Lister le contenu des cellules.
- Compter le nombre de cellules (nombre de fiches);
... etc
2.9 - Remarques
Pour simplifier l'insertion en fin de chaîne, et ne pas avoir à la
parcourir depuis le début on pourra gérer un pointeur sur le dernier élément appelé
pointeur de Queue.
Il est fréquent de trouver des listes doublement chaînées avec un
deuxième pointeur dans la structure, ce dernier pointant la cellule précédente. Il est
ainsi possible de remonter la chaîne dans l'autre sens depuis le pointeur de Queue, ou
plus simplement pour connaître l'adresse de la cellule précédent la cellule courante.
Tete Queue
Cell1 Cell2
Prec N P T Suiv Prec N P T Suiv
NULL NULL
Enfin, nous aurions pu déclarer la structure d'une autre manière :
Pour contourner le problème du nom de la structure inconnu lors de sa
description, et qui nous gênait lors de la déclaration du pointeur sur la structure on
peut procéder en deux temps :
typedef struct cellule *Cellptr; /*
Cellptr : pointeur sur la structure Fiche */
typedef struct cellule
{
char nom[20];
char prenom[20];
char tel[15];
Cellptr suivant;
} Fiche;
Il faut faire attention. Dans ce cas, le champ suivant doit être de
type Cellptr
en non Fiche*, sinon il y aura conflit de type :
Cellptr Tete;
Cellptr Cell1;
Cellptr Cell2;
Cell1=(Cellptr)malloc(sizeof(Fiche));
Cell2=(Cellptr)malloc(sizeof(Fiche));
Cell1->suivant = Cell2; /* affectation correcte : types identiques
*/
3 - Tableaux et chaînes de caractères
3.1 - Tableaux
En langage C le type tableau n’est pas prédéfini. On
l’obtient par l’emploi des crochets [ ].
int table[10]; /* table est un
tableau de 10 entiers */
Les indices de ce tableau iront alors de 0 à 9, c’est à dire que
table[10] n’est pas défini.
Il est possible d’initialiser le tableau :
int table[10] = { 0,1,2,3,4,5,6,7,8,9};
Il est important de savoir que pour le compilateur C, le nom du tableau
correspond en fait à un pointeur sur une suite d’éléments dont la taille dépend
du type, ici 16 bits pour int.
On peut donc accéder aux élément du tableau par :
int *ptr; /* ptr est un pointeur sur
un entier */
int valeur; /* un entier */
ptr = table; /* ptr pointe
maintenant sur le tableau */
valeur = (*ptr); /* contient la
valeur 0 */
ptr++; /* le pointeur augmente
automatiquement de 2 octets
puisque c’est un pointeur sur un entier */
valeur = (*ptr); /* contient la
valeur 1 */
ptr += 2; /* augmente le pointeur de
4 octets ( taille de 2 entiers int ) */
valeur = (*ptr); /* contient la
valeur 3 */
3.2 - Tableaux à plusieurs dimensions
float matix[3][4]; /* matrix est une
matrice de 3 lignes et 4 colonnes de flottants */
Les éléments d’une matrice sont rangés en mémoire
linéairement, ligne par ligne. Pour organiser son contenu en mémoire, il est donc
indispensable de connaître le nombre de colonnes pour savoir où commence la ligne
suivante.
En mémoire :
| 11 , 12 , 13 , 14 | 21 , 22 , 23 , 24 | 31 , 32 , 33 , 34 |
int matix[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; /* initialisation */
int matix[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; /* plus lisible */
float matrix [ ] [4]; /* représente
une matrice de n lignes de 4 éléments flottants */
De même :
int matix[3][4][2] ; /* matrix est
une matrice de 3x4x2 d’entiers */
En mémoire :
| 111 , 112 | 121 , 122 | 131 , 132 | 141 , 142 || 211 , 212 | 221 ,
222 | 231 , 232 | 241 , 242 || 311 , 312 | 321 , 322 | 331 , 332 | 341 , 342 |
3.3 - Chaînes de caractères
Les chaînes de caractères sont en fait des tableaux de caractères,
et comme pour un tableau, le nom de la chaîne représente en fait un pointeur sur cette
chaîne de caractères.
char nom[10]; /* nom est une chaîne
de 10 caractères */
que l’on peut initialiser :
char nom[10] = "Jean";
J | e | a | n | \0 | |||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
\0 est l'octet de fin de chaîne. Il correspond à la valeur 0 (non pas
le caractère '0', mais la valeur 0 décimale ou hexadécimale).On nomme ces chaînes des
chaînes ASCIIZ (Z pour Zéro). Il faut donc toujours prévoir un caractère pour cet
octet. Exemple : pour une chaîne de 8 caractères maximum, il faut compter 9 caractères
soit : char nom[9], qui seront numérotés de 0 à 8.
A partir du tableau ci-dessus exécutons le code suivant:
char *ptr; /* pointeur sur un
caractère (longueur : 1 octet) */
nom[4] = 'n'; /* caractère n dans
la case numéro 4 */
ptr = nom; /* ptr pointe désormais
sur la chaîne, comme nom */
ptr += 5; /* ptr pointe sur la case
numéro 5 */
*ptr = 'e'; /* caractère e dans la
case numéro 5 */
ptr++; /* ptr pointe sur la case
numéro 6 */
*ptr = 0; /* place la valeur zéro
dans la case numéro 6 */
La chaîne contient alors :
J | e | a | n | n | e | \0 | |||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
3.4 - Exemple de déclarations traitant de chaînes :
On peut déclarer un tableau de 5 chaînes de 10 caractères :
char table_string[5][10];
C'est en fait un tableau de deux dimensions.
Ou bien :
char *table_string[5];
Dans ce cas, c'est un tableau de 5 pointeurs sur des chaînes. L'espace
mémoire n'est pas réservé dans ce cas. Il faudra effectuer une allocation dynamique de
mémoire pour réserver 10 octets pour chaque chaîne pointée.
4 - Tests conditionnels
Les tests conditionnels et les boucles d'itérations on recours à
l'évaluation d'une condition pour déterminer quelle action effectuer en conséquence.
Cette évaluation retourne un entier.
L'évaluation d'une condition retourne toujours Vrai ou Faux.
L'ordinateur ne connaissant pas Vrai ou Faux, le compilateur procède ainsi :
Si le résultat de l'évaluation est différent de 0 alors la condition
vaut VRAI. En général, ce résultat vaudra 1 s'il est évalué par le compilateur. Mais
si vous essayez if(-5), la condition vaudra VRAI.
Si le résultat de l'évaluation est égal à 0 alors la condition vaut
FAUX.
4.1 - Opérateurs d'égalité :
L'opérateur d'égalité est == en non = qui est l'opérateur d'affectation.
4.2 - Opérateur de différence :
L'opérateur de différence est !=
4.3 - Autres opérateurs :
Une condition peut être composée de plusieurs conditions reliées par
des opérateurs logiques "OU", "ET" ou encore "NON".
OU logique : || exemple : A OU B A || B
ET logique : && exemple : A ET B A && B
NON logique : ! exemple : NON A !A
Ne pas oublier que les parenthèses peuvent intervenir pour jouer sur
les priorités.
Exemple : !(A||(B&&C))
Une condition doit toujours être mise entre parenthèses.
4.4 - Comparaisons
Une condition peut porter sur un test entre valeurs numérique, entre
caractères et dans ce cas la comparaison porte sur leur valeur ASCII, mais pas
directement entre chaînes de caractères. Il faut utiliser dans la condition la valeur
retournée par la fonction strcmp par exemple.
4.5 - Test "si" :
Ce test fait appel aux mots réservés if et else :
if(condition)
{
/* exécuté si condition vraie */
}
else
{
/* exécuté si condition fausse */
}
Les accolades sont inutiles si il n'y a qu'une instruction a exécuter
:
if(condition)
instruction1; /* exécuté si condition vraie */
else
instruction2; /* exécuté si condition fausse */
Si rien n'est à faire en cas d'évaluation à faux de la condition,
else peut être omis :
if(condition)
{
/* exécuté si condition vraie */
}
Plusieurs tests peuvent être cascadés :
if(condition1)
{
/* exécuté si condition1 vraie */
}
else if(condition2)
{
/* exécuté si condition1 fausse et condition2 vraie */
}
else
{
/* exécuté si condition1 fausse et condition2 fausse */
}
4.6 - Test "selon" :
Ce test fait appel aux mots réservés switch et case :
switch(condition)
{
case const1 : instructions;
break;
case const2 : instructions;
break;
case const3 : instructions;
break;
default : instructions;
break;
}
const1, connst2... sont des
constantes de type entier. Il ne peut s'agir de variables puisque le but est de définir
une action selon des cas préétablis. Il faut garder à l'esprit qu'une condition est en
fait un entier retourné lors de l'évaluation. La condition peut donc être un entier int
ou long, une énumération enum, un caractère et dans ce cas sa valeur ASCII est employée, mais
en aucun cas un flottant qui par nature n'est pas dénombrable.
Switch sert à évaluer la condition
d'une table de décision.
Case énumère les différents cas
possibles.
Break sert à sortir d'une branche
d'instructions. Sans le break, les instructions des case suivants sont aussi exécutés.
Default permet d'exécuter des
instructions dans le cas ou aucune entrée n'a été trouvée dans la table, c'est à dire
dans le cas ou le résultat de l'évaluation de la condition ne correspond à aucune
constante dans les case.
5 - Boucles d'itérations
5.1 - Boucle "for" : le nombre d’itérations est connu
for(i=0;i<=10;i++) /* ( valeur de
départ ; condition de continuation ; incrémentation ) */
{
instructions
}
5.2 - Boucle "while" : le nombre d’itérations dépend
de l’évaluation de la condition
while(condition de continuation)
{
instructions
}
5.3 - Boucle "do - while" : l’évaluation a lieu après
l’exécution du bloc d’instructions
do
{
instructions
}
while(condition de continuation);
6 - Fonctions
<Type retourné> Nom de la Fonction (<types d'arguments>)
{
Corps de la fonction
}
6.1 - Exemples simples :
Fonction n'acceptant pas d'argument et ne retournant rien :
void Fonction (void)
{
}
Fonction retournant un entier, somme de deux entiers passés en
arguments :
int Somme (int a, int b)
{
}
Fonction acceptant deux pointeurs sur des entiers longs et retournant
un pointeur sur un flottant :
float* Somme (long *ptr1, long *ptr2)
{
}
6.2 - Fonctions et tableaux
void Fonction (char table[], int longueur)
{
}
Il n'est pas nécessaire de spécifier la taille du tableau, ce qui
rend la fonction plus généraliste. Il faut cependant passer la dimension du tableau pour
pouvoir le traiter. Sans le savoir, le tableau est passé à la fonction par adresse, car
que le nom du tableau est en fait un pointeur sur ce tableau. Il nous est donc possible de
modifier le contenu du tableau.
void Fonction (int matrice[3][4])
{
}
est correct.
void Fonction (int matrice[][4], int ligne)
{
}
est correct. On peut spécifier le nombre de lignes en le passant en
argument.
void Fonction (int matrice[3][])
{
}
est incorrect. On peut omettre la première dimension du tableau, mais
pas les suivantes, car comme nous l'avons vu, elles entrent dans le calcul de la position
des éléments en mémoire.
6.3 - Fonctions et structures :
Contrairement aux tableaux, le nom d'une structure n'est pas un
pointeur. Il représente simplement une étiquette regroupant un certain nombre de champs.
Si on passe une structure par argument, la fonction pourra lire le
contenu de ses champs mais ne pourra les modifier puisque l'on n'en connaît pas
l'adresse. La fonction Afficher opère par passage par argument. Elle référence les
champs de la structure par l'opérateur '.' préfixant le nom de champ.
Pour qu'une fonction puisse modifier une structure, il faut lui passer
cette structure par adresse. En passant l'adresse de la structure, on peut accéder aux
champs pour les lire ou les modifier. La fonction Enregistrer procède ainsi.
Void Enregistrer (Personne *fiche);
L'argument fiche est donc un pointeur sur la structure Personne. Comme
il ne s'agit plus d'une étiquette mais d'un pointeur, l'accès par référence avec le '.' n'est plus
valable. On accède aux champs d'une structure à partir de son adresse par la flèche '->'. On écrira donc
:
fiche->nom pour accéder au nom
et pas fiche.nom.
On pourrait toutefois écrire (*fiche).nom puisque fiche est un pointeur
sur la structure.
Une chose importante est à remarquer dans le programme qui suit :
L'affectation, le test et toute opération sur les chaînes de caractères ne peut se
faire que par l'emploi de fonction manipulant les chaînes.
Noter enfin que fiche étant un pointeur sur la structure, fiche++ va incrémenter
le pointeur de la taille de la structure. L'emploi de sizeof n'est pas nécessaire. Si plusieurs
éléments de type structure sont en mémoire (tableau de structure), on peut facilement
passer d'un élément à l'autre.
#include<stdio.h>
#include<string.h>
typedef struct {
char nom[20];
char prenom[20];
char tel[15];
int age;
} Personne ;
void Afficher (Personne fiche)
{
printf("Nom : %s\n", fiche.nom);
printf("Prénom : %s\n", fiche.prenom);
printf("Téléphone : %s\n", fiche.tel);
printf("Age : %d\n", fiche.age);
}
void Enregistrer (Personne *fiche)
{
strcpy(fiche->nom,"PROST");
strcpy(fiche->prenom,"Alain");
strcpy(fiche->tel,"00324123456789");
fiche->age=45;
}
void main (void)
{
Personne pers;
strcpy(pers.nom,"PAPIN");
strcpy(pers.prenom,"Jean-Pierre");
strcpy(pers.tel,"0102030405");
pers.age=32;
Afficher(pers);
Enregistrer(&pers);
Afficher(pers);
}
7 - Déclarations complexes
On peut avoir à déclarer des fonctions assez complexes. Il faut alors
faire systématiquement appel à la règle de priorité suivante :
7.1 - Priorités
Priorités par ordre décroissant :
( ) Opérateur de Fonction ou parenthèses de priorité
[ ] Opérateur de Tableau
* Opérateur d'indirection (pointeur)
Il faut tout d'abord rechercher le nom de la déclaration et
reconnaître les blocs prioritaires en partant de ce nom.
7.2 - Par exemple :
*(t[])();
t[] : t est un tableau de ...
* XXX() : XXX est une fonction
retournant un pointeur.
En remplaçant XXX par t[], mis entre parenthèses pour respecter la priorité :
*(t[])(); est un tableau de
fonctions retournant un pointeur.
(*t[])();
t[] : t est en tableau de...
*t[] : t est un tableau de
pointeurs.
XXX() : XXX est une fonction.
En remplaçant XXX par *t[], mis entre parenthèses pour respecter la priorité :
(*t[])(); est un tableau de
pointeurs sur une fonction.
7.3 - Applications :
func est une fonction n'acceptant rien et retournant un pointeur
générique (non défini) :
void *func(void);
funcptr est un pointeur sur une fonction n'acceptant par d'argument et
ne retournant rien :
void(*funcptr)(void);
func est une fonction qui retourne un entier et qui accepte un tableau
de 64 pointeurs sur des fonctions n'acceptant rien et ne retournant rien :
int func(void(*[64])(void));
func est une fonction acceptant un entier et retournant un pointeur sur
une fonction acceptant un tableau de 4 pointeurs sur des chaînes de caractères et
retournant un flottant :
float(*func(int))(char* [4]);
T est un tableau de 3 pointeurs sur des fonctions acceptant un entier
et retournant un pointeur sur un tableau de 5 entiers longs :
long(*(*T[3])(int))[5];
func est une fonction n'acceptant rien et retournant un pointeur sur un
tableau de 10 pointeurs sur des fonctions acceptant un entier et retournant un entier :
int(*(*func(void))[10])(int);
Aucun commentaire:
Enregistrer un commentaire