Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
• main() précise que ce qui suit est le programme principal. • Cette fois le programme principal comporte 2 instructions:
Langage C • Il est délimité par les accolades '{' et '}' • La première instruction contient la notation '\n' : c'est le
• Dans cet exemple nous avons une seule instruction printf(…); caractère de fin de ligne. Il provoque un passage à la ligne
• Le point-virgule qui termine cette instruction est obligatoire suivante.
M. HIMMI Printemps 2015 • Toute instruction simple est suivie d'un point-virgule • Le langage C prévoit une notation de ce type pour différents
caractères dits: caractères de contrôle
•Caractéristiques:
• Un programme écrit dans un langage de haut niveau est un
fichier texte comportant des instructions et des mots clés •Modulaire: Permet de découper une application en modules
Haut niveau Langage humain
proches du langage humain: fichier source . qui peuvent être compilés séparément.
• Pour être exécuté, un programme écrit dans un langage de •Structuré: Traite les tâches en les mettant dans des blocs.
Langage de programmation
haut niveau haut niveau doit être interprété ou compilé. •Efficace: Possède de grandes possibilités de contrôle de la
machine et génère un code compact et rapide.
• Il existe différents interpréteurs et compilateurs pour chaque
Langage assembleur langage selon la plateforme et le système d’exploitation. •Portable: Permet d'utiliser le même code source sur d'autres
type de machines simplement en le recompilant
• Le résultat de la compilation est le "module objet". Il est
rangé dans un fichier ayant l'extension obj. •Extensible: Animé par des bibliothèques de fonctions qui
Bas niveau Langage machine enrichissent le langage.
•Souple et permissif: Hormis la syntaxe, peu de vérifications
et d'interdits ce qui peut poser des problèmes …
Présentation du langage C Présentation du langage C Présentation du langage C
Création d'un Programme: Les Environnements de Développement Intégré (IDE) Exemples d'Environnements de Développement Intégré:
permettent de mettre en œuvre les étapes de développement
On a deux possibilités: d'un programme: édition, compilation, édition des liens,
• Qt Creator www.qt.io/download/
exécution, gestion de projets, mise au point..)
1. Soit on récupère chacun de ces trois programmes • Code::Blocks www.codeblocks.org/
séparément. C'est la méthode la plus compliquée, mais • Il existe plusieurs IDE disponibles gratuitement.
elle fonctionne.. • Microsoft Visual Studio www.visualstudio.com/
• Les IDE vous permettent de programmer sans problèmes.
2. Soit on utilise un programme "trois-en-un" qui combine Certains sont plus complets au niveau des options, d'autres un • Gcc gcc.gnu.org/
l'éditeur de texte, le compilateur et l'éditeur des liens. Ces peu plus intuitifs à utiliser, mais dans tous les cas les
programmes sont appelés "Environnements de programmes que vous créerez seront les mêmes quel que soit
Développement Intégré" ou IDE l'IDE que vous utilisez.
Les identifiants: Les identifiants: Exemples Les mots clés: Ce sont des mots réservés par le langage C et qui
• Les identifiants sont les noms des objets (fonctions, variables, ne peuvent pas être affectés à de identifiants
constantes, …), ce sont des suites de lettres et/ou de chiffres.
• Le premier caractère est obligatoirement une lettre. Le
caractère souligné '_' est considéré comme une lettre.
• L'ensemble des symboles utilisables est donc:
0, 1, 2, ..., 9, A, B, ..., Z, _, a, b, ..., z
• Le C distingue les minuscules des majuscules.
Exemple : var Var VaR VAR sont des identifiants valides et tous
différents.
• La longueur des identifiants n'est pas limitée, mais C distingue
seulement les 31 premiers caractères
Respecter un certain nombres de règles de présentation : Justification de la notion de type: Types de base 1/2:
• Ne pas placer plusieurs instructions sur une même ligne. Toute information quelle que soit sa nature est codée sous Ce sont les types prédéfinis. Ils sont au nombre de six :
• Utiliser des identifiants significatifs. forme binaire. Il ne suffit pas de connaître le contenu d'une void : c'est le type vide. Il est surtout utilisé pour préciser les
• Réserver les identifiants en majuscules pour le préprocesseur. zone pour lui attribuer une signification. fonctions sans argument ou sans retour.
• Faire ressortir la structure syntaxique du programme. En C, une variable se caractérise à partir de son type. int : c'est le type entier. Il se décline avec des qualificatifs pour
•Mettre une ligne entre les déclarations et les instructions. La notion de type sert à: préciser sa taille (long ou short), et le fait qu’il soit que positif
• Une accolade fermante est seule sur une ligne et fait • définir le domaine de valeurs (taille en mémoire et (unsigned) ou positif et négatif (signed).
référence, par sa position horizontale, au début du bloc qu'elle représentation machine) Par défaut signed est appliqué
ferme. • définir les opérations possibles sur cette variable char : ce type représente un entier sur huit bits. C'est le
• Aérer les lignes de programme en entourant par exemple les support des caractères codés en ASCII.
opérateurs avec des espaces. Comme le type int le type char peut être qualifié de signed ou
• Commenter les listings mais éviter les commentaires triviaux unsigned.
Chaque constante a une valeur et un type Chaque constante a une valeur et un type Chaque constante a une valeur et un type
Les constantes et les expressions constantes sont évaluées à la Les constantes réelles sont, par défaut, de type double. Les Constantes caractères sont écrites entre apostrophes.
compilation.
L'expression 1 + 2 * 3 est traduite comme l'expression entière 7 • Elles peuvent être exprimées sous les formes: • Elles peuvent être exprimées sous les formes suivantes :
Décimale : 1234.56 4. -.3456 Normale : 'a'
Les constantes entières peuvent être exprimées sous les formes: ou Scientifique : 1.23e-12 12.3456E4
• Décimale: 1234 Octale : '\007'
• Octale : 0177 • Elles peuvent aussi inclure un suffixe : Hexadécimale : '\x1b'
• Hexadécimale : 0x1Bf 0XF2a Ou symbolique :
f ou F : impose le type float à la constante:
L'entier trop grand pour un int est considéré comme du type long '\n' saut de ligne (LF)
Exemple : 1.23f
On peut imposer à une constante entière d'être du type :
'\a' bip
• long : 123 l 123L l ou L : impose le type long double '\t' tabulation horizontale
• unsigned : 3456U Exemple : 123.4567E45L '\b' retour arrière
• unsigned long : 78UL …
Opérateurs d'incrémentation et de décrémentation: Opérateurs d'affectation élargie: Opérateurs de forçage de type ou opérateur de cast:
Priorité: On dispose d'opérateurs encore plus puissant: On peut forcer la conversion d'une expression dans un type de
Les priorités élevées de ces opérateurs unaires permettent On peut remplacer i = i +k par i+=k son choix:
d'écrire des expressions compliquées sans parenthèses. ou a=a*b par a*=b Si n et p sont des entiers, (double) (n/p) convertie d'abord n en
Exemple: double et l'opération sera alors évalué en double.
3 * i++ * j-- + k++ a un sens! D'une manière générale on peut condenser les affectations de
Intérêt: la forme: C'est un opérateur unaire qui fait la conversion en
lvalue = lvalue opérateur expression double du résultat de l'expression mais pas des différentes
• Allègent l'écriture des expressions en
• On ne cite qu'une fois la lvalue (moins de risque d'erreur) valeurs.
lvalue opérateur = expression La priorité de ces opérateurs est élevée.
pour tous les opérateurs binaires arithmétiques et de
manipulation de bits Il existe autant d'opérateurs de cast que de types
différents
Opérateurs et expressions Opérateurs et expressions Opérateurs et expressions
Opérateurs de manipulation de bits: Exemples Opérateurs de manipulation de bits: Exemples Opérateur sizeof:
Variable / Variable / • Il retourne la taille d'un objet en octets. Dans ce cas on ne
En binaire Hexa. Décimal Opérateur Cas 1 Cas 2 met pas de parenthèses
Opérateur
n 0000 0101 0110 1110 056E 1390 (signed) n 0000 0101 0110 1110 1010 1101 1101 1110 • Il donne la taille en nombre d’octets du type dont le nom
p 0000 0011 1011 0011 03B3 947 (unsigned) p 0000 0101 0110 1110 1010 1101 1101 1110 est entre parenthèses
n << 2 0001 0101 1011 1000 1011 0111 0111 1000 double n;
n&p 0000 0001 0010 0010 0122 290 n >> 3 0000 0000 1010 1101 1111 0101 1011 1011 unsigned int size_of_an_int = sizeof (int);
n|p 0000 0111 1111 1111 07FF 2047 p >> 3 0000 0000 1010 1101 0001 0101 1011 1011 unsigned int size_of_n = sizeof n;
n^p 0000 0110 1101 1101 06DD 1757 n << 2: décale n de 2 bits vers la gauche: 2 bits de poids fort • Intérêt:
~n 1111 1010 1001 0001 FA91 -1391(signed) sont perdus et 2 bits à 0 apparaissent à droite. • Ecrire des programmes portables en connaissant la taille
~n 1111 1010 1001 0001 FA91 64145 (unsigned) n >> 3: décale n de 3 bits vers la droite: 3 bits de droite sont exacte de certains objets
perdus et 3 bits apparaissent à gauche.
• si n est unsigned les bits créés sont à 0. • Eviter de calculer la taille des objet par soi-même d'un type
• si n est signed les bits ajoutés sont identiques au bit de signe complexe
Rang Opérateur ordre de priorité La puissance d'un langage provient de la possibilité de faire: L'instruction if:
1 () gauche à droite •Des comportement différent selon les circonstances if (expression)
2 ~ ++ -- (type) droite à gauche •De répéter des instructions instruction_1
3 * / % gauche à droite •Des branchements conditionnels ou inconditionnels else
4 + - gauche à droite instruction_2
5 << >> gauche à droite En C ces instructions de contrôle sont:
else et l'instruction qui le suit sont facultatifs. On peut avoir:
6 < <= > >= gauche à droite if … else et switch pour les choix if (expression)
7 == != gauche à droite instruction
8 & gauche à droite do … while, while et for pour les boucles
9 ^ gauche à droite Avec:
goto, break et continue pour les branchements expression: expression quelconque
10 | gauche à droite
11 && gauche à droite Instruction: instructions simple,
12 || gauche à droite bloc d'instructions,
13 Op. conditionnel droite à gauche instruction structurée
14 = droite à gauche
Les instructions de contrôle Les instructions de contrôle Les instructions de contrôle
• Les spécificateurs de format indiquent la manière dont les Spécificateurs de format pour: printf Spécificateurs de format pour: printf (suite)
valeurs des expressions sont imprimées. Ils commencent
Symbole Type Impression comme Symbole Type Impression comme
toujours par le symbole % et se terminent par un ou deux
caractères qui indiquent le format d'impression. %d ou %i int entier relatif %f double rationnel en notation décimale
%u int entier naturel (unsigned) %e double rationnel en notation scientifique
• Les spécificateurs de format impliquent une conversion d'un
nombre en chaîne de caractères. %o int entier exprimé en octal %s char* chaîne de caractères
%x int entier exprimé en hexadécimal
• La partie "<format>" contient exactement un spécificateur Pour le type long double, il faut utiliser les spécificateurs
de format pour chaque expression. %c int ou char caractère
%Lf ou %Le
Pour le type long, il faut utiliser les spécificateurs:
%ld %li %lu %lo %lx
p = printf("%3d",n); Nous avons déjà employé putchar et nous pouvons dire que
n= 25 on affiche ^25 et p vaudra 3 printf( "%d", n, p ) la valeur de p ne sera pas affichée putchar(c) joue le même rôle que printf("%c",c)
n= -4583 on affiche -4583 et p vaudra 5
printf( "%d %d", n ) affichera n puis … n'importe quoi. Mais son exécution est plus rapide dans la mesure où elle ne fait
printf étant une fonction elle retourne le nombre de pas appel au mécanisme d'analyse de format.
caractères qu'elle a effectivement affiché ou la valeur -1 en cas
d'erreur.
Ceci peut se révéler intéressant si on veut contrôler la mise en
page et ne forcer le retour à la ligne que quand c'est nécessaire.
En général, getchar est utilisé dans une affectation: appelé champ et désigné par un identificateur type nom-du-tableau[nombre-éléments];
Les champs de bits nombre-éléments est une expression constante entière positive.
int C;
C = getchar(); Les unions int tab[10];
indique que tab est un tableau de 10 éléments de type int.
Les énumérations
Cette déclaration réserve pour tab un espace mémoire
consécutifs de 10 cases de type int
Les types composés Les types composés Les types composés
Les tableaux multidimensionnels: Dimension 2 Les tableaux multidimensionnels: Dimension 2 Les tableaux multidimensionnels: Dimension 2
Un tableau à deux dimensions L et C est un tableau de Un tableau à deux dimensions L et C correspond en Exemple tableau de dimension 2:
dimension L dont chaque élément est un tableau de C éléments. mathématique à une matrice de L lignes et C colonnes. main()
Déclaration d'un tableau à une dimension: {
int tab[5][10];
type nom-du-tableau[nb-lignes] [nb-colonnes]; int i,j;
Exemple int tab[4][6]; { … } /* affectations…*/
for (i=0; i<5; i++) /* Pour chaque ligne ... */
indique que tab est un tableau de 4 lignes et 6 colonnes tous de
L est le nombre de lignes, C est le nombre de colonnes {
type int.
Un tableau à deux dimensions contient donc L*C composantes. for (j=0; j<10; j++) /* pour chaque colonne */
Cette déclaration réserve pour tab un espace mémoire printf("%7d", tab[i][j]);
consécutifs de 4*6=24 cases de type int printf("\n"); /* Retour à la ligne */
Chaque élément est identifié par 2 entiers: l’indice de ligne et }
celui de la colonne }
Les tableaux multidimensionnels: Dimension 2 Les tableaux multidimensionnels: Dimension 2 Les tableaux multidimensionnels: Dimension 2
On peut initialiser les composantes du tableau lors de sa Exemples d’initialisations Réservation automatique
déclaration, en donnant la liste des valeurs entre accolades. Si le nombre de lignes n'est pas indiqué lors de l'initialisation,
les composantes de chaque ligne sont encore une fois comprises l'ordinateur réserve automatiquement le nombre d'octets
entre accolades. nécessaires.
int A[3][10] ={{ 0,10,20,30,40,50,60,70,80,90}, int A[][10] = {{ 0,10,20,30,40,50,60,70,80,90},
{10,11,12,13,14,15,16,17,18,19}, {10,11,12,13,14,15,16,17,18,19},
{ 1,12,23,34,45,56,67,78,89,90}};
{ 1,12,23,34,45,56,67,78,89,90}};
float B[3][2] = {{-1.05, -1.10 }, réservation de 3*10
{86e-5, 87e-5 }, float B[][2] = {{-1.05, -1.10 },
{-12.5E4, -12.3E4}}; {86e-5, 87e-5 },
{-12.5E4, -12.3E4}};
Les valeurs sont affectées ligne par ligne en passant de gauche à réservation de 3*2
droite. Les valeurs manquantes sont initialisées par zéro.
Les types composés Les types composés Les types composés
Cas particulier des chaines de caractères: Cas particulier des chaines de caractères:
Les tableaux multidimensionnels: Cas général Une chaîne de caractères est un tableau qui ne contient que des Les bibliothèques de fonctions de C contiennent une série de
Il se définit par: éléments de type char et terminé par le caractère '\0' . fonctions spéciales pour le traitement de chaînes de caractères.
type Nom_du_tableau [a1][a2][a3] ... [aN] char mot[6] = "F.S.R"; correspond au tableau : Exemple: string.h
Chaque élément entre crochets désigne le nombre d'éléments [0] [1] [2] [3] [4] [5] strlen(s) fournit la longueur de s sans compter le '\0'
F . S . R '\0' strcpy(s,t) copie t vers s
dans chaque dimension Et char ville[10] = "Rabat"; strcat(s, t) ajoute t à la fin de s
Le nombre de dimensions n'est pas limité [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] strcmp(s, t) compare s et t lexicographiquement et fournit
R a b a t '\0'
Un tableau à N dimensions est un tableau de tableaux à N-1 un résultat: négatif si s précède t
On peut omettre la taille du tableau lors de la déclaration, s'il y a zéro si s est égal à t
dimensions. initialisation: char ville[] = "Rabat"; positif si s suit t
[0] [1] [2] [3] [4] [5] strncpy(s, t, n) copie au plus n caractères de t vers s
R a b a t '\0'
strncat(s, t, n) ajoute au plus n caractères de t à la fin de s
L'accès à un élément d'une chaîne de caractères se fait de la
même façon que l'accès à un élément d'un tableau.
Le langage C permet de créer de nouveaux types de variables en La définition d'une variable structurée consiste à créer une Exemple: dans un plan un point est identifié par ses
utilisant la notion de structure. variable ayant comme type celui d'une structure que l'on a coordonnées x et y. on crée une structure nommé coordonnées .
précédemment déclarée struct coordonnées
C'est une suite finie d'objets de types différents appelés champs {
et désigné par un seul identifiant. Pour une variable d'identifiant X de type modele on utilise la int x; // Abscisses
La déclaration d'une structure ne fait que donner l'allure de la syntaxe: struct modele X; int y; // Ordonnées
On peut aussi faire les deux déclarations (du type et de la };
structure:
struct modele variable) en même temps: Si on a besoin de 3 points_a, point_b et point_c on déclare:
{ struct modele struct coordonnées points_a, point_b, point_c;
type_1 membre_2; { Chacun de ces points à 2 coordonnées x et y;
type_2 membre_2; type_1 membre_1; Pour y accéder, il suffit de mettre un point entre le nom de la
… type_2 membre_2;
… variable et le champs désiré:
type_n membre_n; point_b.x=3; points_b.y=12;
}; type_n membre_n; points_a.x= points_b.y;
} X;
Elle ne réserve pas d'espace mémoire. points_a.y= points_b.x;
Les types composés Les types composés Les types composés
Exemple:
Utilisation de typedef Les structures se manipulent comme les autres types. La struct Personne
définition, l’affectation, l'initialisation, … tout est semblable au { char nom[100];
L'instruction typedef crée un alias char prenom[100];
comportement des types de base. char adresse[1000];
typedef struct coordonnees coordonnees;
On utilise des accolades pour préciser les valeurs des champs int age;
Cette ligne indique: int garcon; };
"le mot coordonnees est équivalent à struct coordonnees" en cas d’initialisation (comme pour un tableau). typedef struct Personne fiche;
On peut évidemment déclarer des tableaux de structures et main()
En mettant cette instruction, on n'aura plus besoin de mettre le { fiche utilisateur;
mot struct à chaque définition même définir un champ de type structure ou tableau …
coordonnees a={2,13},b=a, c, tab[10]; printf("Quel est votre nom ? ");
scanf("%s", utilisateur.nom);
Exemple: c=a; printf("Votre prenom ? ");
coordonnees a, b; Par contre écrire b={0,2}; donne une erreur! scanf("%s", utilisateur.prenom);
printf("Vous vous appelez %s %s", utilisateur.prenom,
déclare 2 variables nommées a et b de type struct coordonnees Comme pour les tableaux, la syntaxe utilisée pour utilisateur.nom);
l’initialisation ne marche pas pour une affectation. }
La mémoire d'une machine est constituée de cases On peut accéder au contenu d'une variable de 2 façons : Un pointeur est une variable qui peut contenir l'adresse d'une
(généralement un octet) appelées blocs. • grâce à son identifiants (accès direct) autre variable.
Chacun de ces blocs est identifié par un numéro. C'est La valeur d'un pointeur est toujours un entier (éventuellement
l'adresse du bloc. long).
Une variable, selon son type, occupe un ou plusieurs blocs
consécutifs. Un pointeur est limité à un type de données: Il peut contenir
l'adresse d'une variable correspond à l'adresse du premier l'adresse d'une variable de ce type ou l'adresse d'un élément d'un
• grâce à l'adresse du premier bloc alloué à la variable tableau de ce type.
bloc réservé à la variable.
l'adresse d'une variable change à chaque exécution. Il faut bien faire la différence:
Un pointeur est une variable qui peut 'pointer' sur
différentes adresses.
Le nom d'une variable reste toujours lié à la même adresse.
L'opérateur & ne s'applique qu'aux variables et les tableaux. Il Le symbole '*' indique qu'il s'agit d'une variable de type On ne peut affecter que des adresses à un pointeur. Seule
ne peut pas être appliqué aux constantes ou aux expressions. pointeur exception: La valeur 0 (NULL) pour indiquer qu'un pointeur ne
pointe 'nulle part'.
Si P est un pointeur et A une variable (du même type) contenant Le type indique au compilateur la taille des zones pointées.
la valeur 10. L'instruction P = &A; affecte l'adresse de la Les opérateurs * et & ont la même priorité que les opérateurs
variable A à la variable P. unitaires ( ! , ++ , -- ).
Les pointeurs Les pointeurs Les pointeurs
Opération sur les pointeurs: Adressage des composantes d'un tableau: Adressage des composantes d'un tableau:
Les pointeurs sont des variables et peuvent être utilisés comme En déclarant un tableau A de type int et un pointeur P sur int, Soustraction de deux pointeurs:
telles. int A[10], *P; • Si P1 et P2 deux pointeurs qui pointent dans le même tableau:
Si P1 et P2 sont deux pointeurs sur int, alors P1 = P2; copie le l'instruction: P = A; est équivalente à P = &A[0]; P1-P2 fournit le nombre de composantes comprises entre P1 et P2.
contenu de P2 vers P1. (P1 pointe alors sur le même objet que Si P pointe sur une composante quelconque d'un tableau, alors • Le résultat de P1-P2 est négatif, si P1 précède P2
P2) P+1 pointe sur la composante suivante: zéro, si P1 = P2
Après les instructions: P+i pointe sur la i-ième composante derrière P positif, si P2 précède P1
int A, *P; P = &A; P-i pointe sur la i-ième composante devant P. • Le résultat de P1-P2 est indéfini, si P1 et P2 ne pointent pas
A désigne le contenu de A Ainsi, après l'instruction P = A; dans le même tableau
&A désigne l'adresse de A le pointeur P pointe sur A[0] • La comparaison de 2 pointeurs qui pointent dans le même
P désigne l'adresse de A *(P+1) désigne le contenu de A[1] tableau est équivalente à la comparaison des indices
*P désigne le contenu de A *(P+2) désigne le contenu de A[2] correspondants.
&P désigne l'adresse du pointeur P … ... (Si les pointeurs ne pointent pas dans le même tableau, le résultat est donné
*A est illégal (puisque A n'est pas un pointeur) *(P+i) désigne le contenu de A[i] par leurs positions relatives dans la mémoire).
Un programme devient difficile a comprendre dés qu'il dépasse En C il existe un seul type de module: La FONCTION. main()
une ou deux pages: La fonction reçoit des arguments et fournit UN résultat {
• L'écriture modulaire permet de diviser en plusieurs parties float fexple (float, int, int); /* déclaration de la fonction */
Une fonction est constituée de: float x = 1.5, float y, z;
le programme et de regrouper dans le "programme • entête: type et identifiant de la fonction suivis d’une liste int n = 3, p= 5, q= 10;
principal" les enchaînements de ces sous programmes. d’arguments entre parenthèses y = fexple (x, n, p); /* appel de fexple avec x, n et p */
• Chacun de sous programmes peut être décomposée en • instruction composée constituant le corps de la fonction printf ("valeur de y : %e\n", y);
modules plus élémentaires z = fexple (x+0.5, q, n-1); /*appel de fexple avec formules*/
la fonction doit être déclarée avant que le compilateur printf ("valeur de z : %e\n", z) ;
La "programmation structurée": rencontre le premier appel:
• Permet d'éviter des séquences d'instructions répétitives. }
• Au début de la fonction main() /***********Définition de la fonction fexple *************/
• Permet le partage des modules qu'il suffit d'avoir écrits et • Avant la définition de la fonction main() float fexple (float x, int b, int c)
mis au point une seule fois • Dans un fichier séparé (.h) (qu'il faut inclure avec la { float val; /* déclaration d'une variable "locale" a fexple*/
directive #include !) val = x * x + b * x + c ; return val;
}
Lors de l'appel d'une fonction, les paramètres sont convertis En C, les arguments sont transmis "par valeur": Les arguments sont transmis "par valeur" ce mode se
automatiquement dans les types déclarés dans la définition de la main() transmission interdit qu'une fonction modifie les arguments.
fonction. Exemple: {
…. void echange (int a, int b);
int a = 200; int resultat; int n=10, p=20; Mais on peut contourner ce problème:
resultat = pow(a, 2); printf ("avant appel : %d %d\n",n, p); • En utilisant des "variables globales"
… echange (n, p); avant appel : 10 20 • En transmettant la "valeur" de "l'adresse d'une variable".
printf ("après appel : %d %d", n, p); début échange : 10 20
A l'appel de la fonction pow: } fin échange : 20 10 Si la transmission des arguments se fait "par valeur", les
void echange (int a, int b) après appel : 10 20
• La valeur de a et la constante 2 sont converties en double arguments effectifs peuvent être une expression.
• Le résultat retourné est converti en int avant d'être affecté à {
int c ; Si le mode de transmission est celui "par adresse", les
resultat. arguments effectifs ne peuvent être qu'une lvalue.
printf ("début échange : %d % d\n", a, b);
c = a; a = b; b = c;
printf ("fin échange : %d %d\n", a, b);
}
Une fonction peut avoir comme arguments des structures. Elle Il convient d'être prudent lors de l’utilisation d’une fonction Le langage C autorise la récursivité, celle ci peut prendre deux
peut même retourner un tel objet. retournant un pointeur. Il faut éviter l’erreur qui consiste à aspects :
typedef struct { float v[6]; } Vecteur; retourner l'adresse d’une variable temporaire! • Une fonction comporte dans sa définition au moins un
main() main() appel a elle-même: récursivité directe
{ Vecteur vec = { {1.34f, 8.78f, 10.f, 4.f, 22.12f, 3.145f} }; {
Vecteur inv; • L'appel d'une fonction entraîne celui d'une autre fonction
char *p; qui appelle la fonction initiale: récursivité croisée
Vecteur inverse( Vecteur vecteur, int n ); char *ini_car(void);
int i, n = sizeof(vec.v) / sizeof (vec.v[0]); p = ini_car(); Un exemple classique (inefficace sur le plan du temps d'exécution)
inv = inverse( vec, n ); printf("%c\n", *p);
for( i=0; i < n; i++ ) long fact (int n)
} {
printf( "inv.v[%d] : %f\n", i, inv.v[i] ); } char *ini_car(void)
Vecteur inverse( Vecteur vecteur, int n ) { if (n>1) return (fact(n-1)*n);
{ Vecteur w; int i; char c = ’#’; else return(1);
for( i=0; i < n; i++ ) return(&c); <=== ERREUR }
w.v[i] = vecteur.v[i] ? 1./vecteur.v[i] : 0.f; }
return w; }
Entrées-sorties de haut niveau Entrées-sorties de haut niveau Entrées-sorties de haut niveau
Les entrées-sorties de haut niveau intègrent deux mécanismes Trois flots de données sont prédéfinis: La fonction fopen, de type FILE*, ouvre un fichier et lui
distincts : • stdin: en lecture sur l'entrée standard (clavier) associe un flot.
• le formatage des données • stdout: en écriture sur la sortie standard (écran)
Sa syntaxe est:
• la mémorisation des données dans une mémoire tampon • stderr: en écriture sur la sortie erreur standard (écran) fopen("nom-de-fichier","mode");
Toute opération d'entrée-sortie se fait par l’intermédiaire La création d'un nouveau flot de données s'effectue par l’appel à Le premier argument est le nom du fichier fourni sous forme
d’un flot de données (stream) qui est une structure de données la fonction fopen d'une chaîne de caractères.
de type FILE faisant référence à: La fonction fclose permet de le fermer
• la nature de l’entrée-sortie Le second argument est une chaîne de caractères qui spécifie
• la mémoire tampon le mode d'accès au fichier.
• le fichier sur lequel elle porte
• la position courante dans le fichier
• ...
Le type FILE est défini dans stdio.h
Les spécificateurs de mode d'accès diffèrent suivant le type de La valeur retournée par fopen est un pointeur sur FILE
fichier considéré: "r" ouverture d'un fichier texte en lecture Si l'ouverture a réussi, le pointeur retourné permet de repérer
• les fichiers textes: les caractères de contrôle sont interprétés "w" ouverture d'un fichier texte en écriture le fichier, et devra être passée en paramètre à toutes les
lors de la lecture et de l'écriture "a" ouverture d'un fichier texte en écriture à la fin fonctions d'entrées-sorties sur le fichier
"rb" ouverture d'un fichier binaire en lecture Si l'ouverture est impossible, fopen renvoie la valeur NULL
• les fichiers binaires: permettent de transférer des données
"wb" ouverture d'un fichier binaire en écriture
sans transcodage. Sont plus efficaces, mais les fichiers ne Conditions particulières et cas d'erreur
"ab" ouverture d'un fichier binaire en écriture à la fin
sont pas portables (le codage dépend des machines) • Si le mode contient la lettre r, le fichier doit exister!
"r+" ouverture d'un fichier texte en lecture/écriture
"w+" ouverture d'un fichier texte en lecture/écriture • Si le mode contient la lettre w, si le fichier n'existe pas, il est
Le tableau suivant détaille les différents modes d'accès. créé. Si le fichier existe déjà son ancien contenu est perdu.
"a+" ouverture d'un fichier texte en lecture/écriture à la fin
"r+b" ouverture d'un fichier binaire en lecture/écriture • Si le mode contient la lettre a, si le fichier n'existe pas, il est
créé. Si le fichier existe déjà son ancien contenu est conservé.
"w+b" ouverture d'un fichier binaire en lecture/écriture
Les écritures se font à la fin du fichier au moment de
"a+b" ouverture d'un fichier binaire en lecture/écriture à la fin
l'exécution de l'ordre d'écriture.
La fonction fclose permet de fermer le flot associé à un fichier La fonction d'écriture fprintf, analogue à printf, permet d'écrire La fonction fscanf, analogue à scanf, permet de lire des
créer par la fonction fopen. des données dans un fichier. données à partir d'un fichier.
Sa syntaxe est: Sa syntaxe est: Sa syntaxe est semblable à celle de scanf
fclose(flot) ; fprintf( flot, "chaîne de format",exp_1, ..., exp_n); fscanf( flot, "chaîne de format ",argument-1,...,argument-n)
Avec flot le pointeur de type FILE retourné par la fonction Où: Où:
fopen correspondant. • flot est le flot de données retourné par la fonction fopen • flot est le flot de données retourné par la fonction fopen
• Les spécifications de format utilisées pour la fonction • Les spécifications de format utilisées pour la fonction
fclose retourne un entier: fprintf sont les mêmes que pour printf fscanf sont les mêmes que pour scanf
• zéro si l'opération s'est déroulée normalement
• -1 (EOF) en cas d'erreur
Entrées-sorties de haut niveau Entrées-sorties de haut niveau Entrées-sorties de haut niveau
Ressemblants à getchar et putchar, fgetc et fputc permettent Il existe également deux versions optimisées des fonctions fgetc Les fonctions d'entrées-sorties binaires transfèrent des données
respectivement de lire et d'écrire un caractère dans un fichier. et fputc qui sont implémentées par des macros. sans transcodage
La fonction fgetc retourne le caractère lu dans le fichier. Elle Leur syntaxe est similaire à celle de fgetc et fputc : Elles sont plus efficaces mais les fichiers produits ne sont pas
retourne EOF lorsque la fin du fichier est détectée . int getc(FILE* flot); portables puisque le codage des données dépend des machines
int fgetc(FILE* flot); Elles sont utiles pour des données de grande taille ou
int putc(int caractere, FILE *flot);
renfermant un type composé
La fonction fputc écrit caractère dans le flot de données et
retourne l'entier correspondant au caractère écrit ou EOF en cas
d'erreur):
int fputc(int caractere, FILE *flot);
il est toujours utile de tester la valeur lue ou écrite pour détecter
la fin du fichier ou un problème d'écriture.
Leurs prototypes sont: Les différentes fonctions d'entrées-sorties permettent d'accéder La fonction fseek permet de se positionner à un endroit précis:
size_t fread( void *pt, size_t taille, size_t nb, FILE *flot); à un fichier en mode séquentiel: les données du fichier sont int fseek(FILE *flot, long deplacement, int origine);
lues ou écrites les unes après les autres.
size_t fwrite( void *pt, size_t taille, size_t nb, FILE *flot); deplacement: détermine la nouvelle position dans le fichier. Il
Il est possible d'accéder à un fichier en mode direct: en se s'agit d'un déplacement relatif par rapport à l'origine ; il est
Où: positionnant à n'importe quel endroit du fichier. compté en nombre d'octets.
pt: l'adresse de début des données à transférer
origine peut prendre trois valeurs:
taille: la taille des objets à transférer
nb: leur nombre SEEK_SET (égale à 0) : début du fichier
SEEK_CUR (égale à 1) : position courante
size_t est un alias de unsigned int
SEEK_END (égale à 2) : fin du fichier
Elles retournent toutes deux le nombre de données transférées
La fonction int rewind(FILE *flot);
permet de se positionner au début du fichier. Elle est
équivalente à fseek(flot, 0, SEEK_SET);
La fonction long ftell(FILE *flot); Dans un programme, chaque variable occupe un certain nombre Exemple
retourne la position courante dans le fichier (en nombre d'octets d'octets en mémoire réservés de manière automatique par les Mémoriser 10 phrases saisies au clavier. Ne pouvant pas prévoir
depuis l'origine). déclarations et le nombre d'octets était connu pendant la à l'avance le nombre d'octets à réserver pour les phrases, on
compilation. déclare juste un tableau de 10 pointeurs sur des char par:
Quand on travaille avec des données dont on ne peut pas char *TEXTE[10];
prévoir la taille, on réserve toujours l'espace maximal prévisible Ces 10 pointeurs seront réservés automatiquement.....
Pour éviter ce gaspillage on fait appel à la gestion dynamique La longueur des phrases n'étant connue que pendant l'exécution
de la mémoire lors de l'exécution du programme. du programme, la réservation de la mémoire pour les 10 phrases
se fera de manière dynamique.
Allocations dynamiques Allocations dynamiques Allocations dynamiques
Pour demander de la mémoire manuellement, on a besoin La fonction malloc a pour prototype: Tester le pointeur!
d'inclure la bibliothèque <stdlib.h> qui contient deux fonctions void* malloc(size_t nombreOctetsNecessaires);
dont on a besoin: malloc renvoie un pointeur l'adresse qui a été réservée.
• malloc : demande au système d'exploitation la permission Elle prend un paramètre: le nombre d'octets à réserver et Deux possibilités :
d'utiliser de la mémoire renvoie un pointeur sans type (void*) • si l'allocation a marché, le pointeur contient une adresse
• free : permet d'indiquer à l'OS que l'on n'a plus besoin de Cette fonction renvoie un pointeur indiquant l'adresse réservée • si l'allocation a échoué, le pointeur contient l'adresse NULL.
la mémoire qu'on avait demandée mais ne sait pas quel type de variable on cherche à créer Il est peu probable qu'une allocation échoue, mais cela peut
Trois étapes a suivre lors de l'allocation manuelle de mémoire: C'est le pointeur qui va recevoir la première adresse réservée arriver.
1. appeler malloc pour demander de la mémoire qui définit le type créer
int* memoireAllouee = NULL; Si le pointeur est différent de NULL, le programme peut
2. vérifier la valeur retournée par malloc pour savoir si on a continuer, sinon il faut afficher un message d'erreur ou même
réussi à allouer la mémoire memoireAllouee = malloc(sizeof(int));
mettre fin au programme.
3. libérer avec free l'espace mémoire réservé quand on a fini
de l'utiliser
On utilise la fonction free pour libérer la mémoire dont on ne se Autres fonction d'allocation dynamique de la mémoire:
sert plus. Son prototype est:
void * calloc (size_t n, size_t t)
void free(void* pointeur);
alloue n blocs de taille t. elle est équivalente à malloc( n * t );
Elle a juste besoin de l'adresse mémoire à libérer. mais avec la mémoire réservée est mise à 0
• La fonction free peut aboutir à un désastre si on essaie de
libérer de la mémoire qui n'a pas été allouée par malloc. void * realloc ( void * base , size_t t )
• La fonction free ne change pas le contenu du pointeur; il est tente de redimensionner un bloc mémoire donné à la fonction
conseillé d'affecter la valeur NULL au pointeur par son adresse base avec une nouvelle taille t
immédiatement après avoir libéré le bloc de mémoire qui y • Si une erreur se produit, la fonction retourne le pointeur
était attaché. NULL sinon la fonction renvoie un pointeur vers l'adresse
• Si nous ne libérons pas explicitement la mémoire à l'aide du nouveau bloc réalloué.
free, alors elle est libérée automatiquement à la fin du • Le contenu de ce qui se trouvait dans le bloc d'origine est
programme sauvegardé!