TD — Séance 1 — Syntaxe C complète (fondamentaux)

Types et conversions, opérateurs, portée, contrôle, fonctions, tableaux et chaînes, comportement indéfini

TD de la séance 1 de Programmation C avancée : types, variables, opérateurs, contrôle, fonctions, tableaux, entrées-sorties, compilation.
Auteur

Dr. El Hadji Bassirou TOURÉ · DMI/FST/UCAD

NoteObjectif

Prévoir, sans machine, ce que font des fragments de C : valeurs et conversions, état des variables ligne par ligne, trace d’une boucle, pile d’appels, contenu d’un tableau et d’une chaîne dans un dump ; distinguer ce que la norme définit de ce qu’elle laisse indéfini. Pour chaque réponse, nommer la règle appliquée. Machine x86-64 little-endian, Linux, gcc 13, options -std=c17 -Wall -Wextra : char 1 octet (signé), short 2, int et unsigned 4, long et size_t 8. Les fragments sans main sont supposés dans main, avec <stdio.h>, <string.h> et <limits.h> inclus.

Exercice 1 : types et conversions

char note = 'B' + 3;
unsigned char stock = 240;
stock = stock + 30;
short temp = -7;
unsigned int seuil = 5;
int r = temp < seuil;
int part = 7 / 4 * 4;
double moy = (9 + 10 + 12) / 3;
double moy2 = (9 + 10 + 12) / 3.0;
int x = 5;
size_t s = sizeof(x += 10);
printf("%c %d %d %d\n", note, stock, r, part);
printf("%.2f %.2f %d %zu\n", moy, moy2, x, s);
  1. Donner la valeur de note (code ASCII de 'B' : 66) et la lettre affichée.
  2. Que vaut stock après la ligne 3 ? Le résultat est-il défini par la norme ?
  3. Ligne 6 : dans quel type la comparaison est-elle faite ? Que vaut r ? Quel avertissement gcc donne-t-il ?
  4. Lignes 7 à 9 : valeurs de part, moy et moy2, avec l’ordre des opérations.
  5. Lignes 10 et 11 : valeurs de x et de s.
  6. Écrire les deux lignes affichées.

Exercice 2 : état des variables ligne par ligne

int x = 3;
int y = 7;
int z = 0;
z = x++ * 2 + --y;
x = y > 5 || z++ > 0;
y = x & 2 == 2;
z = z >> 1 ^ 3;
x = (y += 4) - z % 4;
  1. Réécrire chacune des lignes 4 à 8 avec toutes ses parenthèses, selon les priorités.
  2. Remplir le tableau : valeurs de x, y, z après chaque ligne.
  3. Quelle ligne gcc signale-t-il avec -Wall, et pourquoi ? Quelle était sans doute l’intention ?
  4. À la ligne 5, quelle opération n’est pas exécutée ? Que vaudrait z à la fin si elle l’était ?
ligne x y z
début 3 7 0
4
5
6
7
8

Exercice 3 : portée et durée de vie

int n = 100;
int f(void)
{
    static int n = 3;
    n += 2;
    return n;
}
int g(void)
{
    int n = 50;
    n--;
    return n;
}
int h(void)
{
    n = n / 2;
    return n;
}
int main(void)
{
    int a = f();
    int b = f();
    printf("%d %d %d\n", a, b, f());
    a = g();
    b = g();
    printf("%d %d\n", a, b);
    a = h();
    b = h();
    printf("%d %d %d\n", a, b, n);
    {
        int n = 8;
        printf("%d\n", n * h());
    }
    printf("%d\n", n);
    return 0;
}
  1. Pour chacune des quatre variables nommées n (lignes 1, 4, 10 et 32), donner sa portée et sa durée de vie.
  2. Écrire les cinq lignes affichées, en justifiant chaque valeur.
  3. Dans h, de quel n s’agit-il quand h est appelée depuis le bloc des lignes 31 à 34 ?
  4. Que deviendrait la première ligne affichée si le mot static était retiré de la ligne 4 ?

Exercice 4 : boucles et switch

int total = 0;
int i;
for (i = 1; i <= 12; i += 2) {
    if (i % 5 == 0)
        continue;
    total += i;
    if (total > 15)
        break;
}
printf("i = %d, total = %d\n", i, total);
int t = 20;
int tours = 0;
do {
    t -= 7;
    tours++;
} while (t > 0);
for (int c = 0; c < 5; c++) {
    switch (c) {
    case 1:
    case 3:
        printf("i");
        break;
    case 4:
        printf("x");
    default:
        printf("o");
    }
}
  1. Tracer la boucle des lignes 3 à 9 : une rangée par tour (tableau ci-dessous, rangées en trop laissées vides). Qu’affiche la ligne 10 ?
  2. Combien de tours fait la boucle des lignes 13 à 16, et que vaut t à la fin ?
  3. Qu’affiche la boucle des lignes 17 à 28 ? Quelles lignes gcc signale-t-il avec -Wextra ?
tour i i % 5 == 0 total total > 15
1
2
3
4
5
6

Exercice 5 : passage par valeur et récursion

void doubler(int v)
{
    v = 2 * v;
}
int triple(int v)
{
    return 3 * v;
}
int somme_chiffres(int n)
{
    if (n < 10)
        return n;
    return n % 10 + somme_chiffres(n / 10);
}
int p = 8;
doubler(p);
printf("%d\n", p);
p = triple(p);
printf("%d\n", p);
printf("%d\n", somme_chiffres(4705));
  1. Qu’affichent les lignes 17 et 19 ? Expliquer la différence entre doubler et triple.
  2. Pour somme_chiffres(4705), remplir le tableau des appels, du premier au dernier : n, n % 10, valeur rendue (rangées en trop laissées vides).
  3. Combien d’appels sont en cours au plus au même moment ? Dans quel ordre les valeurs sont-elles rendues ?
  4. Que se passerait-il si le test de la ligne 11 était retiré ?
appel n n % 10 valeur rendue
1
2
3
4
5

Exercice 6 : un tableau et une chaîne dans un dump

int cartons[4] = {35, 150, 23, 290};
char commune[8] = "Nioro";
cartons[2] = cartons[3] - cartons[1];
commune[0] = commune[0] + 32;
commune[3] = '\0';
printf("%s %d %zu %zu\n", commune, cartons[2], strlen(commune), sizeof commune);

Dump de la pile relevé avant la ligne 3 (adresses abrégées : 0x00 est l’adresse de cartons). Codes ASCII utiles : N 4e, i 69, o 6f, r 72, n 6e ; une lettre minuscule a le code de la majuscule plus 32 (0x20).

Addr +0 +1 +2 +3 +4 +5 +6 +7
0x00 23 00 00 00 96 00 00 00
0x08 17 00 00 00 22 01 00 00
0x10 4e 69 6f 72 6f 00 00 00
  1. Lire les quatre éléments de cartons (adresse et valeur de chacun). Pourquoi cartons[3] occupe-t-il deux octets non nuls ?
  2. À quelle adresse commence commune ? Lire la chaîne ; que valent strlen(commune) et sizeof commune avant la ligne 3 ?
  3. Remplir le dump après la ligne 5, et entourer les octets modifiés.
  4. Qu’affiche la ligne 6 ?
Addr +0 +1 +2 +3 +4 +5 +6 +7
0x00
0x08
0x10

Exercice 7 (approfondissement) : macros, comportements, édition de liens

A. Macros. Développer à la main, puis donner la valeur affichée :

#define DOUBLE(x) x + x
#define MOITIE(x) ((x) / 2)
printf("%d %d %d\n", 3 * DOUBLE(4), MOITIE(7 + 1), DOUBLE(2) * DOUBLE(2));

B. Défini ou indéfini ? Pour chaque fragment, dire si le comportement est défini, défini par l’implémentation, non spécifié ou indéfini, et donner la valeur quand elle existe (gcc, x86-64).

/* (1) */ unsigned char u = 255;
          u++;
/* (2) */ int m = INT_MIN;
          m = -m;
/* (3) */ int r = -11 % 4;
          int q = -11 / 4;
/* (4) */ signed char s = 129;
/* (5) */ int t[3];
          t[3] = 1;
/* (6) */ int a = 2;
          a = a++ + a;
/* (7) */ int v = f() - g();   /* f et g affichent chacune un message */
/* (8) */ int b = 31;
          int k = 1 << b;
          unsigned w = 1u << b;

C. Édition de liens. Pour chaque cas, les deux fichiers sont compilés ensemble (gcc a.c b.c). Dire si la compilation, puis l’édition de liens, réussissent, et ce qu’affiche le programme le cas échéant.

cas a.c b.c
1 extern int nb_lots; et main qui affiche nb_lots int nb_lots = 64;
2 le même static int nb_lots = 64;
3 int nb_lots = 12; et le même main int nb_lots = 64;

Pour cette séance

Le cours

Les slides

À lire en ligne

À télécharger

Retour au sommet