Cours systèmes embarqués et DSP - Chapitre 1: Rappel du C

Ce laboratoire propose un rappel complet du langage C, essentiel pour la programmation des systèmes embarqués et des DSP. Il couvre les notions fondamentales telles que les pointeurs, les tableaux, les fonctions, les structures de données, la manipulation des bits, la portée des variables, les classes de stockage, les modificateurs, et le préprocesseur.

D'après le document Cours systèmes embarqués et DSP - Chapitre 1: Rappel du C

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source

Cours systèmes embarqués et DSP - Chapitre 1: Rappel du C

Embedded Systems and Digital Signal Processing (DSP) · PDF · 159 pages

Afficher l'aperçu du document

Consulter le document original →

Ce laboratoire propose un rappel complet du langage C, essentiel pour la programmation des systèmes embarqués et des DSP. Il couvre les notions fondamentales telles que les pointeurs, les tableaux, les fonctions, les structures de données, la manipulation des bits, la portée des variables, les classes de stockage, les modificateurs, et le préprocesseur. Ce TP permet d’acquérir les bases nécessaires pour manipuler efficacement la mémoire, structurer les données et écrire des fonctions robustes. Pour suivre ce laboratoire, il est nécessaire de disposer d’un environnement de développement C avec un compilateur standard et d’avoir des connaissances initiales en programmation.

Objectifs

  • Comprendre et utiliser les pointeurs en C.
  • Manipuler les tableaux et comprendre leur relation avec les pointeurs.
  • Écrire, déclarer et appeler des fonctions avec ou sans paramètres.
  • Définir et manipuler des structures de données (struct) et des énumérations (enum).
  • Effectuer des opérations bit à bit et manipuler les bits individuellement.
  • Comprendre la portée des variables et les classes de stockage (auto, static, register, extern).
  • Utiliser les modificateurs const et volatile.
  • Maîtriser les directives du préprocesseur, y compris les macros et l’inclusion conditionnelle.

Prérequis et installation

  • Un compilateur C conforme (gcc, clang, ou autre).
  • Un éditeur de texte ou un IDE pour écrire et compiler les programmes.
  • Connaissances de base en programmation C (variables, types, boucles, conditions).
  • Bibliothèques standard C (stdio.h, stdlib.h) disponibles.

Les pointeurs

Un pointeur est une variable contenant l’adresse mémoire d’une autre variable. Il est essentiel pour la gestion dynamique de la mémoire et la manipulation efficace des données.

Pour déclarer un pointeur, on utilise l’opérateur d’indirection * précédant le nom :

char *pc;
int *pi;
float *pr;

Le contenu pointé par un pointeur s’obtient avec l’opérateur *. Par exemple :

*pc = 34;
printf("Contenu de la case mémoire: %c\n", *pc);
printf("Valeur de l'adresse en hexadécimal: %p\n", pc);

L’opérateur unaire & permet d’obtenir l’adresse d’une variable :

p = &c;  // p pointe sur c

L’arithmétique des pointeurs permet de déplacer un pointeur dans la mémoire en fonction de la taille du type pointé :

  • pi + 1 pointe sur l’élément suivant de type int (généralement 4 octets plus loin).
  • pc + 1 pointe sur l’octet suivant (1 octet plus loin).

Plusieurs pointeurs peuvent pointer vers la même adresse mémoire.

Les tableaux

Un tableau est une collection d’éléments du même type, indexés à partir de 0 :

int T[5];  // tableau de 5 entiers
T[0], T[1], ..., T[4]

On peut parcourir un tableau avec une boucle :

for (int i = 0; i < 5; i++) {
    printf("Indice %d : Valeur %d\n", i, T[i]);
}

Un tableau peut être initialisé lors de sa déclaration :

int Tpremiers[4] = {2, 3, 5, 7};

Attention, on ne peut pas affecter un tableau à un autre avec = ni comparer deux tableaux avec ==. Pour comparer, il faut parcourir les éléments un par un.

Un tableau est en fait un pointeur vers son premier élément :

  • tableau équivaut à &tableau[0]
  • tableau[i] équivaut à *(tableau + i)

Allocation dynamique de mémoire

La mémoire peut être allouée dynamiquement avec les fonctions :

  • malloc(size_t taille) : réserve un espace mémoire non initialisé.
  • calloc(size_t nbre, size_t taille) : réserve un espace mémoire initialisé à zéro.
  • realloc(void* bloc, size_t taille) : modifie la taille d’un bloc mémoire déjà alloué.
  • free(void* ptr) : libère un espace mémoire alloué.

Exemple d’allocation avec malloc :

int *ptr;
int max;
printf("Saisir le nombre d’éléments: ");
scanf("%d", &max);
ptr = malloc(max * sizeof(int));
if (ptr == NULL)
    printf("Échec d’allocation\n");
else
    printf("Succès d’allocation\n");
free(ptr);

Les fonctions en C

Une fonction peut être déclarée (prototype), définie et appelée. Elle peut recevoir des paramètres et retourner une valeur.

Exemple d’une fonction retournant le maximum de deux entiers :

int max(int v1, int v2);

int main() {
    int a, b, valmax;
    printf("Tapez deux entiers: ");
    scanf("%d %d", &a, &b);
    valmax = max(a, b);
    printf("Maximum des deux valeurs: %d\n", valmax);
    return 0;
}

int max(int v1, int v2) {
    if (v1 > v2) return v1;
    else return v2;
}

Les paramètres sont transmis par valeur, c’est-à-dire que la fonction travaille sur une copie.

Pour modifier une variable passée en paramètre, il faut passer son adresse (pointeur) :

void incremente(int *Adr);

int main() {
    int v = 12;
    printf("Avant v = %d\n", v);
    incremente(&v);
    printf("Après v = %d\n", v);
    return 0;
}

void incremente(int *Adr) {
    *Adr = *Adr + 1;
}

Structures de données (struct)

Une structure permet de regrouper plusieurs variables sous un même type :

struct personne {
    char nom[30];
    char prenom[20];
    int age;
};

On peut déclarer des variables de type struct :

struct personne pers1, pers2;

Accès aux champs avec l’opérateur . :

pers1.age = 20;
printf("Âge de %s : %d\n", pers1.prenom, pers1.age);

Il est possible d’imbriquer des structures :

struct date {
    int jour;
    int mois;
    int an;
};

struct personne {
    char nom[30];
    char prenom[20];
    struct date dat_nais;
};

Passage d’une structure en paramètre peut se faire par valeur (copie) ou par adresse (pointeur). Pour modifier les champs, il faut passer un pointeur :

void fct(struct point *s) {
    s->absc = 0;
    s->ord = 0;
}

Les énumérations (enum)

Le type enum permet de définir des constantes entières nommées pour améliorer la lisibilité :

enum automobile {4chv = 60, 5chv = 30, 6chv}; // 6chv = 31

Exemple d’utilisation :

typedef enum {dim = 1, lun, mardi, merc, jeudi, vend, samedi} Jours;

Jours j1 = merc;
printf("Jour j1 = %d\n", j1);

Manipulation des bits

Les opérateurs bit à bit permettent de manipuler les bits individuellement :

  • & : ET bit à bit
  • | : OU bit à bit
  • ^ : OU exclusif (XOR)
  • ~ : complément à un (NOT)
  • Décalages à gauche << et à droite >>

Exemples d’exercices :

  • Complémenter A si A > B, sinon complémenter B.
  • Forcer les bits pairs de A à 1 et les bits impairs à 0.
  • Copier les bits 3, 4 et 5 de A dans les bits 2, 3 et 4 respectivement.
  • Inverser l’état du bit 7 de B.

Portée des variables et classes de stockage

La portée d’une variable définit où elle est visible :

  • Locale : variable déclarée dans un bloc {…}, visible uniquement dans ce bloc.
  • Globale : variable déclarée en dehors de toute fonction, visible dans tout le programme.

Une variable locale masque une variable globale du même nom.

Les classes de stockage modifient la durée de vie et la visibilité :

  • auto : variable locale temporaire (par défaut).
  • static : variable locale conservée entre appels de fonction ou variable globale limitée au fichier.
  • register : suggère de stocker la variable dans un registre CPU pour un accès rapide (adresse non accessible).
  • extern : déclare une variable globale définie dans un autre fichier.

Modificateurs const et volatile

const verrouille une variable en lecture seule après initialisation :

const double pi = 3.141593;

Toute tentative de modification est refusée par le compilateur.

volatile indique que la variable peut être modifiée de manière imprévisible (ex : par un périphérique), empêchant certaines optimisations du compilateur :

volatile int flag;

Le préprocesseur

Le préprocesseur traite les directives avant la compilation :

  • #include : inclusion de fichiers.
  • #define : définition de macros.
  • #undef : suppression d’une macro.
  • #if, #ifdef, #ifndef, #else, #endif : inclusion conditionnelle.
  • #warning et #error : messages d’avertissement ou d’erreur.
  • #expr : évaluation d’expressions au préprocesseur.

Exemple de macro :

#define MAX(a,b) ((a) > (b) ? (a) : (b))

Résultats attendus

  • Affichage correct des adresses et contenus via pointeurs.
  • Manipulation correcte des tableaux, avec accès aux éléments et parcours via boucle.
  • Allocation dynamique réussie avec gestion des erreurs (NULL).
  • Fonctions correctement déclarées, appelées et retournant les résultats attendus.
  • Structures initialisées, modifiées et affichées avec accès aux champs.
  • Opérations bit à bit donnant les résultats logiques attendus.
  • Variables locales et globales visibles selon leur portée, avec effets des classes de stockage.
  • Macros et directives préprocesseur fonctionnant selon les conditions définies.

Pièges courants

  • Confondre l’adresse d’une variable et son contenu lors de l’utilisation des pointeurs.
  • Oublier de libérer la mémoire allouée dynamiquement avec free().
  • Utiliser = pour affecter un tableau à un autre, ce qui est interdit en C.
  • Comparer deux tableaux avec == au lieu de comparer élément par élément.
  • Passer des paramètres par valeur alors qu’une modification est attendue (nécessite passage par adresse).
  • Ne pas initialiser les variables ou les pointeurs avant utilisation.
  • Oublier que les tableaux sont transmis implicitement par adresse aux fonctions.
  • Confusion entre portée locale et globale, notamment avec des variables du même nom.
  • Utiliser incorrectement les modificateurs const et volatile.
  • Macros mal définies pouvant provoquer des erreurs ou comportements inattendus.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions