Sommaire
- Introduction au C
- Qu’est-ce que le C ?
- Historique et importance
- Compilation et liaison
- Structure d’un programme C
- Bases du Langage
- Syntaxe de base
- Variables et types de données (int, float, char, etc.)
- Opérateurs
- Structures de contrôle (if, for, while, switch)
- Fonctions
- Pointeurs
- Déclaration et utilisation des pointeurs
- Arithmétique des pointeurs
- Pointeurs et tableaux
- Allocation dynamique de mémoire (malloc, calloc, realloc, free)
- Tableaux et Chaînes de Caractères
- Déclaration et initialisation des tableaux
- Tableaux multidimensionnels
- Manipulation des chaînes de caractères (bibliothèque string.h)
- Structures et Unions
- Déclaration et utilisation des structures
- Structures imbriquées
- Unions
- Fichiers
- Ouverture et fermeture de fichiers (fopen, fclose)
- Lecture et écriture dans les fichiers (fprintf, fscanf, fread, fwrite)
- Préprocesseur C
- Directives (include, define, ifdef, etc.)
- Macros
- Gestion de la Mémoire
- Pile (Stack) et Tas (Heap)
- Allocation statique et dynamique
- Problèmes courants (fuites de mémoire, pointeurs pendants)
- Bibliothèque Standard C
- Principales bibliothèques (stdio.h, stdlib.h, string.h, math.h)
- Compilation et Débogage
- Utilisation de GCC
- Options de compilation
- Débogage avec GDB
- Bonnes Pratiques
- Conventions de codage
- Gestion des erreurs
- Ressources et Communauté
- Documentation
- Communautés en ligne
1. Introduction au C
Qu’est-ce que le C ?
Langage de programmation structuré, impératif, de bas niveau. Conçu pour être portable et efficace. Utilisé pour systèmes d’exploitation, systèmes embarqués, développement de pilotes, etc.
Historique et importance
- Développé par Dennis Ritchie chez Bell Labs dans les années 1970.
- Fortement lié au développement du système d’exploitation Unix.
- Influence majeure sur de nombreux langages (C++, Java, C#, etc.).
- Toujours largement utilisé aujourd’hui pour sa performance et son contrôle bas niveau.
Compilation et liaison
- Préprocesseur: Traite les directives (
#include
,#define
). - Compilation: Convertit le code source (
.c
) en code assembleur. - Assembleur: Convertit le code assembleur en code objet (
.o
). - Liaison (Linking): Combine les fichiers objet et les bibliothèques pour créer l’exécutable.
// Exemple simple de compilation avec gcc
// Sauvegarder sous main.c
#include <stdio.h>
int main() {
printf("Bonjour, monde!\n");
return 0;
}
# Compiler et lier : crée l'exécutable a.out (ou main sur Windows)
gcc main.c -o main
# Exécuter
./main
Structure d’un programme C
#include <stdio.h> // Inclusion de la bibliothèque standard d'entrée/sortie
int main() { // Fonction principale, point d'entrée du programme
// Corps de la fonction main
printf("Hello, World!"); // Affichage à la console
return 0; // Indique que le programme s'est terminé sans erreur
}
2. Bases du Langage
Syntaxe de base
- Les instructions se terminent par un point-virgule (
;
). - Les commentaires commencent par
//
(ligne unique) ou sont encadrés par/* */
(bloc). - Les blocs de code sont délimités par des accolades (
{}
).
// Ceci est un commentaire sur une seule ligne
/*
Ceci est un
commentaire
sur plusieurs lignes
*/
int main() {
int a = 10; // Instruction
{ // Bloc de code
int b = 20;
}
return 0;
}
Variables et types de données (int, float, char, etc.)
- Déclaration:
type nom_variable;
- Initialisation:
type nom_variable = valeur;
Types de base :
int
: Entiersfloat
: Nombres à virgule flottante simple précisiondouble
: Nombres à virgule flottante double précisionchar
: Caractèresvoid
: Absence de type
int age = 30;
float price = 19.99;
char initial = 'A';
Opérateurs
- Arithmétiques:
+
,-
,*
,/
,%
- Relationnels:
==
,!=
,<
,>
,<=
,>=
- Logiques:
&&
(ET),||
(OU),!
(NON) - Affectation:
=
,+=
,-=
,*=
,/=
,%=
- Incrémentation/Décrémentation:
++
,--
int x = 10, y = 5;
int sum = x + y; // 15
int is_equal = (x == y); // 0 (faux)
int condition = (x > 0 && y < 10); // 1 (vrai)
x++; // x devient 11
Structures de contrôle (if, for, while, switch)
- Conditionnelles:
if
,else if
,else
,switch
- Boucles:
for
,while
,do-while
int score = 85;
if (score > 90) {
// ...
} else if (score > 80) {
// ...
} else {
// ...
}
for (int i = 0; i < 5; i++) {
// ...
}
while (score > 0) {
// ...
score--;
}
Fonctions
- Déclaration:
type_retour nom_fonction(paramètres);
- Définition:
type_retour nom_fonction(paramètres) { // corps }
- Appel:
nom_fonction(arguments);
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // Appel de fonction
return 0;
}
3. Pointeurs
Déclaration et utilisation des pointeurs
- Un pointeur est une variable qui stocke l’adresse mémoire d’une autre variable.
- Déclaration:
type *nom_pointeur;
- Opérateur d’adresse:
&
(retourne l’adresse d’une variable) - Opérateur de déréférencement:
*
(accède à la valeur pointée par le pointeur)
int var = 10;
int *ptr; // Déclaration d'un pointeur vers un entier
ptr = &var; // ptr stocke l'adresse de var
printf("Adresse de var: %p\n", &var); // Affiche l'adresse de var
printf("Valeur de ptr: %p\n", ptr); // Affiche la valeur de ptr (l'adresse de var)
printf("Valeur de var: %d\n", var); // Affiche la valeur de var (10)
printf("Valeur pointée par ptr: %d\n", *ptr); // Affiche la valeur pointée par ptr (10)
*ptr = 20; // Modifie la valeur de var via le pointeur
printf("Nouvelle valeur de var: %d\n", var); // Affiche 20
Arithmétique des pointeurs
- Possibilité d’incrémenter ou de décrémenter un pointeur pour accéder à des éléments contigus en mémoire (ex: éléments d’un tableau).
- L’incrémentation d’un pointeur de 1 le fait avancer d’une taille de
type
.
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr pointe vers le premier élément de arr
printf("Valeur de arr[0]: %d\n", *ptr); // 10
ptr++; // ptr pointe vers le prochain élément (arr[1])
printf("Valeur de arr[1]: %d\n", *ptr); // 20
Pointeurs et tableaux
- Le nom d’un tableau est équivalent à un pointeur vers son premier élément.
arr[i]
est équivalent à*(arr + i)
.
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("arr[2]: %d\n", arr[2]); // 30
printf("*(arr + 2): %d\n", *(arr + 2)); // 30
printf("ptr[2]: %d\n", ptr[2]); // 30
printf("*(ptr + 2): %d\n", *(ptr + 2)); // 30
Allocation dynamique de mémoire (malloc, calloc, realloc, free)
malloc()
: Alloue un bloc de mémoire de la taille spécifiée (en octets). Ne l’initialise pas. Retourne un pointeurvoid*
.calloc()
: Alloue un bloc de mémoire pour un nombre spécifié d’éléments d’une taille spécifiée. Initialise la mémoire à zéro. Retourne un pointeurvoid*
.realloc()
: Redimensionne un bloc de mémoire précédemment alloué.free()
: Libère un bloc de mémoire précédemment alloué avecmalloc
,calloc
ourealloc
.
#include <stdio.h>
#include <stdlib.h> // Pour malloc, calloc, realloc, free
int main() {
int *ptr;
// Allocation avec malloc
ptr = (int*)malloc(5 * sizeof(int)); // Alloue de la mémoire pour 5 entiers
if (ptr == NULL) {
printf("Erreur d'allocation mémoire\n");
return 1;
}
ptr[0] = 10;
free(ptr); // Libère la mémoire
// Allocation avec calloc
ptr = (int*)calloc(5, sizeof(int)); // Alloue et initialise à zéro
if (ptr == NULL) {
printf("Erreur d'allocation mémoire\n");
return 1;
}
free(ptr);
return 0;
}
4. Tableaux et Chaînes de Caractères
Déclaration et initialisation des tableaux
- Un tableau est une collection d’éléments du même type stockés de manière contiguë en mémoire.
- Déclaration:
type nom_tableau[taille];
- Initialisation:
type nom_tableau[taille] = {valeur1, valeur2, ...};
int numbers[5]; // Déclaration d'un tableau de 5 entiers
int values[3] = {10, 20, 30}; // Déclaration et initialisation
char message[] = "Hello"; // Déclaration et initialisation d'un tableau de caractères (chaîne)
Tableaux multidimensionnels
- Tableaux avec plusieurs dimensions (ex: matrices).
- Déclaration:
type nom_tableau[taille1][taille2]...;
int matrix[3][3]; // Déclaration d'une matrice 3x3
int grid[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
Manipulation des chaînes de caractères (bibliothèque string.h)
- En C, les chaînes de caractères sont des tableaux de
char
se terminant par un caractère nul (\0
). - La bibliothèque
string.h
fournit des fonctions pour manipuler les chaînes (ex:strcpy
,strlen
,strcat
,strcmp
).
#include <stdio.h>
#include <string.h> // Pour les fonctions de manipulation de chaînes
int main() {
char str1[20] = "Hello";
char str2[] = " World";
char dest[50];
strcpy(dest, str1); // Copie str1 dans dest
strcat(dest, str2); // Concatène str2 à la fin de dest
printf("Chaîne concaténée: %s\n", dest); // Affiche "Hello World"
printf("Longueur de la chaîne: %zu\n", strlen(dest)); // Affiche 11
if (strcmp(str1, "Hello") == 0) {
printf("str1 est égal à \"Hello\"\n");
}
return 0;
}
5. Structures et Unions
Déclaration et utilisation des structures
- Une structure est un type de données composite qui regroupe des variables de types différents sous un seul nom.
- Déclaration:
struct nom_structure { type membre1; type membre2; ... };
- Utilisation:
struct nom_structure nom_variable; nom_variable.membre1 = valeur;
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person person1;
// Initialisation
strcpy(person1.name, "John Doe");
person1.age = 30;
person1.height = 1.75;
// Affichage
printf("Nom: %s\n", person1.name);
printf("Âge: %d\n", person1.age);
printf("Taille: %.2f\n", person1.height);
return 0;
}
Structures imbriquées
- Une structure peut contenir d’autres structures comme membres.
#include <stdio.h>
#include <string.h>
struct Address {
char street[100];
char city[50];
char zip[10];
};
struct Person {
char name[50];
int age;
struct Address address; // Structure imbriquée
};
int main() {
struct Person person1;
strcpy(person1.name, "Jane Smith");
person1.age = 25;
strcpy(person1.address.street, "123 Main St");
strcpy(person1.address.city, "Anytown");
strcpy(person1.address.zip, "12345");
printf("Nom: %s\n", person1.name);
printf("Adresse: %s, %s %s\n", person1.address.street, person1.address.city, person1.address.zip);
return 0;
}
Unions
- Une union est un type de données qui peut stocker différents types de données dans la même zone de mémoire. Seul un membre peut être utilisé à la fois.
- Déclaration:
union nom_union { type membre1; type membre2; ... };
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 22.5;
printf("data.f: %.2f\n", data.f); // data.i est écrasé
strcpy(data.str, "C Programming");
printf("data.str: %s\n", data.str); // data.i et data.f sont écrasés
return 0;
}
6. Fichiers
Ouverture et fermeture de fichiers (fopen, fclose)
fopen()
: Ouvre un fichier. Retourne un pointeur vers la structureFILE
.fclose()
: Ferme un fichier.
Modes d’ouverture courants:
"r"
: Lecture seule."w"
: Écriture (écrase le contenu existant)."a"
: Ajout (écrit à la fin du fichier)."rb"
,"wb"
,"ab"
: Modes binaires correspondants.
#include <stdio.h>
int main() {
FILE *fp;
// Ouverture en écriture
fp = fopen("my_file.txt", "w");
if (fp == NULL) {
printf("Erreur lors de l'ouverture du fichier\n");
return 1;
}
// Fermeture du fichier
fclose(fp);
printf("Fichier fermé\n");
return 0;
}
Lecture et écriture dans les fichiers (fprintf, fscanf, fread, fwrite)
fprintf()
: Écrit des données formatées dans un fichier. Similaire àprintf
.fscanf()
: Lit des données formatées depuis un fichier. Similaire àscanf
.fread()
: Lit un bloc de données depuis un fichier.fwrite()
: Écrit un bloc de données dans un fichier.
#include <stdio.h>
int main() {
FILE *fp;
int num = 123;
char str[] = "Hello, file!";
// Ouverture en écriture
fp = fopen("my_file.txt", "w");
if (fp == NULL) {
printf("Erreur lors de l'ouverture du fichier\n");
return 1;
}
// Écriture formatée
fprintf(fp, "Nombre: %d\n", num);
fprintf(fp, "Chaîne: %s\n", str);
fclose(fp);
// Ouverture en lecture
fp = fopen("my_file.txt", "r");
if (fp == NULL) {
printf("Erreur lors de l'ouverture du fichier\n");
return 1;
}
int read_num;
char read_str[50];
fscanf(fp, "Nombre: %d\n", &read_num);
fscanf(fp, "Chaîne: %s\n", read_str);
fclose(fp);
printf("Lu depuis le fichier: Nombre = %d, Chaîne = %s\n", read_num, read_str);
return 0;
}
7. Préprocesseur C
Directives (include, define, ifdef, etc.)
- Le préprocesseur est une partie du compilateur qui traite le code source avant la compilation proprement dite.
- Les directives commencent par
#
. #include
: Inclut le contenu d’un fichier d’en-tête.#define
: Définit une macro.#ifdef
,#ifndef
,#endif
: Compilation conditionnelle.
#include <stdio.h> // Inclut la bibliothèque standard d'entrée/sortie
#define PI 3.14159 // Définit une macro pour PI
#ifdef DEBUG
#define DEBUG_PRINT(x) printf("DEBUG: %s = %d\n", #x, x)
#else
#define DEBUG_PRINT(x)
#endif
int main() {
printf("PI = %f\n", PI);
int value = 10;
DEBUG_PRINT(value); // Ne sera affiché que si DEBUG est défini
return 0;
}
Macros
- Substitutions de texte effectuées par le préprocesseur.
- Peuvent être utilisées pour définir des constantes, des fonctions inline, etc.
- Attention aux effets de bord et à la lisibilité.
#include <stdio.h>
#define SQUARE(x) ((x) * (x)) // Macro pour calculer le carré
int main() {
int num = 5;
int result = SQUARE(num); // result = ((num) * (num)) = 25
printf("SQUARE(%d) = %d\n", num, result);
// Attention aux effets de bord
int i = 5;
printf("SQUARE(i++) = %d\n", SQUARE(i++)); // ((i++) * (i++)) -> i est incrémenté deux fois!
printf("i = %d\n", i); // i = 7
return 0;
}
8. Gestion de la Mémoire
Pile (Stack) et Tas (Heap)
- Pile (Stack): Zone de mémoire utilisée pour stocker les variables locales, les arguments de fonction et les adresses de retour. Gérée automatiquement par le compilateur. Taille limitée.
- Tas (Heap): Zone de mémoire utilisée pour l’allocation dynamique. Gérée manuellement par le programmeur (avec
malloc
,calloc
,realloc
,free
).
Allocation statique et dynamique
- Allocation statique: Mémoire allouée au moment de la compilation (variables globales, locales). Taille fixe.
- Allocation dynamique: Mémoire allouée à l’exécution (avec
malloc
,calloc
,realloc
). Taille peut être déterminée à l’exécution.
Problèmes courants (fuites de mémoire, pointeurs pendants)
- Fuites de mémoire: Mémoire allouée dynamiquement qui n’est pas libérée avec
free
. - Pointeurs pendants: Pointeur qui pointe vers une zone de mémoire qui a déjà été libérée.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int*)malloc(sizeof(int));
// ... utiliser ptr ...
// Oublier de libérer la mémoire -> fuite de mémoire
// free(ptr);
ptr = NULL; // Pour éviter de créer un pointeur pendant
return 0;
}
9. Bibliothèque Standard C
Principales bibliothèques (stdio.h, stdlib.h, string.h, math.h)
stdio.h
: Fonctions d’entrée/sortie standard (ex:printf
,scanf
,fopen
,fclose
).stdlib.h
: Fonctions utilitaires (ex:malloc
,calloc
,free
,atoi
,exit
).string.h
: Fonctions de manipulation de chaînes de caractères (ex:strcpy
,strlen
,strcmp
).math.h
: Fonctions mathématiques (ex:sqrt
,sin
,cos
,pow
).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main() {
printf("sqrt(25) = %f\n", sqrt(25)); // math.h
char str[20] = "Hello";
printf("strlen(\"%s\") = %zu\n", str, strlen(str)); // string.h
int num = atoi("123"); // stdlib.h
printf("atoi(\"123\") = %d\n", num);
return 0;
}
9. Bibliothèque Standard C
Principales bibliothèques (stdio.h, stdlib.h, string.h, math.h)
stdio.h
: Fonctions d’entrée/sortie standard (ex:printf
,scanf
,fopen
,fclose
).stdlib.h
: Fonctions utilitaires (ex:malloc
,calloc
,free
,atoi
,exit
).string.h
: Fonctions de manipulation de chaînes de caractères (ex:strcpy
,strlen
,strcmp
).math.h
: Fonctions mathématiques (ex:sqrt
,sin
,cos
,pow
).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main() {
printf("sqrt(25) = %f\n", sqrt(25)); // math.h
char str[20] = "Hello";
printf("strlen(\"%s\") = %zu\n", str, strlen(str)); // string.h
int num = atoi("123"); // stdlib.h
printf("atoi(\"123\") = %d\n", num);
return 0;
}
8. Gestion de la Mémoire
Pile (Stack) et Tas (Heap)
- Pile (Stack): Zone de mémoire utilisée pour stocker les variables locales, les arguments de fonction et les adresses de retour. Gérée automatiquement par le compilateur. Taille limitée.
- Tas (Heap): Zone de mémoire utilisée pour l’allocation dynamique. Gérée manuellement par le programmeur (avec
malloc
,calloc
,realloc
,free
).
Allocation statique et dynamique
- Allocation statique: Mémoire allouée au moment de la compilation (variables globales, locales). Taille fixe.
- Allocation dynamique: Mémoire allouée à l’exécution (avec
malloc
,calloc
,realloc
). Taille peut être déterminée à l’exécution.
Problèmes courants (fuites de mémoire, pointeurs pendants)
- Fuites de mémoire: Mémoire allouée dynamiquement qui n’est pas libérée avec
free
. - Pointeurs pendants: Pointeur qui pointe vers une zone de mémoire qui a déjà été libérée.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int*)malloc(sizeof(int));
// ... utiliser ptr ...
// Oublier de libérer la mémoire -> fuite de mémoire
// free(ptr);
ptr = NULL; // Pour éviter de créer un pointeur pendant
return 0;
}
9. Bibliothèque Standard C
Principales bibliothèques (stdio.h, stdlib.h, string.h, math.h)
stdio.h
: Fonctions d’entrée/sortie standard (ex:printf
,scanf
,fopen
,fclose
).stdlib.h
: Fonctions utilitaires (ex:malloc
,calloc
,free
,atoi
,exit
).string.h
: Fonctions de manipulation de chaînes de caractères (ex:strcpy
,strlen
,strcmp
).math.h
: Fonctions mathématiques (ex:sqrt
,sin
,cos
,pow
).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main() {
printf("sqrt(25) = %f\n", sqrt(25)); // math.h
char str[20] = "Hello";
printf("strlen(\"%s\") = %zu\n", str, strlen(str)); // string.h
int num = atoi("123"); // stdlib.h
printf("atoi(\"123\") = %d\n", num);
return 0;
}
10. Compilation et Débogage
Utilisation de GCC
- GCC (GNU Compiler Collection) est un compilateur largement utilisé pour le C.
- Commande de base pour compiler :
gcc nom_fichier.c -o nom_executable
# Compiler un fichier C
gcc main.c -o main
Options de compilation
-Wall
: Affiche tous les avertissements.-Werror
: Traite les avertissements comme des erreurs.-g
: Ajoute des informations de débogage pour GDB.-O0
,-O1
,-O2
,-O3
: Niveaux d’optimisation (de aucun à maximal).
# Compiler avec avertissements et informations de débogage
gcc -Wall -g main.c -o main
Débogage avec GDB
- GDB (GNU Debugger) est un débogueur en ligne de commande.
- Permet d’exécuter le programme pas à pas, d’inspecter les variables, de définir des points d’arrêt, etc.
# Démarrer GDB
gdb main
# Commandes GDB courantes
# break main (point d'arrêt dans la fonction main)
# run (démarrer l'exécution)
# next (passer à la ligne suivante)
# print var (afficher la valeur de la variable var)
# continue (continuer l'exécution)
# quit (quitter GDB)
11. Bonnes Pratiques
Conventions de codage
- Utiliser des noms de variables et de fonctions clairs et descriptifs.
- Indenter correctement le code.
- Ajouter des commentaires pour expliquer le code complexe.
- Éviter les variables globales autant que possible.
- Utiliser des fonctions pour diviser le code en modules réutilisables.
Gestion des erreurs
- Vérifier les valeurs de retour des fonctions (ex:
malloc
,fopen
). - Utiliser des codes de retour pour indiquer les erreurs.
- Afficher des messages d’erreur clairs et informatifs.