LdB Seance 1

De Ensiwiki
Aller à : navigation, rechercher
AttentionCette page est maintenue uniquement par les enseignants. Afin de ne pas perturber le déroulement des cours, elle n'a pas vocation à être modifiée par les élèves. Mais si vous avez des modifications à proposer, merci d'en discuter ou d'envoyer un e-mail aux auteurs de la page (cf. historique)

Laptop.png  Première Année  CDROM.png  Informatique 

Introduction

Le but de cette première séance est de se familiariser avec la syntaxe du langage C. L'apprentissage complet du C aura lieu lors du Projet C de fin d'année : dans le cadre du cours de Logiciel de Base, on apprendra juste la syntaxe et les conventions d'utilisation de la bibliothèque C.

On peut récupérer toutes les sources utilisées dans cette séance dans cette archive ZIP.

On compile les programmes C avec la commande suivante :

gcc -o BIN -Wall -Wextra -m32 -g -std=c99 SOURCES

SOURCES est le nom du (ou des) fichier(s) C à compiler et BIN le nom qu'on souhaite donner au programme binaire généré.

Les options utilisées sont les suivantes :

  • -o BIN précise le nom du fichier destination (le binaire à produire) ;
  • -Wall -Wextra affiche tous les avertissements possibles, pour aider à mettre au point le programme ;
  • -m32 produit du code 32 bits : cette option est très importante car la plupart des compilateurs produisent maintenant du code 64 bits par défaut, et on ne peut pas mélanger de l'assembleur 32 bits avec du C compilé en 64 bits par exemple ;
  • -g produit les informations de mise au point qui sont indispensables pour utiliser gdb ;
  • -std=c99 demande au compilateur de compiler du C99, qui est le dialecte du langage C que l'on apprend ici.

En C et en assembleur, on utilise indifféremment les 2 types de commentaires suivants :

  • // bla bla bla : toute la ligne est un commentaire (comme le -- de Ada) ;
  • /* bla bla bla */ : tout le texte entre /* et */ est un commentaire : ce texte peut contenir plusieurs lignes.

Premier programme (vide.c)

On commence par un programme qui ne fait rien :

int main(void)
{
    return 0;
}

Points à noter :

  • la fonction main est toujours le programme principal ;
  • elle renvoie un entier signé (int) et ne prend pas de paramètre (void) ;
  • les accolades délimitent les blocs, comme les begin et end en Ada ;
  • par convention, un programme qui s'exécute sans erreur renvoie 0 au système (return 0).

Entrées-sorties en C (es.c)

On va maintenant apprendre à lire des entrées au clavier et afficher des sorties à l'écran, grâce au programme suivant :

#include <stdio.h>

int main(void)
{
    printf("Entrer un entier signe : ");
    int entier;
    scanf("%i", &entier);

    printf("Entrer un entier naturel : ");
    unsigned naturel;
    scanf("%u", &naturel);

    printf("Vous avez entre %i et %u\n", entier, naturel);

    return 0;
}

Points à noter :

  • la directive #include <stdio.h> indique au compilateur qu'on va utiliser des fonctions (ici d'entrée-sorties) pré-définies dans la bibliothèque C standard ;
  • on peut déclarer des variables n'importe-où dans le code ;
  • on déclare une variable de type entier signé (int entier) et une autre de type entier non-signé (unsigned naturel) ;
  • la fonction printf sert à faire des affichages à l'écran ;
  • elle prend en paramètre un nombre variable d'arguments : ce nombre dépend des directives de formatage (%i, %u, ...) intégrées à la chaîne en premier paramètre ;
  • les directives de formatage sont remplacées par les paramètres qui leur correspondent, dans l'ordre ;
  • le caractère de contrôle \n sert à afficher un retour à la ligne ;
  • la fonction scanf sert à lire des valeurs au clavier et fonctionne selon le même principe que printf ;
  • l'opérateur & est obligatoire lorsqu'on utilise scanf, son rôle sera détaillé lors de la séance 2.

On liste ici les quelques directives de formatage les plus utilisées, et le type de la donnée correspondante :

  • %i : int (entier relatif)
  • %u : unsigned (entier naturel)
  • %g : float (réel)
  • %c : char (caractère)
  • %s : une chaîne de caractères

Fonctions (fonctions.c)

Dans le code suivant, on défini une fonction pgcd simple :

unsigned pgcd(unsigned a, unsigned b)
{
    while (a != b) {
        if (a < b) {
            b = b - a;
        } else {
            a = a - b;
        }
    }
    return a;
}

Points à noter :

  • la fonction pgcd prend en paramètre deux entiers naturels a et b et renvoie aussi un naturel ;
  • la condition de continuation de la boucle while est « tant que a est différent de b » (attention : différent s'écrit != et pas /= qui signifie autre chose en C) ;
  • l'affectation s'écrit = en C, et non pas := comme en Ada.

Structures de contrôle (controle.c)

Le programme suivant donne deux versions d'une fonction calculant la somme des N premiers entiers, d'abord récursivement, puis itérativement :

unsigned somme_rec(unsigned n)
{
    if (n == 0) {
        return 0;
    } else {
        return n + somme_rec(n - 1);
    }
}

unsigned somme_iter(unsigned n)
{
    unsigned r = 0;
    for (unsigned i = 0; i <= n; i++) {
        r += i;
    }
    return r;
}

Points à noter :

  • en C, l'égalité s'écrit == dans une comparaison : si on ne met qu'un seul signe égal, le compilateur pensera qu'on fait une affectation et pas une comparaison ;
  • le for est une façon d'écrire une boucle de façon condensée mais exactement équivalente à un while ;
  • on peut déclarer des variables locales à la boucle ;
  • i++ est un raccourci pour i = i + 1 ;
  • r += i signifie r = r + i (et donc a /= b veut dire a = a / b).

Structures de données (structures.c)

En C, on peut définir des structures de données d'une façon très similaire à ce qu'on faisait en Ada avec le mot-clé record :

struct complexe_t {
    float re;
    float im;
};

Points à noter :

  • par convention on ajoute toujours un _t à la fin des noms de type pour les différencier des noms de variables ;
  • le nom du type est struct complexe_t et pas juste complexe_t
  • on accède à un champ d'une structure grâce au point, comme en Ada.

Utilisation de tableaux (tableaux.c)

Le programme principal ci-dessous manipule un tableau d'entiers :

int main(void)
{
    // On initialise le generateur de nombres aleatoires
    srand(time(NULL));

    unsigned taille;
    printf("Entrer la taille du tableau : ");
    scanf("%u", &taille);

    int tab[taille];
...
    return 0;
}

Point à noter :

  • on peut déclarer un tableau dont la taille est une variable.

Ensuite, on peut utiliser ce tableau en le passant en paramètre de fonctions :

void affiche_tab(int tab[], unsigned taille)
{
    for (unsigned i = 0; i < taille; i++) {
        printf("%i ", tab[i]);
    }
    printf("\n");
}

Points à noter :

  • le paramètre int tab[] correspond à un tableau a une dimension d'entiers signés ;
  • on passe explicitement la taille du tableau en paramètre car il n'existe pas d'équivalent au Tab'Range ou Tab'Length de Ada en C ;
  • les tableaux en C commencent toujours à l'indice 0 et s'arrêtent donc à taille - 1 ;
  • on accède aux éléments d'un tableau en écrivant tab[i], et pas en utilisant les parenthèses comme en Ada.

Manipulation de chaînes de caractères (chaines.c)

Dans ce dernier exemple, on va manipuler des chaînes de caractères en utilisant notamment des fonctions utiles de la bibliothèque C :

int main(void)
{
    char chaine[5];
    strcpy(chaine, "toto");

    // Erreur !
    // strcpy(chaine, "chaine trop longue");

    unsigned taille = strlen(chaine);

    for (unsigned i = 0; i < taille; i++) {
        printf("%c", chaine[i]);
    }
    printf("\n");

    if (chaine[taille] == '\0') {
        printf("OK !\n");
    }

    strcpy(chaine, "ti");
    strcat(chaine, "ti");

    if (strcmp(chaine, "titi") == 0) {
        printf("OK !\n");
    }

    if (strcmp(chaine, "toto") != 0) {
        printf("OK !\n");
    }

    return 0;
}

Points à noter :

  • les chaînes de caractères sont représentées tout simplement par des tableaux de caractères, terminés par un caractères spécial noté \0 et dont le code ASCII est zéro ;
  • strcpy(dst, src) sert à copier le contenu de la chaîne src dans la chaîne dst ;
  • strlen renvoie la taille de la chaîne, sans compter le '\0' final ;
  • strcat(dst, src) recopie la chaine src à la fin de la chaîne dst ;
  • strcmp compare les deux chaînes passées en paramètre, et renvoie 0 si elles sont égales, et une valeur différente de 0 sinon.