POUR LES ETIDIANTS DE ENFOMATIQURE ELECTOTECH ELCTRONIQUE

الموضوع في 'منتدى الإعلام الآلي' بواسطة +++AEH+++, بتاريخ ‏11 يناير 2008.

  1. +++AEH+++

    +++AEH+++ عضو مميز

    POUR LES ETIDIANTS DE INFOMATIQURE ELECTROTECH ET ELCTRONIQUE

    السلام عليكم جميعا إخوتي في الله

    اليوم جايبلكم دروس في القمة في مقياس الإعلام الآلي شرح رائع ومبسط

    تخيلو أنكم ستتقنون لغة C للبرمجة

    لذا أرجو الصبر وقراءة الموضوع جيدا وهو مفيد
    أنا عن نفسي كنت لا أفهم شيئا في هذه اللغة ولكن بهذا الملخص أصبحت أفهم أو على الأقل أستطيع حل بعض من تمارين السلسلة بدون الإستعانة بالأستاذ

    أعطيكم إقتراح وهو
    أن تقوموا بطبع الموضوع لكي يتوضح بشكل أفضل ولراحة العينين أيضا

    والموضوع حصري بمنتدياتنا الغالية

    وفقكم الله
    والآن مع الموضوع​



    1. Présentation
    Notions : langage interprété vs. langage compilé, gcc, -Wall, a.out, shell.
    1.1 Historique
    Le langage C a été créé par Brian Kernighan et Dennis Ritchie au début des années 70 afin d'eviter autant que possible l'utilisation de l'assembleur dans l'écriture du système UNIX. L'avantage du C sur l'assembleur est qu'il permet aux programmes d'être plus concis, plus simples à écrire et qu'ils sont facilement portables sur d'autres architectures.
    C est un langage :
    • qui permet de tout faire,
    • qui permet de créer des exécutables très rapides (compilé),
    • qui possède peu d'instructions.
    1.2 Un premier programme
    1 /*
    2 * Mon premier programme C.
    3 */
    4 #include<stdio.h>
    5 int main()
    6 {
    7 printf("hello world\n");
    8 return 0;
    9 }
    ________________________________________
    Points importants à noter :
    [1, 2, 3] Commentaire sur plusieurs lignes.
    [4] On indique un fichier #include pour pouvoir utiliser la fonction printf (fonction de la librairie stdio).
    [5] Fonction principale, point d'entrée du programme.
    [6] Accolade ouvrante, débute le code de la fonction.
    [7] La fonction printf permet d'afficher un message à l'écran. Les guillemets délimitent la chaîne de caractère. Le caractère \n signifie un saut de ligne suivi d'un retour chariot. Le point-virgule à la fin de chaque phrase est indispensable.
    [8] Le programme renvoie la valeur 0 au shell appelant à la fin de son exécution.
    [9] Accolade fermante, fin de bloc de la fonction principale.
    1.3 Compilation et exécution du programme
    Pour compiler et exécuter ce programme sous UNIX, il faut d'abord le copier dans un fichier se terminant par l'extension .c à l'aide d'un éditeur de texte (vi ou emacs). Appelons ce fichier "intro.c". On le compile sous Linux avec gcc (sous d'autre UNIX, le compilateur s'appelle le plus souvent cc) :
    $ gcc -o intro intro.c
    Cela génère un fichier exécutable qui s'appelle "intro". Pour le lancer :
    $ ./intro
    hello world
    L'utilisation de l'option -Wall permet de s'assurer que le code est syntaxiquement impeccable (tolérance du compilateur gcc) :
    $ gcc -Wall -o intro intro.c
    1.4 Exercices
    1. "intro.c" : recopiez le programme précédent, compilez-le et lancez le. Ensuite, modifiez le afin qu'il produise le résultat suivant :
    2. $ ./intro
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12.
    13.

    3. hello
    4. world
    Modifiez la valeur retournée par la fonction main et une fois le programme compilé et exécuté, testez la commande shell suivante : echo $?.
    2. Les variables
    2.1 Variables et types fondamentaux
    Notions : entiers (char, int, short, long), flottants (float, double), nombres signés et non-signés (signed, unsigned), constantes (const), affectation, conversion, débordement.
    Déclarer une variable pour pouvoir l'utiliser
    Une variable est un contenant qui stocke une valeur, comme un nombre. Une variable peut donc être vue comme une boite. En C, toute variable doit être déclarée avant d'être utilisée. Un nom de variable doit commencer par une lettre ou bien par le caractère "_". La suite peut être composée de chiffres, de lettres ou de "_". Attention, le compilateur fait la distinction entre minuscules et majuscules.
    Pour déclarer une variable, on écrit son type suivit de son nom. Le type de la variable spécifie le format et la taille des données qui pourront être stockées grâce à elle en mémoire.
    Il est possible de déclarer plusieurs variables d'un même type sur la même ligne en les séparant par des virgules.
    Exemple de déclarations de variable :
    int age;
    float prix, taux;
    long numero_secu;
    Une variable doit toujours être déclarée à l'intérieur d'une fonction (exceptées les variables globales, voir le chapitre 6) et avant toute instruction. La fonction main ne fait pas exception à la règle :
    ________________________________________
    int main()
    {
    int a, b;
    float x = 5;
    b = 0; /* une premiere instruction */
    /* interdiction de declarer une variable a partir de la ! */

    return 0;
    }
    ________________________________________
    Différents types de données
    Il y a plusieurs types de base :
    • char, qui correspond à une variable d'un octet, sert à représenter aussi bien des petits entiers que des caractères imprimables. Il est très correct d'écrire 'a' + 2 car 'a' est un entier.
    Attention, le jeu de caractères imprimables standard (caractères ASCII) est codé sur 7 bits. Il ne comprends pas les caractères accentués français (codage sur 8 bits).
    • short int pour stocker des petits entiers.
    • int pour stocker des entiers.
    • long int pour stocker des entiers plus grand.
    • float et double pour stocker des nombres réels.
    Les entiers sont signés par défaut mais ils peuvent être non-signé grâce à la directive unsigned. Attention, sur certaines architectures, les char sont non-signés par défaut ! Il est possible de les signer avec la directive signed.
    La directive const permet d'empêcher la modification d'une variable donnée. Le contrôle se fait au moment de la compilation :
    const int i = 70; /* i ne pourra etre modifie */
    const char *p; /* pointeur sur constante */
    char * const p; /* pointeur constant */
    Il n'y a pas de type "chaîne de caractères". Pour ce faire, on utilise les tableaux (ici des tableaux de caractères, voir au chapitre 8).
    Taille et format des données sur une architecture de type i586 :
    ________________________________________
    Type Octets Portée
    char 1 -128 à +127
    short int 2 -32768 à +32767
    int 4 -2 147 483 648 à +2 147 483 647
    long int 8 -4294967296 à +4294967295
    unsigned char 1 0 +255
    unsigned short int 2 0 à +65535
    unsigned int 4 0 à +4 294 967 295
    unsigned long int 8
    float 4
    double 8

    Exemple de débordement :
    ________________________________________
    #include<stdio.h>
    main()
    {
    short a=65535, b=1, c;
    unsigned short ua=65535, ub=1, uc;
    c = a+b;
    printf("%d\n", c);
    uc = ua+ub;
    printf("%d\n", uc);
    return 0;
    }
    ________________________________________
    Affecter une valeur à une variable
    On affecte une valeur à une variable avec l'opérateur d'affectation =. La syntaxe d'une affectation est : nom = expression; . On peut initialiser une variable en lui fournissant une valeur au moment de sa déclaration. Exemples :
    int a, b, c = 7;
    a = 34;
    b = (a + 21) * c;
    a = a + 1; /* cas particulier */
    Attention, avant d'utiliser le contenu d'une variable, il faut qu'elle ait été initialisée, sinon, le résultat est imprévisible :
    ________________________________________
    int main()
    {
    int truc;
    printf("%d\n", truc);
    }
    ________________________________________
    Une affectation renvoie une valeur. Cela permet de réaliser des affectations combinées :
    int a, b;
    a = b = 10; /* l'expression b = 10 renvoie comme valeur 10 */
    Conversion de valeurs et constantes littérales
    Si par exemple on additionne un int et un float, il y a une conversion implicite de l'int en float. Si le résultat est stocké dans un int, il y a aussi une conversion avec un risque d'altération du résultat. Une conversion explicite est faite de la façon suivante :
    (nouveau_type) variable; /* la valeur stockee dans la variable est
    convertie */
    Exemple de conversion :
    int a = 3, b = 6;
    float c;
    c = (float) a * (float) b;
    Par défaut, un nombre utilisé dans une expression, on parle de constante littérale, est de type int. Il est possible d'utiliser des constantes littérales de n'importe quel autre type en procédant de la façon suivante :
    5 /* int */
    5u ou 5U /* unsigned */
    5l ou 5L /* long */
    5ul /* unsigned long */
    05 /* octal */
    0x5 /* hexadecimal */
    5.f /* float */
    5. /* double */
    2.2 Opérateurs
    Notions : opérateurs arithmétiques, operateurs binaires, expression, évaluation, priorités.
    • Opérateurs arithmétiques : + - * / % ++a a++ &horbar;-a a&horbar;-
    • Racourcis d'écritures : variable (operateur)= (expression)
    • Gestion des priorités : ()
    • Operateurs binaires : & | ^ ~ << >>
    2.3 Saisir et afficher des données
    Notions : printf, scanf, format.
    La commande printf sert à afficher des données. Sa syntaxe est printf("...",e1,e2,...,en). Il existe des caractères spéciaux. Les plus utilisés sont :
    \n Début de ligne suivante
    \t Tabulation
    \\ Affiche un barre inverse
    Les indicateurs de formats les plus utilisés sont :
    %c caractère
    %d décimal
    %x et %X hexadécimal
    %f float
    %.2f float avec deux chiffres après la virgule
    %s chaîne de caractères
    2.4 Un programme simple
    Voici un autre exemple de programme simple mais qui cette fois ci utilise des variables :
    ________________________________________
    #include<stdio.h>
    int main()
    {
    int kilos = 50; /* initialisation lors de la déclaration */
    int sacs, total;
    sacs = 300;
    total = sacs * kilos;
    printf("Il y a %d kilos dans %d sacs de haricots\n", total, sacs);
    }
    ________________________________________
    2.5 Exercices
    1. "taxe.c" : programme qui saisit un prix H.T. et affiche le prix T.T.C..
    2. "degresF.c" : programme qui convertit des degrés Celsius en degrés Farheneit : F(C) = (C*9/5)+32.
    3. "add1.c" : Programme qui additionne 2 entiers saisis et qui affiche le résultat.
    4. "conv.c" : programme qui affiche en hexadécimal un nombre saisi en décimal. Faire aussi l'inverse.
    3. Branchements conditionnels
    Notions : if, else, switch, ?:.
    Dans la vie, on a souvent besoin de décider du déroulement des choses en fonction de certaines conditions. Par exemple : "Si il pleut, je prend mon parapluie" (ceci n'a d'ailleurs de sens que si je souhaite sortir !). De mêmen en C, il est possible de décider de n'exécuter une action que si une condition particulière est remplie.
    Pour tester si une condition est vraie avant d'exécuter une action, on dispose d'opérateurs de comparaison : == != < <= >= > et d'opérateurs logiques ! || &&. Une expression utilisant ces opérateurs renvoie 0 si la condition testée est fausse et 1 si elle est vraie.
    Une condition peut consister en une expression. Dans ce cas, si la valeur renvoyée par l'expression égale zéro, la condition est fausse. Elle est vraie pour toute autre valeur.
    3.1 if...
    Si la condition de la structure if... est vraie, on peut exécuter une instruction unique ou bien un ensemble d'instruction entre accolades. Dans ce dernier cas, attention, on ne met pas de au ; après l'accolade fermante !
    if(condition) /* Si la condition est vraie... */
    instruction; /* ...on execute une instruction unique */

    if(condition) /* Si la condition est vraie... */
    { /* ...on execute un bloc d'instructions */
    instruction;
    ...
    instruction;
    }
    Exemples :
    ________________________________________
    if(n>20)
    printf("Vous avez droit au tarif de groupe\n");

    if(x == 3){
    printf("une instruction !\n");
    printf("une autre instruction !\n");
    }
    ________________________________________
    3.2 if... else...
    L'instruction else permet d'exécuter une instruction au cas ou la condition est fausse. C'est l'équivalent du "sinon" (exemple : "si il fait beau, je vais au zoo, sinon je vais au cinéma").
    if(condition)
    instruction;
    else
    instruction;
    Exemple :
    ________________________________________
    if(choix == 'o' || choix == 'O')
    printf("On va au cinema.\n");
    else
    printf("On va au zoo.\n");
    ________________________________________
    3.3 switch...
    Remplace les if... else... les uns à la suite des autres.
    switch(expression) {
    case constante : instructions;
    case constante : instructions;
    ...
    default : instructions;
    }
    Exemple :
    ________________________________________
    switch(choix) {
    case 't' : printf("vous voulez un triangle"); break;
    case 'c' : printf("vous voulez un carre"); break;
    case 'r' : printf("vous voulez un rectangle"); break;
    default : printf("erreur. recommencez !");
    }
    ________________________________________
    L'instruction break est indispensable pour sortir du switch. Si on l'oublie, une fois le branchement exécuté, toutes les instructions qui suivent sont exécutées.
    3.4 Un opérateur très particulier : ?:
    Il s'agit en réalité d'un opérateur qui teste une condition et qui renvoie la valeur de la première expression si la condition est vraie ou de la seconde expression si c'est faux :
    (condition)?(instruction_si_vrai):(instruction_si_faux);
    Exemple :
    c = a<b ? 1 : 0; /* si a<b est vrai, on met 1 dans 'c' */
    3.5 Exercices
    1. "vote.c" : programme qui demande l'âge de la personne. Si la personne a au moins 18 ans, alors on affiche "peut voter", sinon, on affiche "ne peut pas voter".
    2. "div.c" : programme qui indique si un nombre est divisible par 2. Il existe 2 méthodes, l'une utilisant l'opérateur modulo % et l'autre les masques de bits.
    3. "decal.c" : prendre un entier et opérer un décalage de 1 bit à gauche. Qu'observe-t-on ?
    4. Les itérations
    Notions : while, for, break, continue.
    Principe : répéter plusieurs fois une série d'instructions.
    4.1 while...
    L'instruction while... permet d'exécuter un bloc d'instructions tant qu'une condition est vraie.
    while(condition)
    instruction;
    Exemple :
    ________________________________________
    i=0;
    while(i<10){
    printf("%d\n", i);
    i++;
    }
    ________________________________________
    4.2 for...
    for(initialisation ; condition ; opération)
    instruction;
    Voici ce qui se passe :
    • La première fois qu'on rentre dans la boucle, on effectue généralement une initialisation (par exemple, i=0).
    • On teste ensuite la condition. Si elle est vrai, on effectue la ou les instructions du corps de la boucle. Sinon, on sort de la boucle (la boucle est terminée).
    • Après cela, on effectue une opération qui est en principe l'incrémentation d'un compteur.
    • On re-teste la condition, etc...
    Exemple :
    ________________________________________
    for(i=0;i<10;i++)
    printf("%d\n", i);
    ________________________________________
    Il est possible de procéder à plusieurs initialisations et à plusieurs opérations en les séparant par des virgules. Pour procéder à plusieurs tests, il faut utiliser les connecteurs logiques && ou || :
    ________________________________________
    for(i=0,j=1 ; i<10 && j<100 ; i++, j=j*2)
    printf("%d %d\n", i, j);
    ________________________________________
    4.3 do... while...
    C'est une variante de la boucle while vue précédement.
    do
    instruction;
    while(condition);
    4.4 break et continue
    Les instructions break et continue permettent de sortir d'une itération.
    Exemple :
    ________________________________________
    i=0;
    while(1){
    printf("%d\n", i);
    i++;
    if(i>10)
    break;
    }
    ________________________________________
    4.5 Comment faire n fois quelque chose
    Chaque boucle du code suivant affiche 10 fois la même phrase :
    ________________________________________
    /* en utilisant 'while' */
    i=0;
    while(i<10){
    printf("hello world !\n");
    i++;
    }

    /* exactement la meme chose en utilisant 'for' */
    for(i=0;i<10;i++)
    printf("hello world !\n");
    ________________________________________
    On remarque que i est par convention d'écriture toujours initialisé à zéro.
    4.6 Les pièges infernaux des boucles
    Certaines erreurs sont très courantes quand on débute la programmation en C. En voici un certain nombre.
    Ici, i n'est pas initialisé, ce qui rend le déroulement de l'itération imprévisible :
    ________________________________________
    /* oups ! on a oublie d'initialiser 'i' */
    while(i<10){
    printf("hello world !\n");
    i++;
    }
    ________________________________________
    Une autre erreur possible, se tromper dans la comparaison. Ici, la condition ne sera jamais vraie, donc la boucle ne sera pas exécutée :
    ________________________________________
    i=0;
    while(i>10){ /* on a mis '>' ou lieu de '<' */
    printf("hello world !\n");
    i++;
    }
    ________________________________________
    Attention, l'erreur la plus fréquente est sans doute d'oublier d'incrémenter i, il en résulte une boucle infinie :
    ________________________________________
    i=0;
    while(i<10){
    printf("hello world !\n");
    }
    ________________________________________
    Attention aux ; en trop. La boucle suivante ne fait rien pendant 10 fois puis affiche la phrase hello world ! une seule fois :
    ________________________________________
    for(i=0;i<10;i++);
    printf("hello world !\n");
    ________________________________________
    Par ailleurs, attention à la syntaxe des boucles for. A l'intérieur du for, on sépare les parties d'initialisation, de test et d'opération par des ;. Attention donc à ne pas confondre avec les , :
    ________________________________________
    for(i=0,i<10,i++) /* ERREUR : des ',' au lieu des ';' */
    printf("hello world !\n");
    ________________________________________
    4.7 Exercices
    1. "simple.c" : afficher 5 fois la lettre x.
    2. "boucles.c" : afficher les nombres de 1 à 10 puis de 20 à 1 de trois en trois (faire deux fois l'exercice : avec "for" et avec "while").
    3. "mult.c" : afficher les tables de multiplication de 1 à 9.
    4. "puis1.c" : calculer 21 à la puissance 3.
    5. "puis2.c" : programme qui saisit deux nombres entiers non signés et calcule le premier à la puissance du second. On affiche le résultat.
    6. "calc.c" : faire une mini calculette 4 opérations :
    7. $ calc
    8. > 3 4 +
    9. 7
    10. > 5 6 *
    11. 30
    12. > q
    13. $
    14. "premiers.c" : saisir un nombre et indiquer si il est premier.
    15. "bits.c" : afficher un nombre entier en binaire.
    5. Les fonctions
    Notions : déclaration, prototype, arguments, valeur renvoyée, return, void, passage par valeur.
    Jusqu'à présent, on a utilisé des fonctions prédéfinies (printf, scanf, etc.). Il est aussi possible d'écrire ses propres fonctions.
    5.1 Qu'est-ce qu'une fonction ?
    Un regroupement d'instructions
    Une fonction est un regroupement d'instruction. Imaginons une tortue qui sait seulement se déplacer tout droit et tourner à droite ou à gauche. On lui apprend à dessiner un carré en la faisant aller tout droit, puis tourner à droite, puis continuer tout droit, etc, quatre fois. Ces actions (en programmation, on parle d'instructions) peuvent être regroupées au sein d'une procédure que l'on appelerais "dessiner_un_carré". Ainsi, quand on dirait à la tortue "dessiner_un_carré", elle effectuerait les instructions de la procédure les unes à la suite des autres et dessinerait un carré.
    Des arguments pour des résultats différents
    Une fonction peut prendre des arguments. En reprenant la métaphore précédente, on pourrait apprendre à la tortue à dessiner un carré d'une taille spécifiée. On dirait par exemple à la tortue "dessiner_un_carré de 18 cm". On a là un exemple de fonction qui prend un argument.
    Des fonctions qui renvoient un résultat
    Une fonction peut retourner une valeur. On peut très bien imaginer une boite noire dans laquelle on peut insérer des jetons rouges par une fente et des jetons bleus par une autre. Selon le nombre de jetons insérés, la boite nous renvoie un certains nombre de jetons noirs. Nous avons là une fonction, la boîte, qui prend des arguments, les jetons de couleur, et qui renvoie une valeur, des jetons noirs.
    5.2 Prototype
    Le prototype d'une fonction permet de connaitre le nombre et le type de ses arguments. Il nous donne aussi une idée de ce qu'elle renvoie.
    Exemple :
    float taxe(float);
    int putchar (int); /* Fonction predefinie */
    On peut souhaiter créer une fonction qui ne prend aucun argument et qui ne renvoie pas de valeur. En C, void est un type spécial qui signifie "rien". Il est utilisable à la place des arguments et de la valeur renvoyée.
    Exemple :
    void aff_menu(void);
    int getchar (void); /* Fonction predefinie */
    5.3 Définition
    Une fonction est définie de la façon suivante :
    type nom(type var1, type var2, ..., type varn)
    {
    instruction;
    ...
    return valeur;
    }
    Une fonction doit se terminer par l'instruction return pour se terminer. Cette instruction return permet de retourner une valeur.
    Par exemple, la fonctions suivante prend en argument deux entiers et qui renvoie leur somme :
    ________________________________________
    int addition(int a, int b)
    {
    int c;
    c = a + b;
    return c;
    }

    int main()
    {
    int x = 2, y;

    y = addition(x,5)

    return 0;
    }
    ________________________________________
    L'appel à la fonction addition fonctionne en plusieurs étapes :
    1. Dans le main, on fait appel à la fonction addition qui prend en argument la valeur de x et la valeur 5.
    2. Ces valeurs sont copiées dans les variables a et b définies dans l'en-tête de la fonction.
    3. La fonction calcule et retourne la valeur de c.
    4. On revient dans le main. La fonction est remplacée par la valeur qu'elle retourne. Cette valeur est mise dans y.
    5.4 Visibilité des variables dans un programme
    Les variables ne sont pas visibles en dehors de la fonction où elles ont été définies. Quand je suis dans une fonction, je n'ai accès qu'aux variables de cette fonction, c'est tout. La fonction main ne fait pas exeception à la règle.
    Pour comprendre cela, on peut prendre la métaphore suivante. Une fonction est comme une boite noire avec des fentes pour insérer des jetons dedans. Examinons comment la boite est faite. Quand on insère des jetons, ceux-ci sont recueillis dans des petits casiers étiquetés qui ont un nom. En informatique, ces casiers sont appelés "variables". Ces casiers peuvent avoir n'importe quel nom, il n'y a aucune confusion possible avec les casiers en dehors de la boite. Par ailleurs, moi qui suit en dehors de la boite, je ne peut manipuler les casiers dans la boite. De la même façon, il y a dans la boite un petit rat qui fait tourner une roue. Et bien ce petit rat ne peut manipuler que les casiers qui sont dans sa boite.
    Une variable peut être définie en dehors de toute fonction, on dit alors d'elle qu'elle est "globale" ou "externe". Une telle variable est visible de toutes les fonctions, à n'importe quel endroit du programme.
    5.5 Quelques exemples de fonctions
    Cette fonction affiche n fois une lettre. Attention, putchar prend en argument un int :
    ________________________________________
    void affiche_car(int c, int n)
    {
    while(n--)
    putchar(c);
    /* return implicite */
    }
    ________________________________________
    Une fonction qui ne prend aucun argument et qui ne renvoie rien :
    ________________________________________
    void aff_hello(void)
    {
    printf("hello world !\n");
    }
    ________________________________________
    5.6 Déclaration
    Si une fonction est définie dans le code source avant la portion de code qui l'utilise, il n'est pas besoin de la déclarer. Dans le cas contraire il faut la déclarer en mettant son prototype dans l'entête du programme (c'est ce qu'on fait d'une manière particulière avec les fichiers d'include pour pouvoir utiliser des fonctions prédéfinies).
    Exemple :
    ________________________________________
    void affiche_car(int, int); /* declaration */

    int main()
    {
    char c = "x";
    affiche_car("-", 10); /* appel de la fonction */
    affiche_car((int) c, 5);
    return 0;
    }

    void affiche_car(int c, int n) /* definition */
    {
    /* Fonction qui affiche n fois une lettre. */
    while(n--)
    putchar(c);
    /* return implicite */
    }
    ________________________________________
    5.7 Comprendre le passage par valeur
    Une fonction ne prend en argument que des valeurs. Attention, si on passe en argument une variable, on passe en réalité une copie de sa valeur en argument. Une variable ou une expression compliquée passés en argument sont toujours remplacés par leur valeur :
    ________________________________________
    #include<stdio.h>

    int main()
    {
    int a = 2, b = 3;

    printf("%d\n", 5);
    printf("%d\n", a);
    printf("(%d+%d)^2 = %d\n", a, b, (a*a+b*b+2*a*b));

    return 0;
    }
    ________________________________________
    Ce programme affiche :
    $ ./a.out
    5
    2
    (2+3)^2 = 25
    Le passage par valeur implique qu'on ne peut changer la valeur d'une variable passée en argument. Par exemple, dans le programme suivant, la fonction ne modifie pas la valeur de a :
    ________________________________________
    #include<stdio.h>

    void mafonction(int x)
    {
    x = 0;
    }

    int main()
    {
    int a=1;
    mafonction(a); /* la valeur de 'a' n'est pas modifiee */
    printf("%d", a); /* affiche 1 */
    return 0;
    }
    ________________________________________
    5.8 Comprendre la notion de valeur retournée
    L'instruction return, qui termine toujours une fonction, permet de retourner une valeur. Cela signifie que dans un programme, une fonction est remplacée par la valeur qu'elle renvoie. Par exemple, dans le programme suivant, l'appel à la fonction addition est tout à fait autorisé au sein d'une expression mathématique ou dans une fonction :
    ________________________________________
    #include<stdio.h>
    int addition(int, int);

    int main()
    {
    int x=1, y=2, z;

    /* on affecte a 'z' la valeur retournee par la fonction */
    z = addition(x+y);

    /* possible aussi */
    printf("%d\n", addition(x+y) );

    /* egalement possible ! */
    z = addition(addition(x+3),5);

    return 0;
    }

    int addition(int a, int b)
    {
    return (a+b);
    }
    ________________________________________
    5.9 Erreurs courantes
    Oublier de déclarer un nom de variable dans la définition d'une fonction :
    ________________________________________
    int addition(int, int) /* oh, on a oublie les noms de variables ! */
    {
    int c;
    c = a+b;
    return c;
    }
    ________________________________________
    Il y a aussi les ; en trop :
    ________________________________________
    int addition(int a, int b); /* il y a un ';' en trop !!! */
    {
    return a+b;
    }
    ________________________________________
    Une autre erreur très fréquente est de redéclarer les noms de variables :
    ________________________________________
    int addition(int a, int b)
    {
    int a, b; /* variables deja declarees dans l'en-tete de la fonction */
    return a+b;
    }
    ________________________________________
    Si une fonction n'est pas de type void, attention à bien retourner une valeur :
    ________________________________________
    int addition(int a, int b)
    {
    int c;
    c = a + b;
    return; /* on oublie de retourner un entier ! */
    }
    ________________________________________
    5.10 Exercices
    1. "puis3.c" : créer une fonction qui prend deux arguments et qui renvoie le premier nombre puissance le second.
    2. "puis4.c" : refaire l'exercice précédent mais faire en sorte que la fonction créée soit récursive.
    3. "fact.c" : programme qui calcule la factorielle d'un nombre saisi. On utilisera deux méthodes différentes (itération et récursion). Rappel : fact(n) = n * (n-1) * ... * 3 * 2 * 1.
    4. "rand.c" : créer une fonction qui prend en argument un entier et qui renvoie un nombre entre 0 et cet entier (vous utiliserez la fonction "rand"). Pour utiliser rand (lire éventuellement la manpage):
    5. srand( time(NULL) );
    6. j=1+(int) (10.0*rand()/(RAND_MAX+1.0));
    7. "game.c" : le programme génère un nombre au hasard entre 1 et 100 que le joueur doit deviner en un minimum de coup. Un menu propose de jouer ou de quitter.
    8. "pyra.c" : programme qui demande de saisir un nombre et trace une pyramide de la façon suivante :
    9. $ pyra
    10. > 4
    11. ^
    12. ^^^
    13. ^^^^^
    14. ^^^^^^^
    15. > 2
    16. ^
    17. ^^^
    6. Portée des données au sein d'un programme
    Notions : extern, static, variable locale, variable externe/globale.
    Une fonction ou une variable peut être rendue confidentielle (c.à.d. inutilisable en dehors du fichier où elle a été définie) avec le mot clé static (voir aussi le chapitre sur la compilation séparée).
    Une variable déclarée static existe durant toute l'exécution du programme et elle n'est pas visible en dehors de sa classe. Une telle variable est pratique pour savoir combien de fois une fonction a été appelée.
    . Les tableaux
    Notions : définition, initialisation, affectation, indices de 0 à n-1, débordements, opérateur crochets [ ], tableaux à n dimensions, conversion des noms de tableaux en pointeurs, passage de tableaux en argument pointeurs de tableaux.
    7.1 Déclaration et initialisation
    Un tableau permet de regrouper plusieurs données de même type en une entité. Les tableaux en C se déclarent avec l'opérateur [ ]. Tout comme avec les variables simples, il est possible d'initialiser un tableau lors de sa déclaration en mettant les valeurs du tableau entre accolades.
    Exemples de déclarations de tableaux :
    /* declaration d'un tableau de 10 caractères */
    char tab[10];

    /* declaration et initialisation d'un tableau de 3 entiers */
    int a[3]={1,-1,0};

    /* declaration et initialisation d'un tableau de 4 caracteres */
    char msg[]={'a','b','c','\0'};
    7.2 Affectation
    On accède à un élément d'un tableau avec son indice entre crochets. Attention, le premier élément a pour indice 0 et le dernier n-1. Le n-ième élément s'écrit tab[n].
    Les éléments d'un tableau peuvent être affectés lors de la déclaration de celui-ci. Il n'est pas possible d'affecter directement un tableau à un autre tableau. On ne peut affecter un tableau qu'élément par élément.
    Exemple :
    tab = val;
    Attention, l'exemple suivant montre une affectation correcte et une autre incorrecte :
    int tab[5];
    int tab2[5]={1,2,3,5,8}; /* ok */
    tab = tab2; /* incorrect */
    De la même manière, il est incorrect d'utiliser les opérateurs de comparaison pour comparer deux tableaux. Affectation, comparaison... toutes ces opérations ne peuvent être réalisées qu'élément par élément. L'explication de tout cela est qu'un nom de tableau utilisé dans une expression est converti en pointeur sur le premier élément de ce tableau. Ainsi, l'expression tab1 == tab2 revient à comparer les adresses en mémoire où sont implantés ces tableaux.
    Attention, une fonction ne peut jamais renvoyer un tableau.
    Exemple d'initialisation de tous les membres d'un tableau à 0 :
    for(i=0;i<100;i++) t=0;
    7.3 Les débordements
    Attention, quand on affecte un élément d'un tableau, il n'y a aucun contrôle fait pour savoir si on déborde ou non du tableau. Voici un exemple de débordement :
    int tab[5];
    tab[5] = 0; /* debordement : les indices vont de 0 a n-1 */
    Une telle erreur ne sera pas repérée par le compilateur et ne se manifestera qu'à l'exécution par un message du type Bus error ou Segmentation fault.
    7.4 Passage en argument de fonctions
    Il est tout à fait possible de passer des tableaux en argument d'une fonction. Mais attention, un tableau n'est pas une valeur et le passage en argument est très particulier.
    Au niveau de la fonction tout d'abord. La déclaration de l'argument se fait en rajoutant des crochets pour indiquer que l'argument est un tableau. Exemple :
    void mafonction(int[]); /* le prototype */

    void mafonction(int tab[]) /* la fonction */
    {
    /* code de la fonction ... */
    return;
    }
    Au niveau de l'appel de la fonction, on passe en argument juste le nom du tableau passé en argument. Exemple :
    int main()
    {
    int t[6];
    mafonction(t);
    return 0;
    }
    Attention, le passage de tableaux en argument présente des particularités.
    • Quand on est dans la fonction, on n'a aucun moyen de savoir quelle est la taille du tableau passé en argument. Si on a besoin de savoir quelle est cette taille, il faut la passer par un deuxième argument.
    • Un tableau est passé non par valeur mais par "adresse". Cela signifie que si le tableau est modifié dans la fonction, alors, comme par magie, le tableau passé en argument est réellement modifié ! Bon, ça n'est pas vraiment de la magie, mais nous verrons cela plus tard...
    7.5 Exercices
    1. "notes.c" : programme qui saisit les notes de 10 élèves, qui fait la moyenne des notes, puis qui affiche les notes au-dessus de la moyenne.
    2. "tri.c" : programme qui saisit une liste de nombre (maximum 25), la trie puis la réaffiche.
    8. Chaînes de caractères
    Notions : chaînes de caractères, \0, sizeof, fonctions de manipulation de chaînes (strlen, strcpy, strcmp, etc.).
    8.1 Définition
    Une chaîne de caractère est tout simplement une suite de caractères stockés dans un tableau. Une chaîne doit impérativement se terminer par le caractère '\0';
    Une chaîne littérale correspond à une suite de caractères entre guillemets.
    char ch[]="hello"; /* tableau de 6 caracteres avec '\0' */
    char ch2[6]="hello"; /* plus risque ! */
    Attention les chaînes littérales sont assimilées à des constantes. Le code suivant est incorrect :
    char ch[]="hello";
    char ch[3]='X'; /* incorrect */
    8.2 Fonctions de manipulation de chaînes
    Afficher une chaîne
    On utilise la fonction printf avec l'indicateur de format %s et on met en argument le nom du tableau qui contient la chaîne :
    char ch[]="hello";
    printf("%s world !\n", ch);
    Saisir une chaîne
    On utilise la fonction scanf avec l'indicateur de format %s et on met en argument le nom du tableau qui contient la chaîne. Attention, les tableau sont des cas particuliers et il n'est pas besoin de mettre le caractère & devant le nom :
    char ch[512];
    scanf("%s", ch);
    Copier, comparer, mesurer...
    Une chaîne est un tableau, on ne peut donc réaliser directement une affectation ou une comparaison.
    Pour copier une chaîne dans un tableau, on utilise la fonction strcpy :
    char ch[]="hello";
    char ch2[512];
    strcpy(ch2, ch);
    printf("%s\n", ch2);
    Pour comparer, on utilise la fonction strcmp. Une autre fonction utile est la fonction strlen qui sert à mesurer la longueur d'une chaîne (caractère nul non compris).
    8.3 Exercices
    1. "strlen.c", "strcpy.c" et "strcmp" : réimplémenter les fonctions C suivantes : strlen, strcpy et strcmp.
    2. "reverse.c" : programme qui saisit une chaîne et l'affiche à l'envers.
    9. Les pointeurs
    Notions : adresse mémoire, opérateurs * et &, passage d'arguments par adresse, pointeurs de pointeurs de ..., type void* (pointeurs generiques), const int *p et int * const p.
    9.1 Définition
    Les pointeurs sont des variables qui ont pour valeur une adresse mémoire. On les utilise surtout lors du passage d'arguments à une fonction ou lors d'allocation dynamique de mémoire.
    9.2 Déclaration
    On déclare un pointeur d'un type donné en ajoutant le signe * avant le nom du pointeur :
    char *ptr;
    int* ptr2; /* possible aussi ! */
    9.3 Les opérateurs & et *
    L'opérateur & sert à récupérer l'adresse d'une variable. Exemple :
    int var = 4;
    int *ptr; /* declaration de ptr en tant que pointeur sur 'int' */
    ptr = &var; /* 'ptr' pointe sur la variable 'var' */
    L'opérateur * sert à accèder au contenu de la case mémoire pointée. Un pointeur est une adresse en mémoire, et grâce à l'opérateur *, on peut accèder à ce qu'il y a à cette adresse (soit pour modifier ce qui y est stocké, soit pour voir ce qu'il y a là) :
    int var = 5;
    int *p;
    p = &var; /* 'p' pointe sur 'var' */
    printf("%d\n", *p);
    *p = 4; /* 'var' est modifie ! */
    printf("%d\n", *p); /* on recupere la valeur */
    Il est possible de pointer sur l'élément d'un tableau :
    int tab[5];
    int *ptr;
    ptr = &tab[4]; /* ptr pointe sur le dernier element du tableau */
    9.4 Manipulation de pointeurs
    Les pointeurs peuvent être incrémentés, décrémentés, additionnés ou soustraits. Dans ce cas, leur nouvelle valeur dépend de leur type. Incrémenter un pointeur de char ajoute 1 à sa valeur. Incrémenter un pointeur de int ajoute 2 ou 4 (cela dépend de l'architecture).
    Tout comme avec les tableaux, il est possible d'utiliser les crochets pour accèder au contenu d'un élément pointé. Les écritures suivantes sont équivalentes :
    int tab[5], *p = tab;
    *(p+1) = n;
    p[1] = n; /* identique a la ligne precedente */
    9.5 Pointeurs, tableaux et chaînes littérales
    Il est possible de pointer sur un élément particulier d'un tableau :
    int tab[5];
    int *ptr;
    ptr = &tab[2]; /* ptr pointe sur le 3eme element du tableau */
    Un nom de tableau utilisé dans une expression est converti en une adresse du début de ce tableau :
    int tab[5];
    int *ptr;
    ptr = tab; /* equivalent a : "ptr = &tab[0]" */
    Quand une chaîne littérale est utilisée dans une expression, comme pour un tableau, elle est convertie en une adresse sur son début :
    int *ptr="abcd";
    printf("%s\n", ptr);
    Attention, les chaînes littérales sont assimilées à des constantes et ne peuvent être modifiées.
    9.6 Pointeurs génériques
    Les pointeurs de type void * sont utilisés pour pointer sur quelque chose dont on ne connais pas le type. Les seuls opérateurs autorisés avec les pointeurs génériques sont :
    • l'affectation : =
    • la conversion de type
    Les autres opérateurs sont interdits. Parmi eux :
    • l'indirection : *p
    • l'addition : p+i
    • la soustraction : p-i
    9.7 Une utilisation des pointeurs : le passage par adresse
    Le passage d'arguments par adresse ne peut se faire qu'avec des pointeurs. Il permet de changer la valeur d'une variable dont l'adresse est passée en argument de la fonction :
    ________________________________________
    void incr(int *n)
    {
    *n = *n + 1;
    }

    int main()
    {
    int a = 3;
    incr(&a);
    printf("%d\n", a);
    return 0;
    }
    ________________________________________
    L'appel à la fonction incr fonctionne en plusieurs étapes :
    1. Dans le main, on fait appel à la fonction incr qui prend en argument l'adresse de la variable a (l'opérateur & retourne l'adresse d'une variable).
    2. L'adresse de a est stockée dans la variable n de la fonction.
    3. Grâce à l'opérateur *, on accède au contenu de la case pointée par n.
    4. On revient dans le main.
    Attention, nous avons vu que le nom d'un tableau utilisé seul dans une expression était converti en un pointeur sur celui-ci. Cela signifie qu'un tableau est toujours passé par adresse à une fonction. Pour reprendre la métaphore, quand une fonction prend en argument un tableau, elle travaille toujours sur l'original.
    9.8 Utilisation avancée
    int *p; /* pointeur sur un entier */
    int *t[10]; /* tableau de 10 pointeurs sur des entiers */
    int (*pt) [5]; /* pointeur sur un tableau de 5 entiers */
    int *fonc(); /* fonction renvoyant un pointeur sur un entier */
    int (*pfonc) (); /* pointeur sur une fonction retournant un entier */
    9.9 Exercices
    1. "carre.c" : créer une fonction qui remplace la valeur de l'entier passé en argument par son carré.
    2. "echange.c" : fonction qui échange les valeurs de deux entiers passés en argument.
    3. "echange2.c" : fonction qui échange les valeurs de deux pointeurs passés en argument.
    10. Passer des arguments à un programme
    Notions : argc, argv, atoi.
    Le shell permet de transmettre des arguments au lancement d'un programme. Un programme C est capable de récupérer ces arguments qui sont stockés sous forme de chaînes de caractères. Un tableau habituellement nommé argv qui doit être déclaré dans la fonction main contient des pointeurs sur ces chaînes. L'entier argc indique le nombre d'arguments passés au programme dans le tableau argv. Attention, le premier argument est toujours le nom du programme lui-même (comme en shell).
    Exemple :
    ________________________________________
    int main(int argc, char *argv[])
    {
    printf("%s\n", argv[0]); /* nom du programme lance */
    }
    ________________________________________
    10.1 Convertir les arguments récupérés
    La principale conversion est celle d'une chaîne de caractères en un nombre entier. On utilise pour cela la fonction atoi :
    ________________________________________
    char ch[]="1234";
    int n;
    n = atoi(ch); /* 'n' a maintenant pour valeur 1234 */
    ________________________________________
    10.2 Exercices
    1. "params.c" : programme qui affiche tous les arguments passés en paramètre en indiquant leur position :
    2. $ prog un pomme 1234 toto
    3. 1 un
    4. 2 pomme
    5. 3 1234
    6. 4 toto
    7. "add.c" : programme qui additionne tous les nombres passés en argument et qui affiche le résultat.
    8. "basename.c" : implémenter une commande qui enlève le chemin d'accès d'un nom de fichier (cf. la manpage de basename).
    9. "encadre.c" : programme qui encadre une phrase passée en argument de la façon suivante :
    10. $ encadre + "Bonjour a tous"
    11. ++++++++++++++++++
    12. + Bonjour a tous +
    13. ++++++++++++++++++
    11. Les fonction d'entrée/sortie
    Notions : printf, scanf, putchar, getchar.
    Le langage C ne possède aucune instruction d'entrée/sortie mais s'appuie sur une bibliothèque de fonctions standard.
    11.1 Les fonctions d'E/S
    E/S de caractères avec getchar et putchar
    getchar renvoie un int, ce qui lui permet de renvoyer un caractères, sur 8 bits, en cas de succès ou -1 en cas d'échec. putchar prend en argument un int.
    E/S de chaînes de caractères avec gets, fgets et puts
    gets est une fonction à proscrire absolument (dangers de buffer overflow). Cette fonction lit une ligne de l'entrée standard stdin et le copie dans le buffer passé en argument jusqu'à ce que '\n' ou EOF soit rencontré. fgets permet de limiter le nombre de caractères à lire et à copier dans le tableau passé en argument. puts écrit la chaîne passée en argument et rajoute \n à la fin.
    E/S formatées avec printf et scanf
    printf accepte les formats suivants :
    %s pour un argument de type chaîne de caractères
    %c pour un caractère
    %d pour un entier
    %e pour un réel flottant, notation scientifique
    %f pour un réel flottant, notation standard
    %g pour un réel flottant, notation la mieux adaptée
    %o pour une conversion en octal
    %p pour afficher un pointeur
    %x pour une conversion en hexadécimal
    Il existe des modificateurs de format :
    %hd pour un <TT/short int/
    %ld pour un <TT/long int/
    %lf pour un <TT/double/
    %.3f un flottant avec 3 chiffres après la virgule
    %20s une chaîne de 20 caractères
    %[^\n]s une chaîne de caractères avec les espaces
    Il est possible de définir une taille limite des données affichées (cf. manpage).
    scanf fonctionne comme printf mais effectue une lecture sur l'entrée standard. scanf prend en argument les adresses des variables à affecter. Elle renvoie le nombre de champs correctement lus.
    Contourner les pièges de scanf
    Attention, scanf est une fonction qui pose beaucoup de problèmes et qu'il est fortement déconseiller d'utiliser telle quelle ! Parmis ces problèmes, il y a les risques de bouclage infini : quand scanf essaye de convertir une chaine vers un nombre et que la conversion échoue, les caractères non numériques sont laissés dans le buffer. Pour fiabiliser la saisie avec scanf, voici un modèle universel :
    while(1){
    fgets(buf,sizemax,stdin);
    ret = sscanf(buf,"%c",&c);
    if(ret != 1)
    /* instructions à exécuter en cas d'erreur */
    if((strlen(buf) == sizemax-1) && (buf[sizemax-2] != '\n'))
    do c=getchar(); while (c!='\n');
    }
    13. Les fonctions de manipulation de fichiers
    Notions : open, read, write, close.
    13.1 Ouverture et fermeture d'un fichier
    Avant de pouvoir lire ou écrire dans un fichier, il faut que celui-ci soit ouvert. C'est ce que réalise la fonction open qui obéit à la syntaxe suivante :
    int open(const char *chemin, int oflag, mode_t mode);
    Cette fonction ouvre le fichier de nom chemin et retourne un descripteur de fichier qui permettra de l'identifier dans toutes les autres fonctions (lecture, écriture, déplacement, etc.). En cas d'echec, elle retourne -1.
    Le paramètre oflag précise le mode d'ouverture du fichier :
    • O_RDONLY : ouverture en lecture seule
    • O_WRONLY : ouverture en écriture seule
    • O_RDWR : ouverture en lecture et écriture
    • O_NDELAY : ouverture non bloquante
    • O_APPEND : positionnement en fin de fichier avant chaque écriture
    • O_CREAT : création du fichier si il n'existe pas
    • O_TRUNC : ouverture avec troncature si le fichier existe
    • O_EXCL : ouverture exclusive (retourne un code d'erreur si le fichier existe déja lors d'une création)
    Le paramètre mode définit les droits d'accès au fichier en cas de création (dans les autres cas, il n'est nécessaire de positionner ce paramètre).
    On referme un fichier avec la fonction close :
    int close(int fd);
    Cette fonction referme le fichier dont le descripteur est fd. En cas de réussite, elle retourne 0, sinon elle retourne -1.
    13.2 Lecture
    La lecture dans un fichier se fait par la fonction read qui obéit à la syntaxe suivante :
    ssize_t read(int fd, void *buffer, size_t n);
    Cette fonction lit n octets dans le fichier dont le descripteur est fd et les place dans un buffer. En cas de réussite, elle renvoie le nombre d'octets transferés, sinon elle retourne -1.
    Exemple :
    ________________________________________
    #include<fcntl.h>

    int main()
    {
    char c;
    int fd;

    fd = open("/etc/passwd", O_RDONLY);
    if(fd == -1){
    fprintf(stderr,"impossible d'ouvrir le fichier\n");
    exit(1);
    }

    while( read(fd, &c, 1) > 0 )
    putchar(c);

    close(fd);

    return 0;
    }
    ________________________________________
    13.3 ةcriture
    L'écriture dans un fichier se fait par la fonction write :
    ssize_t write(int fd, const void *buffer, size_t n);
    Cette fonction écrit n octets dans le fichier dont le descripteur est fd à partir d'un buffer. Cette fonction retourne le nombre d'octets écrits ou -1 en cas d'erreur.
    Exemple :
    ________________________________________
    #include<fcntl.h>

    int main()
    {
    char buf[]="hello world !\n";
    int fd;

    fd = open("foo", O_CREAT | O_RDWR, 0644);
    write(fd, buf, sizeof buf);

    return 0;
    }
    ________________________________________
    13.4 Exercices
    1. "nbline.c" : programme qui compte les lignes d'un fichier.
    2. "wc.c" : programme qui émule la commande wc.
    3. "accolade.c" : programme qui compte le nombre d'accolades fermantes et ouvrantes d'un code source en C. Il signale une erreur si les deux nombres obtenus ne sont pas identiques.
    4. "comment.c" : programme qui supprime les commentaires (/* */).
    14. Le préprocesseur
    Notions : préprocesseur, #define, #include, #if, #elif, #ifdef, #endif, #undef, ##.
    Le préprocesseur est appelé au début d'une phase de compilation pour modifier le source.
    La directive #define symbole chaine remplace un symbole par une chaîne dans le code source du programme à chaque fois qu'il apparait :
    #define MAX 256
    Le symbole peut être paramètré :
    #define max(a,b) ( (a)>(b) ? (a) : (b) )
    Il est recommandé de parenthèser les arguments de la chaîne. Expliquez pourquoi l'exemple suivant produit un résultat erroné :
    #define carre(a) (a*a)
    ...
    carre(x+1);
    La directive #include <fichier> ou #include "fichier" permet d'inclure le code d'un autre fichier dans le code source de notre programme (voir le chapitre sur la compilation séparée).
    16. Fonctions standards sous UNIX
    1. strcmp, strcat, strlen, strcpy...
    2. isalnum, isalpha, isascii, iscntrl, isdigit, islower, isprint...
    3. atoi, atol, atof, tolower, toupper
    4. pow, sqrt, rand, sin, cos, atan, log, exp...
    17. Compilation séparée
    Notions : compilation séparée, librairies, .o, options de gcc (-l, -L, -I, etc.), make.
    La compilation séparée permet de produire du code réutilisable (bibliothèques) et plus fiable. Indispensable pour les gros projets.
    17.1 Un premier exemple de compilation séparée
    Reprenont le code de la fonction longueur qui renvoie la taille d'une chaîne de caractères. Cette fonction est très utile et devrait pouvoir être réutilisable dans n'importe quel autre programme.
    Compiler un fichier objet qui contient les fonctions
    Voici le code du fichier longueur.c. Il faut le remanier afin qu'il ne contienne que le code de la fonction qui nous intéresse :
    ________________________________________
    #include<stdio.h>

    int longueur(char tab[])
    {
    int i=0;
    while(tab)
    i++;
    return i;
    }
    ________________________________________
    On compile ce fichier afin de produire un fichier "objet" qui se termine par .o :
    $ gcc -Wall -c longueur.c
    On obtient le fichier longueur.o qui contient le code compilé de la fonction longueur. La commande UNIX nm nous permet de vérifier que la fonction longueur est bien définie dans le fichier longueur.o :
    $ nm longueur.o
    00000000 t gcc2_compiled.
    00000000 T longueur
    Utiliser le fichier objet
    Attention, le fichier objet obtenu ne peut être exécuté directement. Pour ce faire, il faut créer un programme, dans un autre fichier, qui va faire appel à la fonction que nous avons définie.
    Exemple, le fichier main.c :
    ________________________________________
    #include <stdio.h>

    extern int longueur(char *);

    int main()
    {
    char chaine[]="hello";
    printf("%s fait %d caractères\n", chaine, longueur(chaine));
    return 0;
    }
    ________________________________________
    Pour pouvoir utiliser la fonction longueur, il faut déclarer son prototype au début du programme en mettant la directive extern devant. Cette directive indique au compilateur que la fonction est présente dans un autre fichier que dans celui du programme principal.
    Pour compiler le programme contenu dans main.c :
    $ gcc -Wall main.c longueur.o -o prog
    Un code plus propre
    A chaque fois que nous voulons utiliser une fonction externe, il faut déclarer son prototype. Il existe un moyen propre de faire cela : utiliser un fichier d'entête. Voici, dans le cas présent, ce fichier que nous appelerons fonc.h :
    ________________________________________
    int longueur(char *);
    ________________________________________
    On inclue le code du fichier d'entête dans le code source du programme avec la directive #include :
    #include <stdio.h>
    #include "fonc.h"
    int main()
    {
    char chaine[]="hello";
    printf("%s fait %d caractères\n", chaine, longueur(chaine));
    return 0;
    }
    ________________________________________
    17.2 Automatiser la compilation avec make
    Exemple de fichier Makefile :
    OBJ=prog
    prog : main.c longueur.o
    gcc -Wall $^ -o $@
    %.o : %.c
    gcc -Wall -c $^
    clean :
    rm -f *.o $(OBJ)
    ________________________________________
    Variables prédéfinies
    $^ : dépendances
    $@ : cible
    $< : nom de la première dépendance
    $(basename filename) : préfixe de la cible courante
    19. Annexe
    19.1 Les éditeurs de textes
    vi, emacs
    19.2 Installer une application GNU sous UNIX
    1. Décompresser l'archive à l'aide de la commande tar :
    2. $ tar xfz appli.tgz
    3. Aller dans le répertoire créé :
    4. $ cd appli
    5. Lire le fichier README et le fichier INSTALL qui accompagnent la distribution.
    6. Compiler et installer :
    7. $ ./configure
    8. $ make
    9. $ make install
    18. Débogage à l'aide de gdb
    18.1 Commandes principales
    La pose de points d'arrêt
    (b)reak [line] : place un point d'arrêt a la ligne indiquée.
    (b)reak [fonc] : place un point d'arrêt a la fonction specifiée.
    info break : indique ou sont définis les point d'arrêts.
    clear [line|fonc] : supprime un point d'arrêt.
    Execution du programme
    run < file : lance le programme avec une redirection de l'entrée standard.
    (n)ext : exécute une instruction sans rentrer dans le code des fonctions.
    (s)tep : exécute une instruction en entrant dans le code des fonctions.
    (c)ount : continue l'exécution du programme.
    jump [line] : saute a la ligne indiquée (modifie le compteur ordinal).
    (l)ist : liste le code source du programme.
    Examen des données
    (p)rint [exp] : affiche la valeur de l'expression.
    (p)rint [*tab@num] : affiche num valeurs du tableau tab.
    display [exp] : affiche la valeur de l'expression après chaque arrêt.
    undisplay [num] : supprime un display.
    set [exp=value] : modifie la valeur d'une variable.
     
    آخر تعديل: ‏28 فبراير 2008