Trouver une SSII

Accueil
Actualité
Définition
Prestations
Fiche Identité
Test d'embauche
Dépôt de CV
Formation
Adhésion
Profils des candidats
Se connecter
Créer un compte

Toutes
SI
II
SE
WEB
CGF
CAO
BTP
FOR
CRO
ENR
AUS
Rechercher
Quartz-ingénierie SWAT INFORAMTIC MEDIANE SYSTEME ALTEN BIG-AFFAIRES.COM Akka technologies AUSY
LANGAGE C EMBARQUE

SOMMAIRE SIMPLIFIE
  1. Introduction
  2. Informatique embarquée
  3. Cycle de génération d'un exécutable 
  4. Bases du langage C
  5. Opérateurs de comparaison et expressions logiques, opérateurs d'affectation
  6. Fonctions et Modules
  7. Types évolués
  8. Tableaux, chaînes de caractères et pointeurs
  9. Compléments
  10. Exercices

SOMMAIRE DETAILLE
  1. Introduction
    1. Historique du langage C
    2. Langage C: un langage de bas niveau?
    3. Avantage du langage C
    4. Inconvénient du langage C
  2. Informatique embarquée
    1. Qu'est-ce que l'informatique embarquée?
    2. Contraintes liées à l'informatique embarquée
      1. Criticité
      2. Temps de réponse
      3. Fiabilité, sécurité, robustesse
      4. Architecture matérielle
      5. Autonomie
    3. Domaines d'applications des systèmes embarqués
  3. Cycle de génération d'un exécutable
    1. Précompilation
    2. Compilation
    3. Edition de lien 
  4. Bases du langage C
    1. Types et variables
    2. Types de base
      1. Types entiers
      2. Types avec partie décimale
      3. Type vide
    3. Qualificatifs de type
      1. Volatile
      2. Const
    4. Spécificateurs de classe de stockage
      1. Auto
      2. Static
      3. Extern
      4. Register
    5. Taille des types
    6. Variables
  5. Opérateurs de comparaison et expressions logiques, opérateurs d'affectation
    1. Opérateurs d'affectation
    2. Opérateurs arithmétiques
      1. Opérateurs binaires d'affectation
      2. Opérateurs unaires d'affectation
    3. Opérateurs de traitement de bit à bit
      1. Complémentation
      2. Opérateurs logiques binaires et masquage
      3. Opérateurs de décalage
      4. Opérateurs d'affectation binaires
    4. Opérateur de taille sizeof
    5. Conversion de type
      1. Conversion implicite
      2. Conversion explicite
    6. Opérateurs de comparaison
      1. Opérateurs logiques
      2. Opérateur ternaire
    7. Priorité des opérateurs
    8. Récapitulatif des opérateurs
    9. Structures de contrôle
      1. Branchements conditionnels
      2. Boucles de contrôle
  6. Fonctions et Modules
    1. Définition de fonctions
    2. Pile d'exécution
    3. Récursivité
    4. Fonction Main
    5. Passage d'arguments à une fonction
      1. Passage de paramètres par valeur
      2. Passage de paramètres par adresse
    6. Visibilité des fonctions
  7. Types évolués
    1. Structures
      1. Définition d'une structure
      2. Déclaration d'une structure
      3. Accès aux champs d'une structure
      4. Champs de bits
    2. Les unions
    3. Redéfinition  de types
    4. Alignement des données
  8. Tableaux, chaînes de caractères et pointeurs
    1. Tableaux
    2. Chaînes de caractères
    3. Définition et utilisation des pointeurs
    4. Equivalence pointeurs - tableaux
    5. Pointeur sur fonction
      1. Déclaration d'un pointeur de fonction
      2. Initialisation de pointeur de fonction
      3. Appels de fonction par leurs pointeurs
  9. Compléments
    1. Routine d'interruptions
    2. Make
  10. Exercices
    1. Préprocesseur et #define (macro-constante)
    2. Préprocesseur et #define (macro-fonction)
    3. Taille mémoire des variables, effet de bord et débordement
    4. Déclaration de variables, de pointeurs, de tableaux et de pointeurs de fonctions
    5. Const
    6. Volatile et interruption
    7. Manipulation de bits
    8. Accès à une adresse mémoire
    9. Routine d'interruption
    10. Conversion implicite et explicite (cast)

DEBUT

1. Introduction

1.1 Historique du  langage C

Le langage C fut inilialement développé à partir de 1970 par Dennis Ritchie dans les laborateurs de Bell Telephone (aujourd'hui propriété d'AT&T). Il est l'aboutissement de deux langages:
  • BPCL développé en 1967 par Martin Richards.
  • et surtout de B développé en 1970 chez AT&T par Ken Thompson.

Le langage C est lié à la conceptiondu système d'exploitation UNIX. Son but est d'ailleurs de pouvoir réécrire UNIX dans un langage de haut niveau (de plus haut niveau que l'assembleur), afin de le rendre portable.

En 1972, la première version du langage C écrite par Dennis Ritchie et Brian Kernighan est prête et 90% d'UNIX sera écrit en C.

A partir de 1974, les laboratoires Bell ont accordé des licences UNIX aux universités et c'est ainsi que le langage C a commencé à être distribué, mais son usage demeura très limité à l'extérieur de Bell jusqu'en 1978, date à laquelle Kernighan et Ritchie publièrent les spécifications définitives du langage «The C programming Language».

A la suite de cette publication, des professionnels de l'informatique impressionnés par les nombreux avantages du langage C commencèrent à en faire une promotion active et au milieu des années 80, la popularité du langage est établie. De nombreux complilateurs C ont été écrits et de nombreuses applications commerciales ont été développées depuis lors. Mieux encore, un grand nombre de produits écrits initialement dans d'autres langages ont été transcrits en C afin de tirer profit de sa portabilité et de son efficacité.

Le développement d'un grand nombre de compilateurs C, mais comportant quelques incompatibilités portant atteinte à l'objectif de portabilité, a rendu nécessaire la création d'un nouveau standard mieux défini. Ainsi, en 1983, l'«American National Standards Institute» (ANSI) a chargé une commission de donner une définition claire du langage C, mais qui devrait conserver l'esprit de ce langage. Ce qui a abouti en 1989 à l'approbation de la norme dite ANSI-C ou C89. Kernighan et Ritchie ont légèrement adapté leur ouvrage pour la seconde édition de «The C programming Langage» pour qu'il décrive ANSI-C. Ce livre est d'ailleurs souvent considéré comme la bible des programmeurs en C. En 1999, une nouvelle évolution du langage est normalisée par l'ISO: C99.

De nombreux autres langages comme Java, JavaScript, PHP ou C# ont largement repris la syntaxe du langage C mais sans être compatible.
 

1.2 Langage C: un langage de bas niveau ?

On peut dire que e langage C est à la fois un langage de bas niveau (proche de la machine) et de haut niveau (plus proche de la pensée et du langage humain).

C'est un langage de bas niveau dans le sens où il permet l'accès à des données (bits, octets, adresses) que manipule l'ordinateur et qui ne sont pas souvent disponibles à partir de langages de haut niveau , mais supporte les structures de base nécessaires à la conception d'applications structurées.

Cette caractéristique, ainsi que la possibilité d'une organisation modulaire (un programme en C peut être composé de plusieurs fichiers) le range dans la catégorie des langages de haut niveau.

On peut parler également du langage C comme étant un «langage évolué de haut niveau», comblant le vide entre le langage machine et les langages de haut niveau conventionnels.

1.3 Avantage du langage C

Le langage C est:

  • universel: il n'est pas orienté vers un domaine d'applications spéciales (comme SQL, par exemple). De plus, il peut être adapté à de nombreuses machines, et dans le domaine qui nous intéresse, les systèmes embarqués, c'est le langage de loin le plus utilisé. Tous les contrôleurs PIC, même les «petits» sont programmables en langage C.
  • moderne: il offre de nombreuses possibilités: c'est un langage structuré, déclaratif, et récursif. Il offre des structures de contrôle et de déclaration.
  • rapide: il est compilé et non interprété. Il permet de développer des programmes concis et rapides.Déjà depuis quelques temps, les compilateurs C génèrent un code aussi performant que les programmes en assembleur les plus optimisés!
  • extensible: il peut être étendu et enrichi par des bibliothèques de fonctions achetées, récupérées (logiciel libre) ou développées spécifiquement pour un projet.
  • compact: il est basé sur un nombre de fonctions et d'opérateurs relativement limité, qui permet la formulation d'expressions simples mais efficaces.
  • portable: en respectant le C ANSI, il est «théoriquement» possible d'utiliser le même programme sur tout autre système (autre hardware, autre système d'exploitation) ayant un compilateur C, simplement en le recompilant.
  • proche de la machine: c'est un langage «bas niveau», il offre donc des opérateurs proches du langage machine (manipulation de bits, pointeurs, etc...). C'est un atout pour le développement logiciel dans le domaine de l'embarqué.

1.4 Inconvénients du langage C

  • sa compréhensibilté: le langage C autorise l'emploi d'expressions compactes rendant la lecture du code difficile. Un projet digne de ce nom interdira souvent cette pratique en définissant des règles de codage particulières. Il faudra de toute façon ne pas négliger l'abondance de commentaires dans son code, sans quoi il deviendrait inutilisable! Sans commentaires ou explications, un fichier source peut devenir incompréhensible pour son développeur après un certain temps, et donc également pour un autre développeur. Il ne faut pas oublier que dans l'optique de la mise au point, de l'évolution et de la maintenance d'un fichier source, sa lisibilité et sa facilité de compréhension sont très importantes.
  • sa portabilité:
    • un programme qui utilise des fonctions spécifiques à la machine de développement ne va pas être facilement portable;
    • le comportement exact des exécutables dépend de l'ordinateur cible.
  • son type: le langage C est un langage dit «faiblement typé». En fait, l'utilisation des termes «faiblement typé» ou «fortement typé» est si diverse qu'elle les rend presque inutiles, mais le langage C gardant cette réputation, il faudra en retenir que:
    • les conversions implicites de types ne sont pas formellement interdites;
    • les conversions entre types ne doivent pas obligatoirement être décrites.
  • le mécanisme du langage C permet de passer outre le système de typage, comme le transtypage («cast»).
  • le danger de l'utilisation de l'inscription «goto» pour la lisibilité et la maintenabilité d'un programme écrit en langage C.
2. Informatique embarquée

2.1 Qu'est-ce que l'informatique embarquée?

On désigne sous le terme «informatique embarquée» les aspects logiciels se trouvant à l'intérieur des équipements n'ayant pas une vocation purement informatique. L'ensemble logiciel et matériel intégrés dans un équipement constitue un système embarqué appelé aussi système enfoui.

La définition la plus simple consisterait à dire que «c'est l'informatique là où il n'y a pas de PC».

Les conséquences d'un problème lors de l'exécution d'un logiciel sur un PC peuvent être très différentes de celles d'un logiciel embarqué. Dans le premier cas, cela peut être le reboot d'un PC, et dans l'autre, le crash d'un avion...

2.2 Contraintes liées à l'informatique embarquée

2.2.1 Criticité

Un système critique est un système dont une panne peut avoir des conséquences dramatiques (tels des morts ou des blessés graves, des dommages matériels importants, ou des conséquences graves pour l'environnement). En effet,comme un tel système agit sur l'environnement physique,les actions qu'il effectue sont irrémédiables. Le degré de criticité est fonction des conséquences des déviations par rapport à un comportement nominal, conséquences qui peuvent concerner la sûreté des personnes et des biens, la sécurité, l'accomplissement des missions, la rentabilité économique...
Les systèmes embarqués sont souvent critiques, et les systèmes critiques sont presque toujours embarqués.

2.2.2 Temps de réponse

Les systèmes embarqués doivent être réactifs: ils doivent interagir avec leur environnement à une vitesse qui est imposée par cet environnement. Ceci induit donc des impératifs de temps de réponses. C'est pour cette raison que l'informatique embarquée est souvent basée sur un système temps réel.

2.2.3 Fiabilité, sécurité, robustesse

L'environnement d'un système embarqué est souvent hostile, pour des raisons physiques (chocs, variations de température, foudre, ...) ou humaines (malveillance). C'est pour cela que la sécurité (au sens de la résistance aux malveillances) et la fiabilité (au sens continuité de service) sont souvent rattachées à la problématique des systèmes embarqués.

2.2.4 Architecture matérielle

La taille mémoire est limitée, mais également l'ensemble hardware composant le système limite les possibilités offertes par la programmation: nombre de bus, périphériques...

2.2.5 Autonomie

Les systèmes embarqués doivent dans la plupart des cas être autonomes, c'est à dire remplir leur mission pendant de longues périodes sans intervention humaine.

2.3 Domaines d'application des systèmes embarqués

  • Transport (automobile, aéronautique);
  • Astronautique (fusée, satellite artificiel, sonde spatiale);
  • Militaire (missile);
  • Télécommunication (téléphonie, routeur, téléphone portable);
  • Electroménager (télévision, four à micro-ondes);
  • Impression (imprimante, photocopieur);
  • Informatique (disque dur);
  • Multimédia (console de jeux vidéo, assistant personnel);
  • Banque (guichet automatique);
  • Equipement médical (cœur artificiel).
3. Cycle de génération d'un exécutable

Fichier1.c          Fichier2.c          Fichiers sources (.c)
                                                  Précompilation
-------------------------------------------------------------
                                                  Compilation

Fichier1.o          Fichier2.o           Fichiers objets          Bibliothèque
-----------------------------------------------------------------------------------
                                                   Edition de liens
-----------------------------------------------------------------------------------
                                        Exécutable

3.1 Précompilation

La précompilation est effectuée par un programme particulier appelé préprocesseur. Le préprocesseur du langage C permet d'effectuer des manipulations sur le texte du programme source avant sa compilation. Il permet des substitutions de texte, l'inclusion de fichiers, la compilation conditionnelle et les macros avec arguments.


Le préprocesseur traite le fichier source et ses actions principales sont:

  • le retrait des commentaires;
  • le traitement des directives de compilation: ce sont les lignes qui commencent par # (par exemple: #define, #include, #pragma,#if, #else, #elif, #endif, #ifdef).

Un usage réfléchi et bien organisé du préprocesseur est fondamental dans la réalisation d'un projet. En effet, le préprocesseur facilite le développement et la maintenance d'un programme et son adaptation à tout changement de contexte matériel et logiciel.


Exemple: cas du #define:

#define permet la définition d'une macro-constante. Chaque apparition de son identificateur dans le fichier source sera remplacé par sa valeur.

#define sert également à la définition d'une macro-fonction. Chaque apparition de son identificateur dans le fichier source sera remplacée par la portion de code correspondante.

3.2 Compilation

La phase de compilation est évidemment le travail du compilateur. C'est un programme qui traduit ce qu'a généré le préprocesseur en une suite d'instructions machine appelée «code objet». Un fichier objet est généré pour chaque fichier source. Les fichiers objets sont généralement d'extension «.o» ou «.obj».

Par abus de langage, on appelle compilation toute la phase de génération d'un fichier exécutable à partir des fichiers sources. Mais c'est seulement une des étapes menant à la création d'un exécutable.

3.3 Edition de liens

L'édition de liens est la dernière étape et a pour but de réunir tous les éléments d'un programme. Les différents fichiers objets sont alors réunis avec les bibliothèques de fonctions, pour ne produire qu'un fichier exécutable.

L'éditeur de liens va remplacer les appels de fonctions par leur code réel: le but de l'édition de liens est de sélectionner les éléments de code utiles présents dans un ensemble de codes compilés et de bibliothèques, et de résoudre les références mutuelles entre ces différents éléments afin de permettre à ceux-ci de se référencer directement à l'exécution du programme.

Les étapes de précompilation, de compilation et d'édition de liens sont généralement automatiquement enchaînées. Les fichiers intermédiaires issus de la précompilation et de la compilation sont automatiquement détruits. Il est cependant possible d'utiliser des options pour les effectuer séparément afin d'obtenir ces fichiers intermédiaires.

4. Bases du langage C

4.1 Types et variables

Ce chapitre traite des définitions de variables. Une définition de variable a les rôles suivants:

  • définir le domaine de valeur de cette variable (taille en mémoire et représentation machine);
  • définir les opérations possibles sur cette variable;
  • définir le domaine de visibilité de cette variable;
  • permettre à l'environnement d'exécution du programme d'associer le nom de la variable à une adresse mémoire;
  • initialiser la variable avec une valeur compatible avec le domaine de valeur.

4.2 Type de base

Un programme C manipule deux types de données de base:

  • les nombres entiers;
  • les nombres flottants.
Un autre type de donnée élémentaire est le pointeur (qui est en fait une adresse de variable).

Les types de base sont les types prédéfinis du compilateur. Ils sont au nombre de six: int, char, float, double, long double et void.

Le type booléen n'existe pas en C. Le langage amène plutôt à évaluer la présence (ou l'absence) d'une donnée, ou à définir un type similaire (voir «Redéfinition de types»).

4.2.1 Types entiers

int: c'est le type entier. Ce type se décline avec ce que l'on appelle des «spécificateurs de type» pour préciser sa taille (long ou short) et le fait qu'il soit uniquement positif (unsigned) ou positif et négatif (signed).

char: ce type est très proche de l'octet. Il représente un entier sur 8 bits. Comme le type int, il peut être spécifié de manière à être signé ou non. Normalement, il est le support des caractères au sens commun du terme. Dans le domaine de l'embarqué, on l'utilise souvent «unsigned» de façon à ce qu'une variable de ce type soit représentative d'un octet ou d'un registre.

short et long sont des spécificateurs de type qui peuvent être utilisés seuls ou avec «int», donnant la possibilité d'avoir des définitions du type «short int» ou «long int». Ces définitions peuvent aussi s'écrire de manière abrégée: short ou long.

signed et unsigned sont d'autres spécificateurs de type.

signed est appliqué par défaut: il n'y a pas de différence entre une variable de type int et une variable de type signed int. Une variable de type entier signé pourra contenir des valeurs positives ou négatives. Par exemple, une variable de type «char» peut évoluer entre -128 et +127.

Les types entiers peuvent aussi être spécifiés avec unsigned qui force les variables de ce type à être considérées comme uniquement positives. Par exemple, la valeur d'une variable de type unsigned char peut évoluer entre 0 et 255.

Bien sûr, on peut déclarer un entier unsigned long ou signed short, etc...

4.2.2 Types avec partie décimale

float: ce type sert pour les calculs avec des parties décimales.

double: c'est un type qui permet de représenter  des valeurs avec une plus grande précision que le type float.

long double: ce type permet de représenter des nombres avec parties décimales qui nécessitent une très grande précision.

La représentation d'un nombre réel est normalisée: norme IEEE754/854. Les nombres à virgule flottante sont des valeurs approchées. En particulier, les opérations sur ces nombres peuvent conduire à des erreurs d'arrondis.

4.2.3 Type vide

void: c'est le type «vide». Il est utilisé pour préciser les fonctions sans paramètre ou sans retour. Il joue un rôle très particulier dans l'utilisation des pointeurs.

4.3 Qualificatifs de type

Lors d'une déclaration de variable, les qualificateurs de type peuvent s'ajouter à n'importe quel type ou spécificateur de type.

4.3.1 Volatile

volatile est un attribut appliqué à une variable lors de sa déclaration. Il indique au compilateur que le contenu de la variable est susceptible de changer à n'importe quel moment, de manière asynchrone, et force ainsi le compilateur:

  • à ne pas réaliser d'optimisation indésirable sur tout le code qui touche de près ou de loin aux variables déclarées avec cet attribut;
  • à recharger la variable à chaque fois qu'elle est utilisée plutôt que d'utiliser une copie de celle-ci dans un registre.

Une variable doit être déclarée volatile quand son contenu est susceptible de changer de façon aléatoire par rapport au déroulement général du programme. En pratique, il y a seulement trois types de variables qui entrent dans ce cadre:

  • les registres des interfaces de periphériques;
  • les variables globales modifiées par une routine d'interruption;
  • les variables globales dans une application multitâche. Si elles ne sont pas déclarées volatile, les variables de ce type génèrent un programme non fonctionnel: le compilateur interprète mal les actions à réaliser, voire supprime du code nécessaire.

4.3.2 Const

Une définition de variable qualifiée de «const» informe le compilateur que cette variable est considérée comme constante et ne doit pas être utilisée dans la partie gauche d'une affectation. Ce type de définition autorise le compilateur à placer la variable dans une zone de mémoire accessible en lecture seule.

En d'autres termes, const permet de limiter l'accès à une variable, et son contenu ne peut être modifié: on parle de lecture seule («read only»).

L'utilisation de const permet d'apporter des informations utiles à quelqu'un qui lit du code qui n'est pas le sien. En effet, déclarer un paramètre «const» donne une information sur l'usage que l'on veut faire de ce paramètre.

L'utilisation de «const» permet aussi de générer du code plus compact en donnant au compilateur cette information supplémentaire. 

Enfin, du code écrit avec «const» permet d'être protégé par le compilateur de modifications faites «par inadvertance» à un paramètre qui n'était pas conçu pour cela.

En bref, utiliser «const» là où c'est utile, réduit les éventualités de bugs.

4.4 Spécificateurs de classe de stockage

Appelés aussi «classes d'allocation», les spécificateurs de classe de stockage permettent de préciser  dans quelle catégorie d'espace mémoire une variable est utilisée (mémoire «permanente», pile, registre). Ils permettent également d'associer aux variables des règles de visibilité (variable globale ou locale).

4.4.1 Auto

Dans une fonction, une variable «automatique» est allouée dynamiquement dès l'appel de cette fonction, puis libérée au retour de cette fonction. La durée de vie d'une variable automatique est donc celle de la fonction dans laquelle elle est définie. Son contenu est donc définitivement perdu entre deux appels consécutifs à cette fonction. Toute variable, à l'intérieur d'une fonction, est de classe automatique par défaut.

L'espace mémoire réservé pour les variables automatiques est alloué dans la pile d'exécution.

4.4.2 Static

Ce spécificateur de classe modifie la visibilité de la variable.

Dans une fonction, (donc pour une variable locale), une variable «static» a un nom local mais une durée de vie égale à celle du programme en cours d'exécution. C'est à dire que la variable conserve sa valeur entre deux appels à la fonction dans laquelle elle est déclarée static.

A l'extérieur d'une fonction (donc pour une variable globale), une variable «static» ne peut pas être utilisée par un autre fichier que celui dans lequel elle est déclarée static.

4.4.3 Extern

Ce spécificateur de classe permet de préciser que la ligne correspondante n'est pas une tentative de définition, mais juste une déclaration. C'est à dire qu'il précise au compilateur que la variable «extern» est définie dans un autre fichier.

  • une variable initialisée sans l'attribut «extern» fait l'objet d'une définition;
  • une variable avec l'attribut «extern» sans initialisation fait l'objet d'une simple déclaration.

4.4.4 Register

Si une variable locale à une fonction doit être utilisée intensément, le programmeur a la possibilité à l'aide de spécificateur de classe, de placer cette variable dans un registre programmable de la machine.

Le compilateur essaie de satisfaire au mieux  les demandes d'allocation dans les registres dans la mesure où il en reste de disponibles. Si le nombre de variables à stocker est supérieur à cette limite, le compilateur continue d'allouer les variables suivantes en mode automatique, c'est à dire dans la pile.

Le choix judicieux de l'utilisation des registres peut réduire de manière appréciable l'espace et surtout le temps d'exécution d'un programme.

4.5 Taille des types

Le but d'utiliser des types «affinés» (avec unsigned ou short par exemple) permet:

  • d'améliorer la précision;
  • de réduire l'encombrement mémoire.
Il n'y a pas de raison, surtout dans le domaine embarqué, de ne pas choisir un type de variable le plus adapté possible à la plage de valeurs et à l'utilisation de cette variable. Par exemple, une variable utilisée pour incrémenter les itérations d'une boucle de 0 à 100: un type unsigned char sera adapté, et surtout pas un type long!

L'espace mémoire qu'occupent les différents types dépend de la cible (ou de la machine) sur laquelle est implanté le compilateur. Le choix est laissé aux concepteurs des compilateurs. Les seules contraintes sont des inégalités non strictes, à savoir:
  • taille d'un short ≤ taille d'un int ≤ taille d'un long;
  • taille d'un float ≤ taille d'un double ≤ taille d'un long double.
Afin de connaître la taille d'un type pour une cible donnée, on peut utiliser l'instruction sizeof. Par exemple, pour connaître la taille d'un int:

printf("int = %d octetsn", sizeof(int));

Exemple de longueurs des types machines:

Types PDP 11 Intel 486 Sparc Pentium Alpha
Années 1970 1989 1993 1993 1994
Taille registres 16 bits 32 bits 32 bits 32 bits 64 bits
char 8 bits 8 bits 8 bits 8 bits 8 bits
short 16 bits 16 bits 16 bits 16 bits 16 bits
int 16 bits 16 bits 32 bits 32 bits 32 bits
long 32 bits 32 bits 32 bits 32 bits 64 bits
float 32 bits 32 bits 32 bits 32 bits 32 bits
double 64 bits 64 bits 64 bits 64 bits 64 bits
long double 64 bits 64 bits 64 bits 64 bits 128 bits
void 0 bit 0 bit 0 bit 0 bit 0 bit

Comme d'une machine à l'autre, la taille de certains types peut être différente. Dans un projet digne de ce nom, on utilisera des types redéfinis de façon à savoir directement avec le nom d'un type quelle est la gamme de valeurs qu'ils peuvent manipuler.

Par exemple, deux types très utilisés dans le domaine de l'embarqué:

  • UByte_t: ce type devra manipuler des données entières uniquement positives sur un octet;
  • UWord_t: ce type devra manipuler des données entières uniquement positives sur un mot de 16 bits.

On pourra aussi introduire les types suivants:

  • Int_16_t: ce type devra manipuler des données entières sur un mot de 16 bits;
  • UInt_32_t: ce type devra manipuler des données uniquement positives sur un mot de 32 bits;

4.6 Variable

Les objets de base que manipule le C sont les variables et les constantes. On a déjà vu qu'une variable pouvait avoir une taille différente selon son type. Une variable est définie par trois «attributs»:

  • son nom (ou identificateur);
  • son type (avec spécificateur de type et qualificatif optionnel);
  • sa classe d'allocation.
La formalisation de l'écriture d'une variable répond à la syntaxe suivante:
  • classe type nom;
  • ou type nom si la classe est implicite;
  • ou classe nom si le type est implicite;
Exemple: static const unsigned long toto;

5. Opérateurs de comparaison et expressions logiques, opérateurs d'affectation

Un opérateur de façon de façon générale est un élément syntaxique rassemblant un ou plusieurs constantes-variables-opérateurs.

5.1 L'opérateur d'affectation

x = 5;

Comme l'affection range une valeur dans une variable (une zone mémoire), il est impératif que le membre gauche d'une affectation représente une zone mémoire «left value». Une constante n'est pas une «left value»  car il ne désigne pas l'adresse d'une zone mémoire. Elle ne peut donc pas figurer en membre gauche d'une affectation. Le membre droit d'une affectation peut désigner soit une constante, soit une zone mémoire, soit une expression.

5.2 Les opérateurs arithmétiques

Les opérateurs + - * fonctionnent comme dans l'arithmétique.

Il faut noter que l'opérateur / ne fonctionne pas de la même façon suivant qu'on emploie des entiers ou des réels (float, double). Pour les entiers, le quotient s'obtient en utilisant la division entière (il faut se souvenir de l'école primaire).

L'opérateur % calcule le reste de la division et il n'est défini que pour les valeurs entières. On rappelle que:

a = q * b + r où 0 < r < b

Exemples:

int a, b, c;
float x, y, z;

a = b / c;         // division entière
a = x / y;         // reste entier
x = y / z;         // division réelle
x = 2 / 3;         // division entière
x = 2.0 / 3.0;   // division réelle
x = (float) ((float) a) / y;   // division réelle

5.2.1 Opérateurs binaires d'affectation

Ces opérateurs s'écrivent: +=, -=, *=, /=, %=, <<=,&= ... et représentent la combinaison d'un opérateur d'affectation et d'un opérateur arithmétique ou de traitement de bits.

Ainsi le compilateur retranscrit:

  • (x += a;) en (x = x + a;)
  • (x <<= a;) en (x = x << a;)
Il en est de même pour les autres opérateurs binaires d'affectation.

5.2.2 Opérateurs unaires d'affectation

++
--

Ces deux opérateurs modifient leur opérande. Le type de l'opérande ne peut être donc qu'un entier, floatant ou pointeur.

De plus, en prenant en compte les spécificités de l'embarqué, la pré-incrémentation et la pré-décrémentation (++i ou --i) sont régulièrement interdites par les règles de codage pour accroitre la lisibilité et la réutilisation du code.

5.3 Opérateurs de traitements de bits à bits

Ces opérateurs permettent la manipulation individuelle de bits à bits à l'intérieur d'un mot mémoire. On peut les répartir en trois groupes: l'opérateur de complémentation, les opérateurs logiques binaires et les opérateurs de décalage.

5.3.1 Complémentation

L'opérateur de complémentation à (~) qui inverse les bits de son opérande, remplace les 1 par des 0 et réciproquement. Cet opérateur précède toujours son opérande. Celui-ci doit être de type entier.

Exemple:

Soit la valeur hexadécimale 0x07ff. En supposant un mot de 16 bits, cette valeur s'écrit en binaire: 0000 0111 1111 1111. Le complément à 1 de cette valeur sera en binaire: 1111 1000 0000 0000, ce qui correspond au nombre hexadécimal 0xf800. On peut donc tout simplement dire que la valeur de ~0x07ff est 0xf800.

©2011 - 2021 - Trouver-une-ssii.com - Mentions légales - Conditions générales - CGV - Tarif - Souscription - Nous contacter