Introduction au C
Langage C: www.developpez.com Chaque fichier source entrant dans la composition d'un programme exécutable est fait d'une succession d'un nombre quelconque d'éléments indépendants, qui sont : des directives pour le préprocesseur (lignes commençant par #), des constructions de types (struct, union, enum, typedef), des déclarations de variables et de fonctions externes, des définitions de variables et des définitions de fonctions.
Introduction au langage C En C on n'a pas une structure syntaxique englobant tout, comme en Pascale. La règle généralement suivie par l'éditeur de liens est la suivante : parmi les fonctions données il doit en exister une dont le nom est main Bloc = suite d’instructions encadrés par {………} #include <stdio.h> int main() { printf("Bonjour\n"); return 0; } #include <stdio.h> void main() { printf("Bonjour\n"); }
Mots clés Mots réservés au langage: auto break case char const continue default do double else enum extern float for goto If int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Commentaires : /* ceci est un commentaire */
identificateurs Un identificateur est une suite de lettres et chiffres contigus, dont le premier est une lettre une lettre majuscule est tenue pour différente de la lettre minuscule correspondante Le caractère _ (appelé « blanc souligné ») est considéré comme une lettre
Opérateurs Symboles simples : Symboles composés : ( ) [ ] . ! ~ < > ? : = , + - * / % | & ^ Symboles composés : -> ++ -- <= >= == != && || << >> += -= *= /= %= <<= >>= |= &= ^=
Caractere et chaine de caracteres Une constante de type caractère se note en écrivant le caractère entre apostrophes. Une constante de type chaine de caractères se note en écrivant ses caractères entre guillemets. Exemples, trois caractères : 'A' '2' ' " ' Quatre chaines de caractères : "A" "Bonjour" " " " ' "
Caracteres non imprimables \n : nouvelle ligne (LF) \t : tabulation (HT) \b : espace-arrière (BS) \r : retour-chariot (CR) \f : saut de page (FF) \a : signal sonore (BELL) \\ : \ • ' : ' • " : " • \d3d2d1 : le caractère qui a pour code le nombre octal d3d2d1. Exemple : "A\033B\"C\fD\\E« 033=27, Ascii 27 = escape
Nombre entier unsigned short : 16 bits pour représenter un nombre entier compris entre 0 et 65.535 short : 16 bits pour représenter un nombre entier compris entre -32.768 et 32.767 unsigned long : 32 bits pour représenter un nombre entier entre 0 et 4.294.967.296 long : 32 bits pour représenter un entier entre - 2.147.483.648 et 2.147.483.647 Int = short; long.
Nombres float La norme ANSI prévoit trois types de nombres flottants : float (simple précision), double (double précision) et long double (précision étendue). Typiquement, sur des systèmes de taille moyenne, un float occupe 32 bits et un double 64 Les long double correspondent généralement aux flottants de grande précision manipulés par certains coprocesseurs arithmétiques ou les bibliothèques de sous-programmes qui les simulent. Mais il n'est pas exclu que sur un système particulier un long double soit la même chose qu'un double.
declaration Dans le cas le plus simple on a spécification var-init , var-init , ... var-init ; ou spécification est de la forme : Var-init :
Les déclarations de variables peuvent se trouver : Exemple : Les déclarations de variables peuvent se trouver : en dehors de toute fonction, il s'agit alors de variables globales ; à l'intérieur d'un bloc, il s'agit alors de variables locales ; dans l'en-tête d'une fonction, il s'agit alors d'arguments formels, int x, y = 0, z; float a, b; unsigned short cpt = 1000;
Opérateurs arithmétiques Addition Soustraction Multiplication Division Modulo expr1 / expr2 • si expr1 et expr2 sont toutes deux entières alors « / » est traduit par l'opération « division entière », et le résultat est entier, • si au moins une des expressions expr1 ou expr2 n'est pas entière, alors l'opération faite est la division flottante des valeurs de expr1 et expr2 toutes deux converties dans le type double. Le résultat est une valeur double qui approche le rationnel expr1.
comparaison égal, différent, inférieur, inférieur ou égal, supérieur, supérieur ou égal
Connecteurs logiques Conjonction Disjonction Pour évaluer exp1 && exp2 : exp1 est évaluée d'abord et • si la valeur de exp1 est nulle, exp2 n'est pas évaluée et exp1 && exp2 vaut 0 ; • sinon exp2 est évaluée et exp1 && exp2 vaut 0 ou 1 selon que la valeur de exp2 est nulle ou non. Pour évaluer exp1 || exp2 : exp1 est évaluée d'abord et • si la valeur de exp1 est non nulle, exp2 n'est pas évaluée et exp1 || exp2 vaut 1 ; • sinon exp2 est évaluée et exp1 || exp2 vaut 0 ou 1 selon que la valeur de exp2 est nulle ou non.
negation !exp
++ et -- post-incrémentation : exp++ pré-incrémentation : ++exp exp doit être de type numérique (entier ou flottant) ou pointeur. pré-incrémentation : ++exp Exemple. y = x++ ; y = x ; x = x + 1 ; y = ++x ; x = x + 1 ; y = x ;
Instructions: affectation Var = expression Une affectation a une valeur A=b=c=0 est équivalente a A=(b=(c=0)) A=0;b=0;c=0; Exemple X=5 Y=5+ 3
Autres affectations si OP représente l'un des opérateurs + - * / % >> << & ^ |, alors exp1 OP= exp2 peut être vue comme ayant la même valeur et le même effet que exp1 = exp1 OP exp2
Exemple double puissance(double x, int n) { double p = 1; while (n != 0) { if (n % 2 != 0) /* n est-il impair ? */ p *= x; x *= x; n /= 2; } return p;
If … else…. if (x<0) if (expression) printf(" nombre negatif " ); instruction1 else Instruction2 if (x<0) printf(" nombre negatif " ); else printf(" nombre positif "); if (moy>=10) printf(" admis ");
On résoud le problème avec if (nombrePersonnes != 0) if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"); else ; printf("Il n'y a personne!"); if (nombrePersonnes != 0) if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"); else printf("Il n'y a personne!");
Ou bien avec if (nombrePersonnes != 0) { if (nombrePersonnes != nombreAdultes) printf("Il y a des enfants!"); }; else printf("Il n'y a personne!");
While et do…while Do while (expression) Instruction instruction
Instruction For for ( expr1 ; expr2 ; expr3 ) Instruction expr1 est l'expression qui effectue les initialisations nécessaires avant l'entrée dans la boucle ; expr2 est le test de continuation de la boucle ; il est évalué avant l'exécution du corps de la boucle ; expr3 est une expression (par exemple une incrémentation) évaluée à la fin du corps de la expr1 ; while (expr2) { instruction expr3; }
Instruction for Exemple for (i = 0; i < 10; i++) t[i] = 0; i=0; while (i<10){ i++; }
Instruction switch switch ( expression ) Corps Exemple j=0; switch (i) { case 3: j++; case 2: case 1: } switch ( expression ) Corps Le corps de l'instruction switch prend la forme d'un bloc f...g renfermant une suite d'instructions entre lesquelles se trouvent des constructions de la forme case expression-constante : Ou bien default :
pointeurs Un pointeur est une variable dont la valeur est l'adresse d'une cellule de la mémoire. Déclaration: type *variable Référence *variable Opération & : obtention de l'adresse d'un objet occupant un emplacement de la mémoire Exemple : type x, *p; p=&x;
exercices Ecrire le programme C qui calcule la somme des 10 premiers nombres à partir de 0 Ecrire le programme C qui calcule le factoriel de 25.
Lecture/ecriture La bibliothèque standard <stdio> contient un ensemble de fonctions qui assurent la communication de la machine avec le monde extérieur. Les plus importantes: printf() : écriture formatée de données scanf() : lecture formatée de données putchar() : écriture d'un caractère getchar() : lecture d'un caractère
ecriture printf() : utilisée pour transférer du texte, des valeurs de variables ou des résultats d'expressions vers le fichier de sortie standard stdout (par défaut l'écran). printf("<format>",<Expr1>,<Expr2>, ... ) "<format>" : format de représentation. Les spécificateurs de format commencent toujours par le symbole % et se terminent par un ou deux caractères qui indiquent le format d'impression. <Expr1>,... : variables et expressions dont les valeurs sont à représenter char B = 'A'; printf("Le caractère %c a le code %i !\n", B, B); va afficher sur l'écran: Le caractère A a le code 65 !
Spécification de format printf: Pour pouvoir traiter correctement les arguments du type long, il faut utiliser les spécificateurs %ld, %li, %lu, %lo, %lx. SYMBOLE TYPE IMPRESSION COMME %d ou %i int entier relatif %u entier naturel (unsigned) %o entier exprimé en octal %x entier exprimé en hexadécimal %c char Caractère %f float rationnel en notation décimale %e rationnel en notation scientifique %s char* chaîne de caractères
Les spécificateurs %f et %e peuvent être utilisés pour représenter des arguments du type float ou double. La mantisse des nombres représentés par %e contient exactement un chiffre (non nul) devant le point décimal. Cette représentation s'appelle la notation scientifique des rationnels. Pour pouvoir traiter correctement les arguments du type long double, il faut utiliser les spécificateurs %Lf et %Le. Exemple float N = 12.1234; double M = 12.123456789; long double P = 15.5; printf("%f", N); ==> 12.123400 printf("%f", M); ==> 12.123457 printf("%e", N); ==> 1.212340e+01 printf("%e", M); ==> 1.212346e+01 printf("%Le", P); ==> 1.550000e+01
lecture scanf() : symétrique à printf; elle nous offre pratiquement les mêmes conversions que printf, mais en sens inverse. scanf("<format>",<AdrVar1>,<AdrVar2>, ...) <format>: format de lecture des données <AdrVar1>,… : adresses des variables auxquelles les données seront attribuées Les données reçues correctement sont mémorisées successivement aux adresses indiquées par <AdrVar1>,... . L'adresse d'une variable est indiquée par le nom de la variable précédé du signe &.
Lors de l'entrée des données, une suite de signes d'espacement (espaces, tabulateurs, interlignes) est évaluée comme un seul espace. Dans la chaîne de format, les symboles \t, \n, \r ont le même effet qu'un simple espace. Exemple int JOUR, MOIS, ANNEE; scanf("%i %i %i", &JOUR, &MOIS, &ANNEE); les entrées suivantes sont correctes et équivalentes: 12 4 1980 ou 12 004 1980 ou 12 4 1980 scanf("%i/%i/%i", &JOUR, &MOIS, &ANNEE); accepte rejette 12/4/1980 12 4 1980 12/04/01980 12 /4 /1980
Ecriture d’un caractere putchar('a'); transfère le caractère a vers le fichier standard de sortie stdout. Les arguments de la fonction putchar sont ou bien des caractères (c.-à-d. des nombres entiers entre 0 et 255) ou bien le symbole EOF (End Of File). Exemples char A = 225; char B = '\a'; int C = '\a'; putchar('x'); /* afficher la lettre x */ putchar('?'); /* afficher le symbole ? */ putchar('\n'); /* retour à la ligne */ putchar(65); /* afficher le symbole avec le code 65 (ASCII: 'A') */
En général, getchar est utilisé dans une affectation: Les valeurs retournées par getchar sont ou bien des caractères (0 - 255) ou bien le symbole EOF. Comme la valeur du symbole EOF sort du domaine des caractères, le type résultat de getchar est int. En général, getchar est utilisé dans une affectation: int C; C = getchar();
Fonctions Exemple : } déclarations locales instructions TypeRés NomFonct (TypePar1 NomPar1 , TypePar2 NomPar2, ... ) { déclarations locales instructions } Exemple : int MAX(int N1, int N2) { if (N1>N2) return N1; else return N2;
Si une fonction ne fournit pas de résultat, il faut indiquer void (vide) comme type du résultat. Si une fonction n'a pas de paramètres, on peut déclarer la liste des paramètres comme (void) ou simplement comme () . Le type par défaut est int; autrement dit: si le type d'une fonction n'est pas déclaré explicitement, elle est automatiquement du type int. Il est interdit de définir des fonctions à l'intérieur d'une autre fonction (comme en Pascal). La fonction principale main est du type int. Elle est exécutée automatiquement lors de l'appel du programme. int main(void)
Prototype d'une fonction Toute fonction doit être déclarée avant son utilisation. La déclaration d'une fonction se fait par un prototype de la fonction qui indique uniquement le type des données transmises et reçues par la fonction. Déclaration : Prototype d'une fonction TypeRés NomFonct (TypePar1 , TypePar2 , ... ); ou bien TypeRés NomFonct (TypePar1 NomPar1 , TypePar2 NomPar2, ... );
Règles pour la déclaration des fonctions De façon analogue aux déclarations de variables, nous pouvons déclarer une fonction localement ou globalement. Déclaration locale: Une fonction peut être déclarée localement dans la fonction qui l'appelle (avant la déclaration des variables). Elle est alors disponible à cette fonction. Déclaration globale: Une fonction peut être déclarée globalement au début du programme (derrière les instructions #include). Elle est alors disponible à toutes les fonctions du programme. Déclaration implicite par la définition: La fonction est automatiquement disponible à toutes les fonctions qui suivent sa définition.
Appel des fonctions L'appel d'une fonction se fait en écrivant son nom, suivi d'une paire de parenthèses contenant éventuellement une liste d'arguments effectifs. Exemple: Déclaration type nom_fonction ( type1 arg_formel1 , ... typek arg_formelk ) Appel nom_fonction ( arg_effectif 1 , ... arg_effectif k ) Transmission des valeurs des arguments arg_formel1 = arg_effectif 1 ... arg_formelk = arg_effectif k
Arguments des fonctions Passage des arguments: L'idée maitresse est qu'en C le passage des arguments des fonctions se fait toujours par valeur. Après avoir fait les conversions opportunes la valeur de chaque argument effectif est affectée à l'argument formel correspondant.
exemple void ETOILES(int N) { while (N>0) { printf("*"); N--; } printf("\n"); La fonction TRIANGLE, appelle la fonction ETOILES en utilisant la variable L comme paramètre: void TRIANGLE(void) { int L; for (L=1; L<10; L++) ETOILES(L);
Passage par adresse Pour changer la valeur d'une variable de la fonction appelante, nous devons procéder comme suit: - la fonction appelante doit fournir l'adresse de la variable et la fonction appelée doit déclarer le paramètre comme pointeur. On peut alors atteindre la variable à l'aide du pointeur.
exemple Nous appelons la fonction par: void PERMUTER (int *A, int *B) { int AIDE; AIDE = *A; *A = *B; *B = AIDE; } Nous appelons la fonction par: PERMUTER(&X, &Y); Résultat: Le contenu des variables X et Y est échangé.
Passage d’adresse d’un tableau En général, on fournit l'adresse du premier élément du tableau, qui est donnée par le nom du tableau. Dans la liste des paramètres d'une fonction, on peut déclarer un tableau par le nom suivi de crochets, type nom [] ou simplement par un pointeur sur le type des éléments du tableau: type *nom
Considérons la situation suivante: La fonction main appelle la fonction FA. La fonction FA appelle la fonction FB. Nous obtenons donc la hiérarchie suivante:
a) Déclarations locales des fonctions et définition 'top-down' /* Définition de main */ main() { /* Déclaration locale de FA */ int FA (int X, int Y); ... /* Appel de FA */ I = FA(2, 3); ... } /* Définition de FA */ int FA(int X, int Y) { /* Déclaration locale de FB */ int FB (int N, int M); ... /* Appel de FB */ J = FB(20, 30); ... /* Définition de FB */ int FB(int N, int M) { ...
Définition 'bottom-up' sans déclarations La définition 'bottom-up' commence en bas de la hiérarchie: La fonction main se trouve à la fin du fichier. Les fonctions qui traitent les détails du problème sont définies en premier lieu. /* Définition de FB */ int FB(int N, int M) { ... } /* Définition de FA */ int FA(int X, int Y) { ... /* Appel de FB */ J = FB(20, 30); } /* Définition de main */ main() { /* Appel de FA */ I = FA(2, 3);
Déclaration globale des fonctions et définition 'top-down' /* Déclaration globale de FA et FB */ int FA (int X, int Y); int FB (int N, int M); /* Définition de main */ main() { ... /* Appel de FA */ I = FA(2, 3); ... } /* Définition de FA */ int FA(int X, int Y) { ... /* Appel de FB */ J = FB(20, 30); /* Définition de FB */ int FB(int N, int M) {
1)Définir la hiérarchie des appels pour les 3 fonctions 2)définir les fonctions selon les trois méthodes en ajoutant les déclarations manquantes #include <stdio.h> main() { float R; printf("Introduire le rayon du cercle : "); scanf("%f", &R); printf("La surface du cercle est %f. \n", SURFACE(R)); return 0; } double PI(void) { return 3.14159265; double SURFACE(float RAYON) { return PI()*RAYON*RAYON;
Instruction return 1) return expression ; Ou bien 2) return ; l'instruction return provoque l'abandon de la fonction en cours et le retour à la fonction appelante. Dans la forme 1) expression est évaluée ; son résultat est la valeur que la fonction renvoie à la fonction appelante
tableaux Déclaration : type indent [taille] Exemple : tableau de 5 entiers : int t[5] En C, le nom d'un tableau est le représentant de l'adresse du premier élément du tableau. Les adresses des autres composantes sont calculées (automatiquement) relativement à cette adresse. short A[5] = {1200, 2300, 3400, 4500, 5600};
Initialisation Lors de la déclaration d'un tableau, on peut initialiser les composantes du tableau, en indiquant la liste des valeurs respectives entre accolades. Exemples int A[5] = {10, 20, 30, 40, 50}; float B[4] = {-1.05, 3.33, 87e-5, -12.3E4}; int C[10] = {1, 0, 0, 1, 1, 1, 0, 1, 0, 1}; Il faut évidemment veiller à ce que le nombre de valeurs dans la liste corresponde à la dimension du tableau. Si la liste ne contient pas assez de valeurs pour toutes les composantes, les composantes restantes sont initialisées par zéro. Réservation automatique Si la dimension n'est pas indiquée explicitement lors de l'initialisation, alors l'ordinateur réserve automatiquement le nombre d'octets nécessaires. int A[] = {10, 20, 30, 40, 50}; ==> réservation de 5*sizeof(int) octets (dans notre cas: 10 octets)
En déclarant un tableau par: int A[5]; nous avons défini un tableau A avec cinq composantes, auxquelles on peut accéder par: A[0], A[1], ... , A[4] Important Considérons un tableau T de dimension N: - l'accès au premier élément du tableau : T[0] - l'accès au dernier élément du tableau : T[N-1]
Tableaux à deux dimensions On appelle L le nombre de lignes du tableau et C le nombre de colonnes du tableau. L et C sont alors les deux dimensions du tableau. Un tableau à deux dimensions contient donc L*C composantes.
Déclaration : type identif [nbre_Ligne][nbre_Col]; Exemple float B[2][20]; Les composantes d'un tableau à deux dimensions sont stockées ligne par ligne dans la mémoire. Lors de la déclaration d'un tableau, on peut initialiser les composantes du tableau, en indiquant la liste des valeurs respectives entre accolades. Exemples int A[3][10] ={{ 0,10,20,30,40,50,60,70,80,90}, {10,11,12,13,14,15,16,17,18,19}, { 1,12,23,34,45,56,67,78,89,90} };
Première ligne, première colonne : Accès : Identif [Ligne][Colonne] Première ligne, première colonne : Identif[0][0] Dernière ligne; dernière colonne : Identif[L-1][C-1]