Langage C - TP 1: Premiers Pas

1. Préparation

Avant de commencer à programmer, il est nécessaire de bien configurer son environnement.

Exercice 1.1.

Le but de cet exercice est de créer un environnement de travail et de pouvoir y retourner lors des travaux pratiques. Pour cela :

  1. Ouvrir un terminal
  2. Aller à la racine de votre espace en tapant la commande cd ~
  3. Créer un répertoire c_language en tapant la commande mkdir c_language
  4. Se placer dans répertoire c_language en tapant la commande cd c_language
  5. Créer un répertoire tp1 en tapant la commande mkdir tp1
  6. Se placer dans répertoire tp1 en tapant la commande cd tp1

IMPORTANT : Vous pouvez maintenant accéder au répertoire de travail tp1 en tapant la commande cd ~/c_language/tp1.

Exercice 1.2.

Le but de cet exercice est de créer votre premier fichier C, de le compiler et d’exécuter le programme créé. Pour cela :

  1. Se rendre dans le répertoire du tp1 en utilisant la commande cd ~/c_language/tp1
  2. Créer un fichier tp1-ex12.c en utilisant la commande touch tp1-ex12.c
  3. Editer le fichier tp1-ex12.c en utilisant la commande gedit tp1-ex12.c
  4. A partir de l’éditeur de texte, copier le contenu suivant dans le fichier : 
    #include <stdio.h>
    
    int main()
    {
        printf("Hello World");
        return 0;
    }
    
  5. Fermer l’éditeur de texte et revenir dans le terminal
  6. Compiler le programme en utilisant la commande gcc tp1-ex12.c -o tp1-ex12
  7. Vérifier que la compilation a bien réussi en listant les fichiers du répertoire courant en tapant la commande ls
  8. La commande ls doit afficher 2 fichiers : tp1-ex12.c et tp1-ex12 qui est un programme (exécutable)
  9. Exécuter le programme tp1-ex12 en tapant la commande ./tp1-ex12
  10. La console doit afficher Hello World.

IMPORTANT : Pour créer un fichier vide dans le répertoire courant, vous pouvez utiliser la commande touch <fichier>, où <fichier> est le nom de fichier à créer.

A RETENIR : Pour compiler un programme C nommé prog.c, il est de convention d’utiliser la commande : gcc prog.c -o prog

#include <stdio.h>

int main()
{
    printf("Hello World");
    return 0;
}

2. Premiers programmes

Cette section illustre la programmation en langage C avec des programmes simples.

Exercice 2.1.

Afin de produire un exécutable, un programme doit avoir un point d’entrée qui spécifie au système par où commencer l’exécution. En langage C, le point d’entrée d’un programme est défini par la fonction main. Celle-ci peut s’écrire de 2 façons :

void main(void){

}

ou encore:

int main(void){

  return 0 ;
}

La seconde écriture indique que la fonction main retourne un nombre à la fin de son exécution et que ce nombre est 0. Cette écriture est la plus standard et permet de communiquer avec le système (par exemple des codes erreurs). Par convention, si un programme s’est déroulé sans erreur, c’est la valeur 0 qui est retournée.

  1. Dans le répertoire ~/c_language/tp1 créer un fichier tp1-ex21.c et le remplir avec le code de la seconde écriture de la fonction main (celle retournant un int). 
  2. Compiler et exécuter le programme. Que se passe-t-il ?
  3. Juste après l’exécution, taper la commande echo $? dans le même terminal. Que s’affiche-t-il ?
  4. Modifier le programme tp1-ex21.c pour qu’il retourne 5 et plus 0.
  5. Compiler et exécuter le programme. Que se passe-t-il ?
  6. Juste après l’exécution, taper la commande echo $? dans le même terminal. Que s’affiche-t-il ?

A RETENIR : Un programme peut communiquer avec le système qui l’exécute via la fonction main. Sous linux, la variable système ? (accessible via $?) permet de récupérer le retour du dernier programme exécuté. Dans le cadre ou la fonction main retourne un int, la dernière instruction de la fonction est forcément un return.

2. Il ne se passe rien

3. La valeur 0 est affichée sur la console

4. 

int main(void){

  return 5 ;
}

5. Il ne se passe toujours rien apparemment

6. La commande affiche 5

Exercice 2.2.

Ecrire un programme tp1-ex22.c qui :

  1. Déclare une variable c de type char et l’initialise à 65
  2. Déclare une variable i de type int et l’initialise à 2
  3. Déclare une variable f de type float et l’initialise à 3.0
  4. Déclare une variable d de type double et l’initialise à 4.0
  5. Affiche la valeur des variables sous la forme : c=W, i=X, f=Y, d=ZW, X, Y et Z sont respectivement les valeurs des variables c, i, f et d
  6. Incrémente chacune des variables c, i, f et d de la valeur 4
  7. Affiche la valeur des variables sous la forme : c=W, i=X, f=Y, d=ZW, X, Y et Z sont respectivement les valeurs des variables c, i, f et d

A RETENIR : La fonction printf permet de mettre en forme les valeurs de variables à afficher au moyen des instructions précédées de %. Plus d’informations ici : https://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output

#include <stdio.h>

int main(void)
{
    char c = 65;
    
    int i = 2;
    
    float f = 3.0;
	
	double d = 4.0;

    printf("c=%c, i=%d, f=%f, d=%f\n", c, i, f, d);
    
    c += 4;
    i += 4;
    f += 4;
    d += 4;

    printf("c=%c, i=%d, f=%f, %d\n", c, i, f, d);
    
    return 0;
}

Exercice 2.3. 

Ecrire un programme tp1-ex23.c qui affiche les valeurs des expressions suivantes l’une à la suite de l’autre :

  1. 4*18-198%10
  2. (7+3)<=10
  3. (8+4)>3 && 3>2
  4. (37+18%4)/4
  5. ((3>7)||(3<=7))&&(7!=10)
  6. 8==9

#include <stdio.h>

int main(void)
{
    printf("%d\n", 4*18-198%10);
	printf("%d\n", (7+3)<=10);
    printf("%d\n", (8+4)>3 && 3>2);
    printf("%d\n", (37+18%4)/4);
    printf("%d\n", ((3>7)||(3<=7))&&(7!=10));
	printf("%d\n", 8==9);
    
    return 0;
}

Exercice 2.4.

Ecrire un programme tp1-ex24.c qui déclare les variables x, y, z, u, v et w de type float, les initialise avec respectivement les valeurs 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 et affiche le résultat des calculs suivants les uns à la suite des autres :

  1. $\frac{x\ +\ 2}{y\ +\ 4}$
  2. $\frac{xy}{v\ +\ 2}$
  3. $x(y+z(3-u))$
  4. $ux^{3}\ +\ vx^{2}\ +\ wx$

#include <stdio.h>

int main(void)
{
	float x=1.0, y=2.0, z=3.0, u=4.0, v=5.0, w=6.0;
	
	
    printf("%f\n", (x+2)/(y+4));
	printf("%f\n", x*y/(v+2));
	printf("%f\n", x*(y+z*(3-u)));
	printf("%f\n", u*x*x*x+v*x*x+x*x);
    
    return 0;
}

Exercice 2.5

Soit a et b deux variables entières pouvant prendre les valeurs 0 ou 1. Ecrire un programme tp1-ex25.c qui évalue l’expression « a et non b ou non a et b » et qui affiche ses résultats pour toutes les valeurs possibles de la paire (a, b). Que peut-on en déduire ?

#include <stdio.h>

int main(void)
{
    int a = 0, b = 0;
    
    printf("(%d, %d) -> %d\n", a, b, a && !b || !a &&b);
    
    a = 0;
    b = 1;
    printf("(%d, %d) -> %d\n", a, b, a && !b || !a &&b);
    
    a = 1;
    b = 0;
    printf("(%d, %d) -> %d\n", a, b, a && !b || !a &&b);
    
    a = 1;
    b = 1;
    printf("(%d, %d) -> %d\n", a, b, a && !b || !a &&b);
    
    return 0;
}

Le programme affiche:

(0, 0) -> 0
(0, 1) -> 1
(1, 0) -> 1
(1, 1) -> 0

On remarque qu'il s'agit des valeurs de vérité de a ou b. La formule a et non b ou non a et b est dont équivalente à a ou b.

3. Entrées / Sorties (I/O)

Cette section illustre les opérations d'entrée / sorties (i/o) de base en langage C.

Exercice 3.1.

Ecrire un programme tp1-ex31.c qui contient le programme suivant :

int main() {
    char c = 'A';
    int i = 66;
    float f = 67.6;
    
    printf("char avec %%c : %c\n", c);
    printf("char avec %%d : %d\n", c);
    printf("char avec %%f : %f\n", c);
    printf("char avec %%lf: %lf\n", c);
    
    printf("\n");
    printf("int avec %%c : %c\n", i);
    printf("int avec %%d : %d\n", i);
    printf("int avec %%f : %f\n", i);
    printf("int avec %%lf: %lf\n", i);

    printf("\n");
    printf("float avec %%c : %c\n", f);
    printf("float avec %%d : %d\n", f);
    printf("float avec %%f : %f\n", f);
    printf("float avec %%lf: %lf\n", f);

    return 0;
}

Son exécution est-elle conforme à vos attentes ? Que s’est-il passé lors de la compilation ?

Le programme montre que les modes d'affichages des valeurs doivent être choisis judicieusement. Les différents modes (%) ne font qu'interpréter une valeur mémoire avant de l'afficher, ce qui peut amener des incohérences.

Exercice 3.2.

La fonction scanf, permet de saisir la valeur d’une variable au clavier en spécifiant le format à lire. Ecrire le programme tp1-ex32.c contenant le code suivant :

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int i;
    float f;

    printf("Entrer un entier: ");
    scanf("%d", &i);
    printf("i = %d\n", i);
    
    printf("Entrer un reel: ");
    scanf("%f", &f);
    printf("f = %f\n", f);
    
    printf("Entrer un caractere: ");
    scanf("%c", &c1);
    printf("c1 = %c\n", c1);
    
    printf("Entrer un autre caractere: ");
    scanf("%c", &c2);
    printf("c2 = %c\n", c2);
    
    return 0;
}

L’exécution se passe-t-elle correctement ? Comment expliquer le comportement observé ?

La fonction scanf arrête de lire au clavier lorsqu’elle lit dans le buffer des entrées le caractère \n. Cependant, lorsque l’on demande à scanf de lire un caractère (avec %c), si le buffer des entrées contient déjà un \n (par exemple suite à un autre scanf exécuté précédemment), alors le scanf actuel considère le \n comme le caractère lu et s’arrête. Pour remédier à ce problème, il est possible d’utiliser la fonction getchar qui retire le prochain caractère du buffer d’entrée. Placée après un scanf, cette fonction s’assure que le \n laissé par le scanf dans le buffer d’entrée soit bien retiré avant l’utilisation d’un autre scanf.

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int i;
    float f;

    printf("Entrer un entier: ");
    scanf("%d", &i);
    printf("i = %d\n", i);
    
    printf("Entrer un reel: ");
    scanf("%f", &f);
    printf("f = %f\n", f);
    
    printf("Entrer un caractere: ");
	getchar();
    scanf("%c", &c1);
    printf("c1 = %c\n", c1);
    
    printf("Entrer un autre caractere: ");
	getchar();
    scanf("%c", &c2);
    printf("c2 = %c\n", c2);
    
    return 0;
}

Exercice 3.3.

Ecrire un programme tp1-ex33.c qui saisit en une seule instruction 1 entier suivi d'un réel et de deux caractères, le tout séparés par des espaces. Le programme affichera ensuite les valeurs saisies.

Rappel: Tout comme la fonction printf peut afficher plusieurs variables en une seule instruction, la fonction scanf peut saisir plusieurs valeurs de variables dans la même instruction. Pour cela la syntaxe est : scanf("%a %b … %z", &a, &b, … &z) ou les %a, …, %z sont les formateurs adaptés aux variables a, …, z. La saisie de fait alors en tapant les valeurs au clavier séparées par un espace et en tapant entrée après la dernière valeur.

#include <stdio.h>

int main(void)
{
    char c1, c2;
    int i;
    float f;

    printf("Entrer un entier, un float et deux caracteres le tout separes par des espaces: ");
    scanf("%d %f %c %c", &i, &f, &c1, &c2);
	
    printf("i: %d f: %f c1: %c c2: %c\n", i, f, c1, c2);
    
    return 0;
}

4. Programmes

Exercice 4.1.

On choisit de coder une date par un entier composé de la façon suivante : $a\ \times{}\ 10000\ +\ m\ \times{}\ 100\ +\ j$, où $a$ est une année représentée sur 4 chiffres, $m$ est un mois (de 1 à 12) et $j$ est un jour (de 1 à 31). Dans ce codage, la date du 14 juillet 1789 sera par exemple représentée par $17890714$.

Ecrire le programme tp1-ex41.c qui saisit une date sous forme d’entier au clavier et qui affiche ensuite son année, son mois et son jour. 

AIDE : penser à / et %

#include <stdio.h>

int main(void)
{
    int date;
    
    int annee;
    char mois;
    char jour;
    
    printf("Saisir une date (AAAMMJJ): ");
    scanf("%d", &date);
    
    jour = date % 100;
    
    mois = (date / 100) % 100;
    
    annee = date / 10000;
    
    printf("Annee: %d, mois: %d, jour: %d", annee, mois, jour);
    
    return 0;
}

Exercice 4.2.

Ecrire un programme tp1-ex42.c qui saisit le rayon d’un cercle r (nombre réel) et affiche :

  • Son périmètre ($2\pi{}r$)
  • Son aire ($\pi{}r^{2}$)

Penser à déclarer la constante PI de valeur $3.141592653589793$.

Exercice 4.3.

Ecrire un programme tp1-ex43.c qui saisit les coordonnées (x, y) de deux vecteurs A et B dans un espace à 2 dimensions et qui calcule et affiche le produit scalaire des 2 vecteurs. Vous pouvez chercher la formule du produit scalaire sur internet.

#include <stdio.h>

int main(void)
{
    float xa, ya;
    float xb, yb;

    printf("Entrer les coordonnees d'un vecteur 2D A sous la forme: x y\n");
    scanf("%f %f", &xa, &ya);
    
    printf("Entrer les coordonnees d'un second vecteur 2D  B sous la forme: x y\n");
    scanf("%f %f", &xb, &yb);
	
    printf("Le produit scalaire de A et B est: %f", xa*xb + ya*yb);
    
    return 0;
}

Exercice 4.4.

Ecrire un programme tp1-ex44.c qui permet d’évaluer l’expression suivante et qui affiche la valeur des variables x, y et z avant et après l’expression.

++x || (++y > z && (y*++z))

Compiler let exécuter le programme pour les valeurs de variables suivantes :

  • x = 1, y = 1 et z = 1
  • x = -1, y = 1 et z = 3
  • x = -1, y = 1 et z = 0

L’exécution est-elle conforme à l’expression ? Expliquer ce qui se passe en détaillant l’évaluation de l’expression.

#include <stdio.h>

int main(void)
{
    int x, y, z;

    x = 1;
	y = 1;
	z = 1;
    printf("1. AVANT - x: %d, y: %d, y: %d\n", x, y, z);
	++x || (++y > z && (y*++z));
	printf("1. APRES - x: %d, y: %d, y: %d\n\n", x, y, z);
    
	x = -1;
	y = 1;
	z = 3;
    printf("2. AVANT - x: %d, y: %d, y: %d\n", x, y, z);
	++x || (++y > z && (y*++z));
	printf("2. APRES - x: %d, y: %d, y: %d\n\n", x, y, z);
	
	x = -1;
	y = 1;
	z = 0;
    printf("3. AVANT - x: %d, y: %d, y: %d\n", x, y, z);
	++x || (++y > z && (y*++z));
	printf("3. APRES - x: %d, y: %d, y: %d\n\n", x, y, z);
	
    return 0;
}

En fonction des valeurs de variables, seulement certaines parties de l'expression ++x || (++y > z && (y*++z)) sont calculées. Il se peut donc que certaines affectations ne soient pas réalisées.

Dans le cas 1, x valant 1, ++x vaudra 2. Le || n'a pas besoin de calculer le reste de l'expression pour connaitre sa valeur finale (qui sera forcément 1). La partie de l'expression est donc ignorée et les ++y et ++z ne sont pas exécutés. Il en est de même pour les autres cas.

Exercice 4.5.

Ecrire un programme tp1-ex45.c qui saisit un nombre entier et affiche 1 si le nombre est impair et 0 s’il est pair (interdiction d’utiliser de structure conditionnelle).

#include <stdio.h>

int main(void)
{
    int x;
	
	printf("entrer un nombre: \n");
	scanf("%d", &x);
	
	printf("Parite: %d", x % 2);
	
    return 0;
}