💻 Langage C - S5
Année: 2022-2023 (Semestre 5)
Crédits: 3 ECTS
Type: Programmation Système
PART A: PRÉSENTATION GÉNÉRALE
Objectifs du cours
Le cours de Langage C fournit une formation complète à la programmation système en C, langage fondamental pour le développement de systèmes embarqués, systèmes d'exploitation et applications nécessitant des performances optimales. Le cours couvre les concepts fondamentaux (pointeurs, tableaux, structures) ainsi que les aspects avancés (allocation dynamique, manipulation de bits, structures de données complexes).
Compétences visées
- Maîtriser la syntaxe C et les types de données
- Comprendre les pointeurs et l'arithmétique des pointeurs
- Gérer la mémoire dynamique (malloc, free)
- Manipuler les structures de données (listes chaînées, arbres)
- Réaliser des opérations bit à bit pour le bas niveau
- Créer des programmes modulaires avec fichiers multiples
- Déboguer et optimiser du code C
Organisation
- Volume horaire: 48h (CM: 24h, TD/TP: 24h)
- Évaluation: Examens écrits (60%) + TPs et projets (40%)
- Semestre: 5 (2022-2023)
- Prérequis: Algorithmique de base, notions de programmation
PART B: EXPÉRIENCE, CONTEXTE ET FONCTION
Contenu pédagogique
1. Fondamentaux du C
Types de données et opérateurs:
Le C propose des types primitifs de tailles fixes:
char: 1 octet (8 bits)int: 4 octets sur systèmes 32/64 bitsfloat: 4 octets (simple précision)double: 8 octets (double précision)
Modificateurs: signed, unsigned, short, long
Exemple de déclarations:
int nombre = 42;
unsigned int positif = 100;
char lettre = 'A';
float pi = 3.14f;
Opérateurs bit à bit:
Essentiels pour la manipulation bas niveau:
&(AND),|(OR),^(XOR),~(NOT)<<(décalage gauche),>>(décalage droite)
Application TD1: Compter les bits à 1 dans un entier:
int bitcount(int n) {
int count = 0;
while (n) {
count += n & 1; // Teste le bit de poids faible
n >>= 1; // Décale d'un bit à droite
}
return count;
}
Pour 0xF0000F00, résultat: 8 bits à 1.
Structures de contrôle:
Boucles et conditionnelles standard:
// Tri à bulles (TD1)
for (int j = 0; j < TAILLE-1; j++) {
for (int i = 0; i < TAILLE-j-1; i++) {
if (tab[i+1] < tab[i]) {
int temp = tab[i];
tab[i] = tab[i+1];
tab[i+1] = temp;
}
}
}
2. Pointeurs et Gestion Mémoire
Les pointeurs:
Concept fondamental du C: variable contenant une adresse mémoire.
Déclaration et utilisation:
int x = 10;
int *ptr = &x; // ptr pointe vers x
*ptr = 20; // Modifie x via le pointeur
Arithmétique des pointeurs:
int tab[5] = {1, 2, 3, 4, 5};
int *p = tab; // Pointe vers tab[0]
p++; // Pointe vers tab[1]
*p = 100; // tab[1] = 100
Permutation par pointeurs (TD2):
int Permute(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
return 0;
}
Allocation dynamique:
Fonctions essentielles:
malloc(size): alloue size octetscalloc(n, size): alloue et initialise à 0free(ptr): libère la mémoire
Exemple TD2:
char* chaine = calloc(10, sizeof(char));
int* tab = calloc(10, sizeof(int));
// Utilisation...
free(chaine);
free(tab);
Risques:
- Fuite mémoire si oubli de
free - Double free (libérer deux fois)
- Accès après free (use-after-free)
- Dépassement de buffer
3. Structures et Types Composés
Définition de structures (TD3):
struct Etudiant {
char nom[50];
int naissance;
int notes[10];
int nb_notes;
};
Utilisation:
struct Etudiant cedric;
strcpy(cedric.nom, "Chanfreau");
cedric.naissance = 2000;
cedric.notes[0] = 15;
Pointeurs vers structures:
struct Etudiant *ptr = &cedric;
ptr->naissance = 2001; // Équivalent à (*ptr).naissance
Figure : Concept des pointeurs - Un pointeur stocke l'adresse mémoire d'une variable
Copie de structures:
Attention aux pointeurs dans les structures:
struct S1 {
int a;
char* ch; // Pointeur
};
struct S1 v1, v2;
v1.ch = calloc(10, sizeof(char));
strcpy(v1.ch, "Test");
v2 = v1; // Copie superficielle: v2.ch pointe vers la même zone!
Solution: copie profonde manuelle ou allocation séparée.
4. Structures de Données Dynamiques
Listes chaînées (TP principal):
Structure d'un élément:
struct element {
struct Coureur* coureur;
struct element* suiv;
};
struct liste {
struct element* premier;
struct element* dernier;
struct element* courant;
};
Initialisation:
int init_liste(struct liste* liste) {
liste->premier = NULL;
liste->dernier = NULL;
return 0;
}
Ajout en queue (file):
int ajout_file(struct liste* der, struct Coureur* coureur) {
if (der->dernier == NULL) {
// Liste vide
der->dernier = malloc(sizeof(struct element));
der->dernier->coureur = coureur;
der->premier = der->dernier;
der->dernier->suiv = NULL;
} else {
// Ajout en fin
der->dernier->suiv = malloc(sizeof(struct element));
der->dernier = der->dernier->suiv;
der->dernier->coureur = coureur;
der->dernier->suiv = NULL;
}
return 0;
}
Parcours de liste:
int Aller_Debut(struct liste* liste) {
liste->courant = liste->premier;
return 0;
}
int Avancer(struct liste* liste) {
liste->courant = liste->courant->suiv;
return 0;
}
int Fin(struct liste liste) {
return (liste.courant == NULL);
}
Comptage d'éléments:
int nbElement(struct liste liste) {
int count = 0;
while (!Fin(liste)) {
Avancer(&liste);
count++;
}
return count;
}
5. Fichiers et Arguments
Arguments de ligne de commande (TD2):
int main(int argc, char* argv[]) {
printf("Nombre d'arguments: %d\n", argc);
if (argc == 3) {
printf("Programme: %s\n", argv[0]);
printf("Arg1: %s\n", argv[1]);
printf("Arg2: %s\n", argv[2]);
}
return 0;
}
Exécution: ./programme arg1 arg2
Manipulation de fichiers:
Ouverture et lecture:
FILE* fichier = fopen("data.txt", "r");
if (fichier == NULL) {
perror("Erreur ouverture");
return -1;
}
char buffer[256];
while (fgets(buffer, 256, fichier) != NULL) {
printf("%s", buffer);
}
fclose(fichier);
Modes d'ouverture: "r" (lecture), "w" (écriture), "a" (ajout), "rb" (binaire)
6. Programmation Modulaire
Projet TP: Structure modulaire
Fichier coureur.h:
#ifndef _COUREUR_H
#define _COUREUR_H
struct Coureur {
char* nom;
char* prenom;
int num_dossard;
char* nom_equipe;
int temps_etapes;
};
struct Coureur* Creer_Coureur(char nom[], char prenom[],
int dossard, char equipe[], int temps);
int Ajouter_Temps(struct Coureur* coureur, int temps_jour);
int Afficher_Coureur(struct Coureur* coureur);
#endif
Guards d'inclusion: #ifndef, #define, #endif évitent les inclusions multiples.
Compilation modulaire:
gcc -Wall -c coureur.c -o coureur.o
gcc -Wall -c liste.c -o liste.o
gcc -Wall -c test_coureur.c -o test_coureur.o
gcc coureur.o liste.o test_coureur.o -o programme
PART C: ASPECTS TECHNIQUES
Exercices de TD
TD1: Manipulation de tableaux et bits
Objectifs:
- Tri à bulles d'un tableau
- Multiplication sans opérateur *
- Opérations bit à bit (masquage, comptage)
TD2: Pointeurs et allocation dynamique
Exercices:
- Fonction Permute avec pointeurs
- Allocation dynamique avec calloc
- Arguments de ligne de commande
TD3: Structures et copie
Problématiques:
- Structures avec pointeurs vs tableaux
- Copie superficielle vs profonde
- Gestion mémoire dans structures
TD4-TD5: Fichiers et structures avancées
Applications pratiques de manipulation de données.
Travaux Pratiques
TP: Gestion de liste de coureurs
Application complète avec:
- Structure
Coureuravec allocation dynamique de chaînes - Liste chaînée pour gérer plusieurs coureurs
- Opérations: création, ajout, parcours, suppression
- Affichage et calcul de statistiques
Fonctionnalités implémentées:
- Création de coureurs avec malloc pour les strings
- Ajout en file (FIFO)
- Parcours avec pointeur courant
- Suppression d'élément à position donnée
- Comptage d'éléments
- Libération mémoire complète
TP_bis: Extensions
Versions avancées avec:
- Tri de listes
- Recherche par critères
- Fusion de listes
- Optimisations mémoire
Outils de Développement
GCC (GNU Compiler Collection):
Compilation de base:
gcc -Wall -o programme source.c
Options utiles:
-Wall: affiche tous les warnings-g: ajoute symboles de debug-O2: optimisation niveau 2-std=c99: standard C99
GDB (GNU Debugger):
gcc -g -o programme source.c
gdb ./programme
Commandes GDB:
break main: point d'arrêtrun: exécutionnext: ligne suivanteprint var: afficher variablebacktrace: pile d'appels
Valgrind (détection fuites mémoire):
valgrind --leak-check=full ./programme
Détecte:
- Fuites mémoire (malloc sans free)
- Accès mémoire invalides
- Utilisation de mémoire non initialisée
Bonnes Pratiques
Gestion mémoire:
- Toujours libérer ce qui est alloué
- Vérifier le retour de malloc
- Mettre les pointeurs à NULL après free
Code sûr:
char* str = malloc(100);
if (str == NULL) {
fprintf(stderr, "Erreur allocation\n");
return -1;
}
// Utilisation...
free(str);
str = NULL; // Évite double free
Organisation code:
- Fichiers .h pour déclarations
- Fichiers .c pour implémentations
- Guards d'inclusion systématiques
- Commentaires pour fonctions complexes
PART D: ANALYSE ET RÉFLEXION
Compétences acquises
Techniques:
- Maîtrise des pointeurs et gestion mémoire
- Implémentation de structures de données complexes
- Débogage avec GDB et Valgrind
- Compilation modulaire et organisation projet
- Manipulation bit à bit pour le bas niveau
Méthodologiques:
- Rigueur dans la gestion mémoire
- Tests systématiques des allocations
- Documentation et organisation du code
- Résolution méthodique des bugs
Auto-évaluation
Points forts:
- Compréhension solide des pointeurs
- Capacité à créer structures de données dynamiques
- Bonne pratique de la compilation modulaire
- Maîtrise des outils (GCC, GDB)
Défis:
- Complexité initiale des pointeurs de pointeurs
- Gestion rigoureuse de la mémoire (fuites)
- Debugging de segmentation faults
- Compréhension fine des copies de structures
Applications pratiques
Le C est utilisé dans:
- Systèmes embarqués: microcontrôleurs (Arduino, STM32)
- Systèmes d'exploitation: Linux kernel, drivers
- Temps réel: applications critiques
- Performances: calcul scientifique, traitement signal
- Réseaux: protocoles, serveurs
- Bases de données: PostgreSQL, SQLite
Connexions avec autres cours
- Microcontrôleurs (S6): programmation STM32 en C
- Systèmes d'exploitation (S6): appels système en C
- Temps Réel (S8): FreeRTOS programmé en C
- Réseaux (S6): sockets et protocoles en C
- Architecture (S5): compréhension assembleur depuis C
Perspectives
Évolution du langage:
- C11, C17, C23: standards modernes
- Outils statiques: Clang, sanitizers
- Intégration IDE: VS Code, CLion
Alternatives modernes:
- Rust: sécurité mémoire garantie
- C++: orienté objet avec compatibilité C
- Zig: modernisation du C
Mais le C reste incontournable pour:
- Systèmes embarqués contraints
- Interfaces hardware
- Performance maximale
- Legacy code (Linux, UNIX)
Recommandations
- Pratiquer régulièrement: coder chaque jour
- Lire du code: projets open source (Git, SQLite)
- Utiliser Valgrind: systématiquement
- Tester rigoureusement: cas limites, gestion erreurs
- Documenter: fonctions et algorithmes complexes
Ressources:
- K&R: "The C Programming Language" (référence)
- Beej's Guide: tutoriels réseau et sockets
- Linux kernel source: exemples réels
- Stack Overflow: résolution problèmes
En conclusion, le C reste le langage fondamental pour comprendre le fonctionnement bas niveau des ordinateurs et programmer efficacement les systèmes embarqués. La rigueur acquise en C est bénéfique pour tous les autres langages.
📚 Documents de Cours
Voici les supports de cours en PDF pour approfondir les différents aspects de la programmation C :
🎯 Les Pointeurs
Cours complet sur les pointeurs, l'arithmétique des pointeurs, et la manipulation de la mémoire en C.
🏗️ Structures et Types Composés
Définition et utilisation des structures, unions, énumérations et types définis par l'utilisateur.
💾 Gestion de la Mémoire
Allocation dynamique, gestion du heap, détection de fuites mémoire et bonnes pratiques.
⚙️ Compilation et Linking
Processus de compilation, préprocesseur, linkage, bibliothèques statiques et dynamiques.
📁 Entrées/Sorties et Fichiers
Manipulation de fichiers en C, flux standard, fonctions de lecture/écriture et gestion d'erreurs.
🏃 TP: Gestion de Coureurs
Travail pratique complet sur les listes chaînées avec gestion dynamique de structures de coureurs.
💻 C Language - S5
Year: 2022-2023 (Semester 5)
Credits: 3 ECTS
Type: Systems Programming
PART A: GENERAL OVERVIEW
Course Objectives
The C Language course provides comprehensive training in systems programming with C, a fundamental language for developing embedded systems, operating systems, and applications requiring optimal performance. The course covers fundamental concepts (pointers, arrays, structures) as well as advanced topics (dynamic memory allocation, bit manipulation, complex data structures).
Target Skills
- Master C syntax and data types
- Understand pointers and pointer arithmetic
- Manage dynamic memory (malloc, free)
- Manipulate data structures (linked lists, trees)
- Perform bitwise operations for low-level programming
- Create modular programs with multiple files
- Debug and optimize C code
Organization
- Course hours: 48h (Lectures: 24h, Tutorials/Labs: 24h)
- Assessment: Written exams (60%) + Labs and projects (40%)
- Semester: 5 (2022-2023)
- Prerequisites: Basic algorithms, programming fundamentals
PART B: EXPERIENCE, CONTEXT AND FUNCTION
Course Content
1. C Fundamentals
Data Types and Operators:
C provides fixed-size primitive types:
char: 1 byte (8 bits)int: 4 bytes on 32/64-bit systemsfloat: 4 bytes (single precision)double: 8 bytes (double precision)
Modifiers: signed, unsigned, short, long
Declaration examples:
int nombre = 42;
unsigned int positif = 100;
char lettre = 'A';
float pi = 3.14f;
Bitwise Operators:
Essential for low-level manipulation:
&(AND),|(OR),^(XOR),~(NOT)<<(left shift),>>(right shift)
TD1 Application: Count the number of 1-bits in an integer:
int bitcount(int n) {
int count = 0;
while (n) {
count += n & 1; // Test the least significant bit
n >>= 1; // Shift one bit to the right
}
return count;
}
For 0xF0000F00, result: 8 bits set to 1.
Control Structures:
Standard loops and conditionals:
// Bubble sort (TD1)
for (int j = 0; j < TAILLE-1; j++) {
for (int i = 0; i < TAILLE-j-1; i++) {
if (tab[i+1] < tab[i]) {
int temp = tab[i];
tab[i] = tab[i+1];
tab[i+1] = temp;
}
}
}
2. Pointers and Memory Management
Pointers:
A fundamental concept in C: a variable that holds a memory address.
Declaration and usage:
int x = 10;
int *ptr = &x; // ptr points to x
*ptr = 20; // Modifies x through the pointer
Pointer Arithmetic:
int tab[5] = {1, 2, 3, 4, 5};
int *p = tab; // Points to tab[0]
p++; // Points to tab[1]
*p = 100; // tab[1] = 100
Swap via Pointers (TD2):
int Permute(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
return 0;
}
Dynamic Allocation:
Essential functions:
malloc(size): allocates size bytescalloc(n, size): allocates and initializes to 0free(ptr): frees the memory
TD2 example:
char* chaine = calloc(10, sizeof(char));
int* tab = calloc(10, sizeof(int));
// Usage...
free(chaine);
free(tab);
Risks:
- Memory leak if
freeis forgotten - Double free (freeing twice)
- Access after free (use-after-free)
- Buffer overflow
3. Structures and Composite Types
Structure Definition (TD3):
struct Etudiant {
char nom[50];
int naissance;
int notes[10];
int nb_notes;
};
Usage:
struct Etudiant cedric;
strcpy(cedric.nom, "Chanfreau");
cedric.naissance = 2000;
cedric.notes[0] = 15;
Pointers to Structures:
struct Etudiant *ptr = &cedric;
ptr->naissance = 2001; // Equivalent to (*ptr).naissance
Figure: Pointer concept - A pointer stores the memory address of a variable
Structure Copying:
Beware of pointers inside structures:
struct S1 {
int a;
char* ch; // Pointer
};
struct S1 v1, v2;
v1.ch = calloc(10, sizeof(char));
strcpy(v1.ch, "Test");
v2 = v1; // Shallow copy: v2.ch points to the same memory!
Solution: manual deep copy or separate allocation.
4. Dynamic Data Structures
Linked Lists (Main Lab Project):
Element structure:
struct element {
struct Coureur* coureur;
struct element* suiv;
};
struct liste {
struct element* premier;
struct element* dernier;
struct element* courant;
};
Initialization:
int init_liste(struct liste* liste) {
liste->premier = NULL;
liste->dernier = NULL;
return 0;
}
Enqueue (Queue Insertion):
int ajout_file(struct liste* der, struct Coureur* coureur) {
if (der->dernier == NULL) {
// Empty list
der->dernier = malloc(sizeof(struct element));
der->dernier->coureur = coureur;
der->premier = der->dernier;
der->dernier->suiv = NULL;
} else {
// Append at end
der->dernier->suiv = malloc(sizeof(struct element));
der->dernier = der->dernier->suiv;
der->dernier->coureur = coureur;
der->dernier->suiv = NULL;
}
return 0;
}
List Traversal:
int Aller_Debut(struct liste* liste) {
liste->courant = liste->premier;
return 0;
}
int Avancer(struct liste* liste) {
liste->courant = liste->courant->suiv;
return 0;
}
int Fin(struct liste liste) {
return (liste.courant == NULL);
}
Element Counting:
int nbElement(struct liste liste) {
int count = 0;
while (!Fin(liste)) {
Avancer(&liste);
count++;
}
return count;
}
5. Files and Arguments
Command-Line Arguments (TD2):
int main(int argc, char* argv[]) {
printf("Number of arguments: %d\n", argc);
if (argc == 3) {
printf("Program: %s\n", argv[0]);
printf("Arg1: %s\n", argv[1]);
printf("Arg2: %s\n", argv[2]);
}
return 0;
}
Execution: ./programme arg1 arg2
File Manipulation:
Opening and reading:
FILE* fichier = fopen("data.txt", "r");
if (fichier == NULL) {
perror("Open error");
return -1;
}
char buffer[256];
while (fgets(buffer, 256, fichier) != NULL) {
printf("%s", buffer);
}
fclose(fichier);
Open modes: "r" (read), "w" (write), "a" (append), "rb" (binary)
6. Modular Programming
Lab Project: Modular Structure
File coureur.h:
#ifndef _COUREUR_H
#define _COUREUR_H
struct Coureur {
char* nom;
char* prenom;
int num_dossard;
char* nom_equipe;
int temps_etapes;
};
struct Coureur* Creer_Coureur(char nom[], char prenom[],
int dossard, char equipe[], int temps);
int Ajouter_Temps(struct Coureur* coureur, int temps_jour);
int Afficher_Coureur(struct Coureur* coureur);
#endif
Include Guards: #ifndef, #define, #endif prevent multiple inclusions.
Modular Compilation:
gcc -Wall -c coureur.c -o coureur.o
gcc -Wall -c liste.c -o liste.o
gcc -Wall -c test_coureur.c -o test_coureur.o
gcc coureur.o liste.o test_coureur.o -o programme
PART C: TECHNICAL ASPECTS
Tutorial Exercises
TD1: Array and Bit Manipulation
Objectives:
- Bubble sort on an array
- Multiplication without the * operator
- Bitwise operations (masking, counting)
TD2: Pointers and Dynamic Allocation
Exercises:
- Swap function using pointers
- Dynamic allocation with calloc
- Command-line arguments
TD3: Structures and Copying
Key issues:
- Structures with pointers vs arrays
- Shallow copy vs deep copy
- Memory management within structures
TD4-TD5: Files and Advanced Structures
Practical data manipulation applications.
Lab Work
Lab: Runner List Management
Complete application with:
Coureur(Runner) structure with dynamic string allocation- Linked list to manage multiple runners
- Operations: creation, insertion, traversal, deletion
- Display and statistics computation
Implemented Features:
- Runner creation with malloc for strings
- Queue insertion (FIFO)
- Traversal with current pointer
- Element deletion at a given position
- Element counting
- Full memory deallocation
Lab Extension
Advanced versions with:
- List sorting
- Criteria-based search
- List merging
- Memory optimizations
Development Tools
GCC (GNU Compiler Collection):
Basic compilation:
gcc -Wall -o programme source.c
Useful options:
-Wall: display all warnings-g: add debug symbols-O2: optimization level 2-std=c99: C99 standard
GDB (GNU Debugger):
gcc -g -o programme source.c
gdb ./programme
GDB commands:
break main: set breakpointrun: executenext: next lineprint var: display variablebacktrace: call stack
Valgrind (memory leak detection):
valgrind --leak-check=full ./programme
Detects:
- Memory leaks (malloc without free)
- Invalid memory accesses
- Use of uninitialized memory
Best Practices
Memory Management:
- Always free what is allocated
- Check the return value of malloc
- Set pointers to NULL after free
Safe Code:
char* str = malloc(100);
if (str == NULL) {
fprintf(stderr, "Allocation error\n");
return -1;
}
// Usage...
free(str);
str = NULL; // Prevents double free
Code Organization:
- .h files for declarations
- .c files for implementations
- Systematic include guards
- Comments for complex functions
PART D: ANALYSIS AND REFLECTION
Acquired Skills
Technical:
- Mastery of pointers and memory management
- Implementation of complex data structures
- Debugging with GDB and Valgrind
- Modular compilation and project organization
- Bitwise manipulation for low-level programming
Methodological:
- Rigor in memory management
- Systematic testing of allocations
- Code documentation and organization
- Methodical bug resolution
Self-Assessment
Strengths:
- Solid understanding of pointers
- Ability to create dynamic data structures
- Good modular compilation practices
- Proficiency with tools (GCC, GDB)
Challenges:
- Initial complexity of pointer-to-pointer concepts
- Rigorous memory management (leaks)
- Debugging segmentation faults
- Deep understanding of structure copying
Practical Applications
C is used in:
- Embedded systems: microcontrollers (Arduino, STM32)
- Operating systems: Linux kernel, drivers
- Real-time: safety-critical applications
- Performance: scientific computing, signal processing
- Networking: protocols, servers
- Databases: PostgreSQL, SQLite
Connections with Other Courses
- Microcontrollers (S6): STM32 programming in C
- Operating Systems (S6): system calls in C
- Real-Time Systems (S8): FreeRTOS programmed in C
- Networking (S6): sockets and protocols in C
- Architecture (S5): understanding assembly from C
Outlook
Language Evolution:
- C11, C17, C23: modern standards
- Static analysis tools: Clang, sanitizers
- IDE integration: VS Code, CLion
Modern Alternatives:
- Rust: guaranteed memory safety
- C++: object-oriented with C compatibility
- Zig: modernization of C
But C remains essential for:
- Resource-constrained embedded systems
- Hardware interfaces
- Maximum performance
- Legacy code (Linux, UNIX)
Recommendations
- Practice regularly: code every day
- Read code: open source projects (Git, SQLite)
- Use Valgrind: systematically
- Test rigorously: edge cases, error handling
- Document: complex functions and algorithms
Resources:
- K&R: "The C Programming Language" (reference)
- Beej's Guide: networking and sockets tutorials
- Linux kernel source: real-world examples
- Stack Overflow: problem solving
In conclusion, C remains the fundamental language for understanding the low-level workings of computers and for efficiently programming embedded systems. The rigor acquired through C is beneficial for all other programming languages.
📚 Course Documents
Here are the course materials in PDF format to deepen your understanding of the various aspects of C programming:
🎯 Pointers
Complete course on pointers, pointer arithmetic, and memory manipulation in C.
🏗️ Structures and Composite Types
Definition and use of structures, unions, enumerations and user-defined types.
💾 Memory Management
Dynamic allocation, heap management, memory leak detection and best practices.
⚙️ Compilation and Linking
Compilation process, preprocessor, linking, static and dynamic libraries.
📁 Input/Output and Files
File manipulation in C, standard streams, read/write functions and error handling.
🏃 Lab: Runner Management
Complete lab on linked lists with dynamic management of runner structures.