Aller au contenu principal

C

Langage de programmation structuré, mais de bas niveau, permettant de manipuler dynamiquement les espaces mémoire avec des pointeurs, offrant des types primitifs, et très performant grâce à une implémentation de ses compilateurs près du matériel.

Historique​

En 1969, dans les laboratoires Bell, Ken Thompson entreprend le développement d'un système d'exploitation qui deviendra le très célèbre UNIX.

Écrit en assembleur, le système d'exploitation devient difficile à maintenir et à faire évoluer. Thompson connaît le langage BCPL, de Martin Richards, utilisé pour écrire des outils système, mais celui-ci est trop lourd, alors il en tire une version réduite, le langage B. Ce dernier, sans types et raisonnant en mots machine, ne convient pas pour la réécriture du système UNIX. En 1971, Dennis Ritchie entreprend de faire évoluer le langage B pour créer le langage C.

Dennis Ritchie et Ken Thompson

AT&T, société mère des laboratoires Bell, oeuvre dans le domaine des systèmes téléphoniques et, à l'époque, un accord antitrust lui interdit de commercialiser autre chose. Ainsi, en 1975, le système d'exploitation UNIX complet, incluant son code source réécrit en langage C, est distribué dans les universités à un coût nominal.

C'est ainsi que le langage C devient l'un des plus utilisés dans le domaine des technologies de l'information.

Source​

Le code source d'un programme en langage C est contenu dans des fichiers texte ayant, habituellement, .c comme extension.

Squelette​

// Inclusions.

// Déclarations.

int main() {
// Code source de la fonction principale.
return 0; // Code de retour du programme.
}

Compilation​

Le programme cl.exe est un compilateur pour les langages C et C++ sur le système d'exploitation Windows :

cl /c main.c

L'argument /c (compile) permet de générer le code assembleur, pour ensuite être traduit en langage machine.

Liaison​

Le programme link.exe est un lieur pour le système d'exploitation Windows :

link main.obj

Commentaires​

Tout ce qui suit les caractères // sur une ligne est ignoré lors de la compilation :

// Commentaire sur une ligne.

Tout ce qui est encadré des balises /* et */ est aussi ignoré lors de la compilation :

/* Commentaire
entre
balises. */

Blocs​

Un bloc de code est initié par une accolade ouvrante et terminé par une accolade fermante :

{ // Début du bloc de code.
// Contenu du bloc de code.
} // Fin du bloc de code.

Types​

Le langage C offre quelques types primitifs :

TypeLangage CTailleLittéral
Entierchar1 octet127 ou '~'
short2 octets32767
int4 octets2147483647
long8 octets9223372036854775807
Réelfloat4 octets3.141592f
double8 octets3.14159265358979

Il est possible de préciser, pour les types entiers, que les valeurs ne peuvent être que positives ou nulles, permettant de représenter de plus grandes valeurs :

Langage CTailleLittéral
unsigned char1 octet255
unsigned short2 octets65535
unsigned int4 octets4294967295U
unsigned long8 octets18446744073709551615U

Il est à noter que sur les plateformes Windows, ce sont les types long long et unsigned long long qui ont une taille de 8 octets.

Définitions de type​

L'identificateur de certains types n'est parfois pas assez révélateur au niveau du type de donnée et de sa taille. Il est possible de créer des alias de ces types :

typedef char byte;

Certains types sont définis par la biblothèque standard du langage tel size_t qui représente la de n'importe quel objet selon l'architecture utilisée.

Variables​

Une variable représente un espace, en mémoire centrale, permettant de stocker et de consulter une donnée selon un type.

Identificateur​

Plutôt que d'utiliser les adresses de la mémoire centrale pour accéder aux données, il est possible de nommer les variables à l'aide d'un identificateur. Toutefois, certaines contraintes doivent être respectées lors de la composition des identificateurs :

  • Doivent débuter par une lettre non accentuée.
  • Ne doivent contenir que des lettres non accentuées, des chiffres et des caractères _.
  • Doivent être différents des mots-clés du langage C.

Déclaration​

La déclaration d'une variable commence par son type, suivi d'un identificateur, et se termine par le caractère ;. La variable peut aussi être initialisée par une valeur. Il est possible de déclarer plusieurs variables de même type en les séparant par le caractère , :

Déclaration de variable

Tableau​

Pour déclarer un tableau, il suffit d'ajouter, entre crochets après l'identificateur, le nombre d'éléments désirés :

short entiers[5];

Le nombre d'éléments doit être constant et les indices de tableaux débutent toujours à 0.

Un tableau ne peut être initialisé qu'au moment de sa déclaration :

short entiers[] = { 7, 42, 69, 404, 666 };

Le nombre d'éléments ne peut être omis que si une liste d'initialisation est fournie; il est alors déduit du nombre de valeurs. Si la taille est précisée et que la liste est incomplète, les éléments restants seront initialisés à 0.

Préprocesseur​

Les directives du préprocesseur sont toutes précédées du caractère # et sont traitées avant la compilation.

Définitions​

Agit comme un « rechercher-remplacer » :

#define TPS 0.05
#define TVQ 0.09975

Chaque occurrence de TPS sera remplacée par 0.05 et celles de TVQ par 0.09975 avant la compilation.

Inclusions​

Agit comme un « copier-coller » :

#include <fichier.h>

Le code de fichier.h sera copié et collé à l'endroit du #include.

Comme exemple, pour utiliser des variables booléennes en langage C, il faut définir le type bool, et définir les constantes false et true. Ces définitions ont plutôt été placées dans un fichier d'en-tête stdbool.h, permettant de seulement l'inclure dans des projets.

Opérateurs​

Le langage C offre les opérateurs suivants :

PrioritéOpérateurDescriptionAssociativité
1()Appel de fonctionDroite
[]IndexationDroite
.Membre de structureDroite
->Membre via pointeurDroite
--Décrémentation postfixéeDroite
++Incrémentation postfixéeDroite
2(type)TranstypageGauche
sizeofTaille en octetsGauche
*IndirectionGauche
&Adresse deGauche
~Négation bit à bitGauche
!Négation logiqueGauche
-Négation arithmétiqueGauche
--Décrémentation préfixéeGauche
++Incrémentation préfixéeGauche
3*MultiplicationDroite
/DivisionDroite
%ModuloDroite
4+AdditionDroite
-SoustractionDroite
5<<Décalage de bits à gaucheDroite
>>Décalage de bits à droiteDroite
6<Plus petitDroite
<=Plus petit ou égalDroite
>Plus grandDroite
>=Plus grand ou égalDroite
7==ÉgalitéDroite
!=InégalitéDroite
8&Et bit à bitDroite
9^Ou exclusif bit à bitDroite
10|Ou bit à bitDroite
11&&Et logiqueDroite
12||Ou logiqueDroite
13?:Opérateur conditionnelGauche
14=AffectationGauche
+=Addition et affectationGauche
-=Soustraction et affectationGauche
*=Multiplication et affectationGauche
/=Division et affectationGauche
%=Modulo et affectationGauche
<<=Décalage à gauche et affectationGauche
>>=Décalage à droite et affectationGauche
&=Et bit à bit et affectationGauche
^=Ou exclusif bit à bit et affectationGauche
|=Ou bit à bit et affectationGauche
15,SéquenceDroite

Les deux derniers opérateurs de priorité 1 et les opérateurs de priorité 2 sont unaires.

Structures conditionnelles​

Le langage C offre deux principales structures conditionnelles : l'une selon une condition, l'autre selon une valeur.

Si​

Le mot-clé if suivi d'une condition, entre parenthèses, permet d'introduire cette structure. Les instructions exécutées si cette condition est vraie sont placées entre accolades :

if (note >= 60) {
printf("Reussite\n");
}

Le mot-clé else permet d'introduire une branche pour exécuter des instructions, placées entre accolades, seulement si la condition est fausse :

if (note >= 60) {
printf("Reussite\n");
}
else {
printf("Echec\n");
}

Dans le cas où une branche ne contient qu'une seule instruction, les accolades sont facultatives :

Diagramme syntaxique du si-sinon

Cas​

Le mot-clé switch suivi d'une valeur entière, entre parenthèses, permet d'introduire cette structure. Les cas, introduits par le mot-clé case suivi d'une valeur constante entière unique et du caractère :, sont placés entre accolades. Un cas par défaut peut être introduit par le mot-clé default suivi du caractère :.

switch (position) {
case 1:
printf("Medaille d'or\n");
break;
case 2:
printf("Medaille d'argent\n");
break;
case 3:
printf("Medaille de bronze\n");
break;
default:
printf("Aucune medaille\n");
break;
}

L'exécution des instructions continue, même dans les cas suivants, jusqu'à la rencontre de l'instruction break ou jusqu'à la fin de la structure :

Diagramme syntaxique de cas

Structures de répétition​

Les boucles permettent d'exécuter un bloc d'instructions de façon répétée.

Tant que​

Cette boucle itère tant que sa condition est vraie :

int compteur = 6;
while (--compteur)
printf("%i\n", compteur);

Le mot-clé while, suivi d'une condition entre parenthèses, introduit cette structure. Les instructions exécutées à chaque itération se trouvent entre accolades. Les accolades sont facultatives s'il n'y a qu'une instruction :

Diagramme syntaxique du tant que

Pour​

Cette boucle permet d'alléger la syntaxe et de déclarer les itérateurs localement :

for (long compteur = 5; compteur; --compteur)
printf("%i\n", compteur);

La syntaxe de cette structure comporte trois expressions, séparées par le caractère ; :

  • Initialisation : exécutée qu'une seule fois, avant la boucle.
  • Condition : évaluée avant chaque itération de la boucle.
  • Post-itération : exécutée à la fin de chaque itération de la boucle.

Fonctions​

Les fonctions permettent de subdiviser subdiviser un programme et éviter la répétition de code puisqu'elles sont réutilisables :

void fonction() {
// Instructions exécutées par la fonction
}

La définition d'une fonction commence par son type de retour, suivi d'un identificateur puis de la liste de paramètres entre parenthèses. Le corps de la fonction se trouve dans un bloc de code, entre accolades :

Diagramme syntaxique d&#39;une fonction

Le mot-clé return permet de retourner une valeur et l'exécution de la fonction s'arrête dès cette instruction :

int min(int nbr1, int nbr2) {
if (nbr1 < nbr2)
return nbr1;
return nbr2;
}

Principale​

La fonction principale main peut recevoir des arguments lors de son appel :

#include <stdio.h>

/// @brief Fonction principale.
/// @param argc Nombre d'arguments.
/// @param argv Arguments.
/// @return Code de retour.
int main(int argc, char* argv[]) {
for (int i = 0; i < argc; ++i)
printf("%s\n", argv[i]);

return 0;
}

Bibliothèque standard

Le langage C vient avec une bibliothèque standard proposant des fonctionnalités courantes.

Chaînes de caractères​

Une chaîne de caractères n’est en réalité qu'une suite, un tableau, de caractères se terminant par la valeur \0 :

char chaine[26] = "Langages";
printf("%s\n", chaine);

Uniquement à la déclaration, les guillemets initialisent chaque case du tableau avec le caractère correspondant et ajoutent automatiquement le caractère \0 final.

Pour utiliser les fonctions de chaînes de caractères, le fichier d'en-tête doit être inclus :

#include <string.h>

Concaténer​

La fonction strcat permet de concaténer une chaîne de caractères à une autre :

strcat(chaine, " de programmation");
printf("%s\n", chaine);

La chaîne de destination doit être suffisamment grande, incluant le caractère \0.

Taille​

La fonction strlen retourne le nombre de caractères de la chaîne en argument :

printf("Taille : %z\n", strlen(chaine));

Copier​

La fonction strcpy permet de copier une chaîne de caractères :

strcpy(chaine, "Programmation III");
printf("%s\n", chaine);

La chaîne de destination doit être suffisamment grande, incluant le caractère \0.

Comparer​

La fonction strcmp permet de comparer les caractères de deux chaînes en arguments :

if (strcmp(chaine, "Reseau II"))
printf("Chaines differentes\n");
else
printf("Chaines identiques\n");

Le retour peut être l'un des trois suivants :

RetourDescription
< 0Par ordre alphabétique, la première chaîne vient avant la deuxième.
== 0Les chaînes sont identiques.
> 0Par ordre alphabétique, la première chaîne vient après la deuxième.

Affichage​

Pour utiliser la fonction d'affichage, le fichier d'en-tête des entrées et sorties standards doit être inclus :

#include <stdio.h>

La fonction printf permet d'afficher une chaîne de caractères formatée dans le terminal :

printf("420-C35-JO : Langages de programmation\n");

Caractères spéciaux​

Des séquences de caractères spéciaux peuvent être utilisées dans le format :

SéquenceDescription
\nSaut de ligne
\tTabulation
\\Caractère \
\"Caractère "
......

Balises de données​

Une balise de donnée, dans le format, sera remplacée par une valeur de ce type :

BaliseDescription
%iEntier signé
%uEntier non signé
%zType taille
%XEntier non signé sous forme hexadécimal
%pAdresse mémoire
%fRéel
%eRéel en notation scientifique
%cCaractère
%sChaîne de caractères
%%Caractère %
......

Les valeurs qui remplaceront les balises de données doivent être spécifiées, dans l'ordre, à la suite du format :

short entier = 404;
float reel = 3.14159;
printf("La variable entier vaut %i et reel vaut %f\n", entier, reel);

Gabarit​

Il est possible de préciser le nombre minimum de caractères à afficher :

int entier = 123456;
printf("L'entier %10i est precede de 4 espaces.\n", entier);
printf("L'entier %010i est precede de 4 zeros.\n", entier);

Précision​

Pour les réels, il est aussi possible de préciser le nombre de décimales :

float reel = 123.456789;
printf("Le reel %.2f est arrondi a 2 decimales.\n", reel);

Six décimales sont affichées par défaut pour les balises %f et %e.

Fichiers​

La structure FILE permet de représenter un fichier :

FILE* fichier;

Ouverture​

La fonction fopen permet d'ouvrir un fichier :

fichier = fopen("NomFichier.ext", mode);

Le mode d'ouverture peut être l'un des suivants :

ModeDescription
"r"Lecture (le fichier doit être existant)
"rb"Lecture binaire (le fichier doit être existant)
"w"Écriture (le fichier est créé ou écrasé si existant)
"wb"Écriture binaire (le fichier est créé ou écrasé si existant)
"a"Ajout (le fichier est ouvert à la fin ou créé si inexistant)
"ab"Ajout binaire (le fichier est ouvert à la fin ou créé si inexistant)

Validation​

Pour valider si le fichier a été ouvert :

if (fichier)
// L'ouverture du fichier a réussi
else
// L'ouverture du fichier a échoué

Déplacement​

La fonction fseek permet de se déplacer dans un fichier :

fseek(fichier, deplacement, origine);

Le déplacement est exprimé en nombre d'octets.

L'origine peut être l'une des suivantes :

OrigineDescription
SEEK_SETDébut du fichier
SEEK_CURPosition actuelle
SEEK_ENDFin du fichier

Position​

La fonction ftell permet d'obtenir la position actuelle :

long position = ftell(fichier);

Lecture​

La fonction fread permet de lire dans un fichier :

fread(destination, taille, compte, fichier);

Le compte correspond au nombre de données à lire et leur taille est exprimée en octets.

La lecture se fait à partir de la position actuelle dans le fichier, et cette position est modifiée selon le nombre et la taille des données lues.

Fermeture​

La fonction fclose permet de fermer un fichier :

fclose(fichier);