Rappel sur la programmation en C: Structure d’un programme [ des inclusion d’autres fichiers, ex: include <stdio.h>] [ directives au préprocesseur, ex: define MA_CONSTANTE 10] [ déclarations de variables globales, externes ou statiques. Ces variables sont utilisable (visibles) par toutes les fonctions qui suivent] type fonction1 ( arguments ) { déclarations de variables internes; instructions et/ou fonctions; } type fonction2 ( arguments ) { int main(int argc, char *argv[]) { déclarations de variables; instruction: expression ou fonction1 ou fonctions2 ou tout autre fonction déclarée dans les fichiers de la directive « include »; … return val; ESME - Programmation Système sous Linux - A.TALBI
Rappel sur la programmation en C: Structure d’un programme Un programme en langage C est constitué de groupes de composants élémentaires suivants: – les identificateurs (noms de variables et fonctions) – les mots-clefs (ex: les types de variables), – les constantes, – les chaines de caractères, – les operateurs, – les signes de ponctuation. On peut ajouter a ces six groupes les commentaires, qui sont enlevés par le préprocesseur, ex: // ceci est un commentaire sur une ligne en C /* ce ci est un commentaire sur plusieurs lignes en C */ Les mots-clefs: – les spécificateurs de stockage: register static extern typedef – les spécificateurs de type: char double enum float int long short signed struct union unsigned void – les qualificateurs de type: const volatile – les instructions de contrôle: break case continue default do else for goto if switch while – divers return sizeof ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: types de variables Quelques types de variables et les fonctions printf/scanf ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: les caractères Les constants de type caractères: Mémoire \n nouvelle ligne \r retour chariot \t tabulation horizontale \f saut de page \v tabulation verticale \a signal d’alerte \b retour arrière addr1 0x41 0x2b Contrôleur graphique 0x42 Ecran 0x3d A+B= C Codes ASCII 0x0d 0x43 Programme Afficher les 4 caractères à partir de l'adresse addr1 ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: instructions de contrôle Format d’une instruction: variable op_affect expression (en général l’operateur d’affectation est ‘’=‘’ ) Format d’une expression: « variable » ou bien « variable operateur expression » ou bien « expression operateur expression » ou bien « » Operateurs: Arithmétique: + , - , * , / , % logiques: > , >= , < , >= , == , != , && , || , ! bit a bit: & , ~ , << , >> affectation: = , += , -= , /= , &= , |= , ++ , -- …. Branchement conditionnel if---else Branchement multiple switch Boucle while Boucle for if ( expression1 ) { instruction(s)1; } else if ( expression2 ) instruction(s)2; … else if(expression_n) else instructions(s); switch ( expression ) { case constante1: instruction(s)1; break; case constante2: instruction(s)2; ... default: instruction(s)n; } while ( expression ) { instruction(s); } do }while ( expression ); for ( exp-debut ; cond-fin ; exp-increment ) { instruction(s); } ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: fonctions d’entrées-sorties (fonctions d’affichage et de saisie) printf("chaine de caractères %token1….%token2…", var1,var2…); scanf("chaine de caractères %token1…", &var1,…); si var1 est la variable cible où mettre la donnée saisie scanf("chaine de caractères %token1….", var1 ,…); si var1 est l'adresses de la variable cible ou l'adresse de début de la zone de stockage (ex: cas de %s) caractere = getchar(); putchar(caractere); ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: les types composés - tableaux déclaration: type nom-du-tableau[nombre-d-eléments]; Exemple 1: #define N 4 int tab[N] = {1, 2, 3, 4}; main() { int i; for (i = 0; i < N; i++) printf("tab[%d] = %d\n",i,tab[i]); } Exemple 2: #define N 10 main() { int tab1[N], tab2[N]; int i; ... for (i = 0; i < N; i++) tab1[i] = tab2[i]; } Exemple 3: tableau à deux dimensions #define M 2 #define N 3 int tab[M][N] = {{1, 2, 3}, {4, 5, 6}}; main() { int i, j; for (i = 0 ; i < M; i++) for (j = 0; j < N; j++) printf("tab[%d][%d]=%d\n",i,j,tab[i][j]); } ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: les types composés - les structures déclaration: struct nom_structure { type1 membre1; // champ type2 membre2; ... type_n member_n; }; Remarque: le type d'un champ d'une structure peut être lui même une structure… Exemple: #include <math.h> struct complexe { double reelle; double imaginaire; }; main() struct complexe z; double norme; ... z.relle = … z.imaginaire = … norme = sqrt(z.reelle * z.reelle + z.imaginaire * z.imaginaire); printf("norme de (%f + i %f) = %f \n", z.reelle,z.imaginaire,norme); } ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: les pointeurs Variables dans la mémoire Exemple: int i, j; i = 3; j = i; Mémoire Adresse 0 …… Adresse 4831836000 3 (=i) Adresse 4831836004 3 (=j) ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: les pointeurs - suite Les pointeurs Déclaration: type *nom_du_pointeur int i = 3; int *p; // p est une variable de type pointeur sur un entier p = &i; // p prend la valeur de l'adresse mémoire de la variable i L’opérateur unaire d’indirection * permet d’accéder directement à la valeur de l’objet pointé, exemple: main() { int i = 3; int *p; p = &i; printf("le contenu de l'addresse mémoire %d est %d \n", p , *p); } Le programme ci-dessus afficher: le contenu de l'addresse mémoire 4831836000 est 3 ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: arithmétique sur les pointeurs int i = 3; // supposant que l'adresse mémoire de i est égale à 40 int *ptr_i = &i; // la valeur de ptr_i est égale à l'adresse mémoire de i donc ptr_i = 40 char c ='a'; // suposant que l'adresse mémoire de c est égale à 80 char *ptr_c = &c // la valeur de ptr_c est égale à l'adresse de c donc ptr_c = 80 … ptr_i = ptr_i + 1; // la valeur de ptr_i devient égale à 44 (= 40 + 4, car la taille du type entier est égale à 4 octets) ptr_c = ptr_c + 1; // la valeur de ptr_c devient égale à 81 (= 80 + 1 car la taile du type char est égale à 1 octet) En générale, dans l'expression "ptr = ptr + n", la nouvelle valeur réelle (contenu mémoire) de la variable ptr est: "ptr_val + n * sizeof(type_vers_lequel_pointe_ptr)" Remarque: la fonction sizeof permet de récupérer la taille en octets d'un type ou d'une variable, ex: sizeof(int) donne en général 4 (peut changer suivant l'architecture du processeur et du compilateur…) sizeof(char) donne en général 1 (peut changer dans le future peut changer suivant l'architecture du processeur et du compilateur…) ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: pointeurs et tableaux Mémoire #define N 5 int tab[5] = {1, 2, 6, 0, 7}; main() { int i; int *p; p = tab; for (i = 0; i < N; i++) printf(" %d \n", p[i]); } #define N 5 int tab[5] = {1, 2, 6, 0, 7}; main() { int i; int *p; p = tab; tab = tab +1; // instruction illégale for (i = 0; i < N; i++) printf(" %d \n", p[i]); } Adresse 0 p = tab 1 2 6 7 En fait, tab est un type de pointeur particulier. C'est un pointeur constant et sa valeur lui est attribuée au moment de la compilation. tab pointe sur le début de la mémoire du tableau auquel il fait référence Un pointeur peut être manipulé comme un tableau ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: pointeurs et chaines de caractères Mémoire Adresse 0 chaine 'c' 'h' 'a' 'i' 'n' 'e' ' ' 'd' 'e' ,,,, 'r' 'e' 's' ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: allocation dynamique de la mémoire Problème: Supposant qu’on a les 3 programmes suivant sur un PC de 64Mb de RAM: Programme P1: navigateur internet Main() { char video[30Mb]; while(1) attendre_action_utilisateur(); download(video, "www.youtube.com/video1"); jouer_video(video) } Ce programme occupe plus de 30Mb en mémoire à cause du tableau video Programme P2: client ftp (transfert de fichier) Main() { char fichier[10Mb]; while(1) attendre_action_utilisateur(); download(fichier, "www.esme.fr/cours.pdf"); sauvegarder_fichier(fichier); } Ce programme occupe plus de 10Mb en mémoire à cause du tableau fichier Programme P3: éditeur de texte ("ex word") Main() { char pages[15][2Mb]; while(1) attendre_action_utilisateur(); read(pages, "mon_fichier"); afficher(); } Ce programme occupe plus de 30Mb (15x2) en mémoire à cause du tableau fichier Que se passe-t-il si l'utilisateur lance P1 puis P2 puis P3 ? le programme P3 ne pourra pas se lancer car il ne reste plus assez de mémoire dans le système. Pire!! l'utilisateur profane ne peut pas savoir pourquoi sont éditeur de texte (P3) ne peut pas se lancer… L'utilisation de la mémoire par les programmes P1, P2, P3 n'est pas optimale ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: allocation dynamique de la mémoire – suite Solution: Allouer de la mémoire au moment où on en a besoin Allocation dynamique Programme P1: navigateur internet #define TAILLE (0x100000 * 10) Main() { char *video; // video est un pointeur while(1) attendre_action_utilisateur(); while(video == NULL) // demander un espace mémoire // au system (kernel) video = malloc(TAILLE ); sleep(1); } download(video, "www.youtube.com/video1"); jouer_video(video) free(video); // restituer la mémoire au system video = NULL; Programme P2: client ftp (transfert de fichier) #define TAILLE (0x100000 * 10) Main() { char *fichier; while(1) attendre_action_utilisateur(); while(fichier == NULL) // demander un espace mémoire // au system (kernel) fichier = malloc(TAILLE ); sleep(1); } download(fichier, "www.esme.fr/cours.pdf"); sauvegarder_fichier(fichier); free(fichier); // restituer la mémoire au system fichier = NULL; Programme P3: éditeur de texte ("ex word") #define TAILLE (0x200000) Main() { int i = 0; char *pages[15]; while(1) attendre_action_utilisateur(); for(i = 0; i < 15; i++) pages[i] = NULL; while(pages[i] == NULL) pages[i] = malloc(TAILLE ); sleep(1); } read(pages, "mon_fichier"); sauvegarder_fichier(fichier); free(pages[i]); ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: arguments en ligne de commande > ./test 1 15.3 bonjour A -39 les 5 parametres de la commande ./test sont : 1 15.3 bonjour A -39 > ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: fonctions usuelles (libc) Manipulation de chaines de caractère <string.h> fonction prototype action strcpy char *strcpy(char *ch1, char *ch2) copie la chaîne ch2 dans la chaîne ch1 ; retourne ch1. strncpy char *strcpy(char *ch1, char *ch2, int n) copie n caractères de la chaîne ch2 dans la chaîne ch1 ; retourne ch1. strcat char *strcat(char *ch1, char *ch2) copie la chaîne ch2 à la fin de la chaîne ch1 ; retourne ch1. strncat char *strncat(char *ch1, char *ch2, int n) copie n caractères de la chaîne ch2 à la fin de la chaîne ch1 ; retourne ch1. strcmp int strcmp(char *ch1, char *ch2) compare ch1 et ch2 pour l'ordre lexicographique ; retourne une valeur négative si ch1 est inférieure à ch2, une valeur positive si ch1 est supérieure à ch2, 0 si elles sont identiques. strncmp int strcmp(char *ch1, char *ch2, int n) compare les n premiers caractères de ch1 et ch2. strchr char *strchr(char *chaine, char c) retourne un pointeur sur la première occurence de c dans chaine, et NULL si c n'y figure pas. strrchr retourne un pointeur sur la dernière occurence de c dans chaine, et NULL si c n'y figure pas. strstr char *strchr(char *ch1, char *ch2) retourne un pointeur sur la première occurence de ch2 dans ch1, et NULL si ch2 n'y figure pas. strlen int strlen(char *chaine) retourne la longueur de chaine. ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: fonctions usuelles (libc) Conversion de chaînes de caractères en nombres fonction prototype action atof double atof(char *chaine) convertit chaine en un double atoi int atoi(char *chaine) convertit chaine en un int atol long atol(char *chaine) convertit chaine en un long int Arithmétique sur les entiers fonction prototype action abs int abs(int n) valeur absolue d'un entier labs long labs(long n) valeur absolue d'un long int div div_t div(int a, int b) quotient et reste de la division euclidienne de a par b. Les résultats sont stockés dans les champs quot et rem (de type int) d'une structure de type div_t. ldiv ldiv_t ldiv(long a, long b) quotient et reste de la division euclidienne de a par b. Les résultats sont stockés dans les champs quot et rem (de type long int) d'une structure de type ldiv_t. rand int rand(void) fournit un nombre entier pseudo-aléatoire srand void srand(unsigned int germe) modifie la valeur de l'initialisation du générateur pseudo-aléatoire utilisé par rand. ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: fonctions usuelles (libc) Fonctions mathématiques <math.h> Manipulation de caractères <ctype.h> fonction action acos arc cosinus asin arc sinus atan arc tangente cos cosinus sin sinus tan tangente cosh cosinus hyperbolique sinh tanh tangente hyperbolique exp exponentielle log logarithme népérien log10 logarithme en base 10 pow puissance sqrt racine carrée fabs valeur absolue fmod reste de la division ceil partie entière supérieure floor partie entière inférieure fonction renvoie 1 si le caractère est isalnum une lettre ou un chiffre isalpha une lettre iscntrl un caractère de commande isdigit un chiffre décimal isgraph un caractère imprimable ou le blanc islower une lettre minuscule isprint un caractère imprimable (pas le blanc) ispunct un caractère imprimable qui n'est ni une lettre ni un chiffre isspace un blanc isupper une lettre majuscule isxdigit un chiffre hexadécimal ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: La Compilation ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: Types d'exécutables Phases de la compilation: Le préprocessing: L'étape ou toutes les instructions préprocesseurs sont exécutées (ce sont les instructions commençants par le caractère #, ex: #include, #define…) on peut le voir comme une étape d'insertion et de remplacement du texte pour avoir à la fin un seul gros fichier texte qui va être donné au compilateur. La compilation produit un fichier objet (fichier dont l'extension est .o) à partir du texte issue du préprocessing du fichier .c) L'édition de liens produit un binaire en faisant le lien entres les fichiers objets. Ce dernier peut être de l'un des types suivant: Un programme exécutable Une bibliothèque partagée/dynamiques (fichier dont l'extension est .so pour Shared Object): c'est un binaire qui peut être utilisé (partagé) par toutes les instances du même programmes et d'autres instances d'autres programmes. De ce fait une bibliothèque dynamique possède une seule instance en mémoire (donc on économise de la mémoire). Elle offre au programme des fonctions qui sont d'une utilité répondue (ex, les binaires des fonctions printf, scanf sont inclus dans la libc.so ) Remarque: il existe un troisième type qui est les bibliothèques statiques (fichier dont l'extension est .a) qui sont en fait une archive de fichiers objets mais à l'inverse des bibliothèques partagés il y a une instance de librairie statique par instance de programme En général le compilateur et l'éditeur de lien font partie du même programme qu'on appelle par abus/extension de langage le compilateur. Sous linux le compilateur généralement utilisé est GCC: Gnu C Compiler Pour compiler un fichier on utilise la commande : gcc –c –Wall mon_fichier.c Pour linker plusieurs fichiers on utilise la commande: gcc –o mon_executable fichier1.o fichier2.o … fichiern.o –lmysharedlib my_static_lib.a… On peut passer à GCC plusieurs options tels que: -Wall : afficher tous les warning (il faut absolument éviter qu'il y en ai car ce sont généralement des bugs) -Inom_du_dossier: indique à GCC où aller chercher des fichier header (.h) -Lnom_du_dossier: indique à GCC où aller chercher les libraires partagées ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: bibliothèque partagée vs statique Bibliothèque statique Le nom d'une lib static a en général en extension .a Création: #compilation des fichier de la lib gcc –c fichier1.c création du fichier objet fichier1.o gcc –c fichier2.c …. gcc –c fichiern.c #archivage et création de la lib ar rcs fichier1.o fichier2.o … fichiern.o libtest.a #utilisation pour générer deux programmes: gcc –o prog1 prog1.o libtest.a génération d'un programme prog1 gcc –o prog1 prog1.o libtest.a génération d'un programme prog2 Bibliothèque partagée Le nom d'une lib static a en général en extension .so Création: #compilation des fichier de la lib gcc –c -fPIC fichier1.c gcc –c –fPIC fichier2.c …. gcc –c –fPIC fichiern.c #archivage et création de la lib gcc -shared -o libtest.so fichier1.o fichier2.o … fichiern.o #utilisation pour générer deux programmes: gcc -L/path/to/lib -Wall -o prog1 prog1.o –ltest gcc -L/path/to/lib -Wall -o prog1 prog2.o -ltest mémoire mémoire prog1 prog1 Libtest.a Libtest.so 1 seule copie en mémoire 2 copies en mémoire prog2 Libtest.a prog2 ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: Types de compilations On appelle machine hôte (host) la machine sur laquelle s'exécute notre compilation. C'est la machine (en général un PC) sur laquelle est installé l'environnement de développement (toolchain: chaine d'outils de développement) . Ce dernier contient en général un éditeur de texte et obligatoirement le compilateur. On appelle machine cible (target), la machine sur laquelle va s'exécuter notre programme issue de la compilation. Cette machine peut être de type PC ou bien une carte numérique pour un système embarqué (téléphone, robot, avion, voiture, machine industrielle…) Il existe plusieurs familles d'architecture matérielle (architecture du microprocesseur donc du jeux d'instructions en assembleur), ex x86: carte à base d'un intel/AMD sur 32 bits X86-64: carte à base d'un intel/AMD sur 64 bits ARMv7: architecture ARM Il existe deux types de compilation: 1- Native: la machine hôte et la machine cible sont de la même famille (donc l'exécutable doit "tourner" sur la machine hôte) 2- Croisée (cross-compilation): la machine hôte et la machine cible sont de familles différentes (donc l'exécutable ne peut être exécuté que sur la target) Il existe deux types de compilation: 1- Native: la machine hôte et la machine cible sont de la même famille (c'est le cas en générale des programmes/logiciels pour PC) 2- Croisée (cross-compilation): la machine hôte et la machine cible sont de familles différentes (donc l'exécutable ne peut être exécuté que sur la target). C'est les cas en général des programmes/logiciels embarqués ESME - Programmation Système sous Linux - A.TALBI
Gestion des entrées/sorties: Automatisation de la compilation (BUILD) Problème : Supposant qu'on a un programme a compiler. Ce programme se compose de quelques dizaines de fichiers C. Comment vous y prendriez vous ? Remarque: L'opération consistant à compiler plusieurs fichiers afin de générer un binaire s'appelle un BUILD Pistes: 1- Compiler les fichier un après l'autre puis lancer l'édition des liens de tous les fichiers objets est juste aberrant est contre même l'essence de l'informatique qui signifie automatisation du traitement de l'information 2- Utiliser un script pour automatiser le build: Ceci peut être une solution mais elle a des inconvénients majeurs tels que: si on modifie un fichier on est obligé de relancer le script pour re-builder et donc recompiler tous les autres fichiers perte de temps (sachant que le build d'un gros system comme linux peut prendre des heures) . Modifier le nom d'un fichier implique la modification du script manque de robustesse
Gestion des entrées/sorties: Automatisation de la compilation avec un Makefile Solution : Utiliser un Makefile Un makefile est un fichier qui permet l'automatisation du build d'un ou plusieurs binaires d'une manière robuste et optimale. Un makefile s'utilise avec la commande make. Par défault lorsqu'on lance la commande make, cette dernière s'attend à trouver un fichier dont le nom est "Makefile" dans le répertoire courant. Ce fichier contient des instructions avec un format bien défini Structure d'un makefile: cible1: liste de dépendances <TAB> commandes UNIX cible2: liste de dépendances Cible_n: liste de dépendances Principe: Si un (ou plusieurs) élément de la liste de dépendance d'une cible a été modifié alors les commande de la cible sont exécutés La liste de dépendance d'une cible peut être composée de fichiers ou d'autres cibles (à vrai dire un fichier est considéré comme étant lui-même une cible)
Gestion des entrées/sorties: Makefile (Gain de temps de build) programme: fichier_1.o fichier_2.o fichier_3.o … fichier_n.o <TAB> gcc –o programme fichier_1.o fichier_2.o … fichier_n.o fichier_1.o: fichier_1.c <TAB> gcc –Wall –O0 –c fichier_1.c fichier_2.o: fichier_2.c <TAB> gcc –Wall –O0 –c fichier_2.c …. fichier_n.o: fichier_n.c <TAB> gcc –Wall –O0 –c fichier_n.c Remarque : Dans cet exemple si on modifier un seul fichier ceci implique la recompilation du fichier modifié et l'édition de liens (on ne recompile pas les autres fichiers) gain de temps
Gestion des entrées/sorties: Makefile (variables automatiques) Question: Comment peut on faire pour éviter de taper tous les noms de fichiers Solution: utiliser des variables automatiques: programme: fichier_1.o fichier_2.o fichier_3.o … fichier_n.o <TAB> gcc –o programme fichier_1.o fichier_2.o … fichier_n.o %.o: %.c <TAB> gcc –Wall –O0 –c $<
Gestion des entrées/sorties: Makefile (fonctions) Question: Comment peut on faire pour éviter de modifier le makefile si on change le nom d'un fichier ou si on le supprime Solution: utiliser des variables et des fonctions (un makefile est pareil qu'un langage de programmation !!!) LIST_SRC = $(wildcard *.c) LIST_OBJ = $(patsubst %.c,%.o,$(LIST_SRC)) CFLAGS = -Wall –O0 programme: $(LIST_OBJ) <TAB> gcc –o programme $(LIST_OBJ) %.o: %.c <TAB> gcc $(CFLAGS) –c $< Remarque: Dans les exemples précédent il faudrait faire attention au dépendance par rapport au fichier d'entête (sinon on risque de faire crasher le programme)
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: Règles (bonnes pratiques) de codage en C Commentaires: Début du fichier: nom du module , description de la tache principale du module, auteur(s) et date de création. Séparer le fichier en sections et écrire un commentaire pour chaque section du fichier qui contient des informations sur celle-ci. En générale on retrouves les sections suivantes dans l'ordre: section définition des constantes et macros, section définition des types, section définition des variables globales, section définition des variables static (locales au fichier), section des fonctions static (local au fichier) et en en fin section des fonction d'interface (celles qui vont être utilisées dans d'autres fichiers) Indentation: A chaque fois qu'on ouvre une accolade les lignes suivantes doivent être alignées et décalées de la ligne précédent l'accolade et, ceci permet de visualiser rapidement les différent blocs du code, les éditeurs de texte (ex VI) permettent d'automatiser cette opération. Une instruction par ligne (sauf dans le cas d'une expression de calcul triviale ou il faut utiliser plus d'un opérateur…) Conventions de nommages: Utiliser un define pour chaque constante, ex il ne faut pas utiliser la valeur 9,81 pour la gravité directement dans le code mais plutôt : Ceci facilite la vie d'un développeur car s'il faut changer la "valeur" de la constante alors on ne le fera qu'une seul fois et on et sure qu'on a pas oublié de la changé quelques part dans le code (on sait jamais on pourra modifier les unités de mesure ou même utiliser votre programme pour une mission sur la lune !!!) . Nommage des variable et fonctions: prefix_nom_sufix Nommage des type: MonType, ex ElectircalMachine Nommage des constantes et des enums: PREFIX_NOM_SUFIX Toujours traiter les cas d'erreurs en premier et libérer les ressources allouées dans le cas d'une erreurs. Valeurs de retour: Analyser la valeur de retour (si y en a ) de chaque fonction utilisée car la fonction peut échouer et si on fait pas gaffe on risque de faire cracher le programme ou pire tout le système dans le cas d'un driver ESME - Programmation Système sous Linux - A.TALBI
ESME - Programmation Système sous Linux - A.TALBI Rappel sur la programmation en C: TP Exercice 1: équation du second degré Soit l’équation suivante: a(X^2) + bX + c = 0 Écrire un programme resolve2 qui prend trois paramètres a, b et c (./resolve2 a b c) puis calcule les solutions de cette équation et les affiche à l’écran Exercice 2: pointeurs et chaine de caractères Écrivez une fonction qui prend en argument une chaîne de caractères (max 100) puis la transforme en majuscules ( "toto a 29 ans "−→"TOTO A 29 ANS" ), aidez vous du tableau des codes ascii (la différence entre le code d'une lettre en majuscule et celui du code de la même lettre en minuscule est une constante) Exercice 3: getc, sscanf et malloc L'objectif de cette exercice est de créer un programme qui classe par ordre croissant une liste d'entiers ou par ordre alphabétique une liste de noms Créez un fichier acquire.c, puis dans ce fichier: - Écrire une fonction int acquire(char *buf) qui récupère ce que l'utilisateur tape sur le clavier à l'aide de la fonction getc() et le stocke dans le buffer (tableau) buf, la fonction doit s'arrêter lors d'un retour à la ligne (caractère '\n') et renvoyer le nombre de virgules trouvés. Si getc() renvoie une erreur ou le nombre de caractères entrés au clavier atteint une limite maximal (constante prédéfinie à l'aide d'un #define) un message d'erreur doit être affiché et un code de retour négatif (ex -1) signifiant une erreur doit être retourné par la fonction acquire() Créez un fichier iorder.c, puis dans ce fichier: - Écrire une fonction int * iparse(char *buf, int len) qui alloue (malloc) un tableau d'entiers de taille égale à len, puis à l'aide des fonction sscanf() et getc(), récupère les entiers un après l'autre depuis le buffer buf (en supposant que buf devrais contenir une liste d'entiers séparés par des virgules) et les stocke dans le tableau précédemment alloué. La fonction iparse() doit retourner le pointeur sur le tableau alloué ou bien NULL dans le cas où malloc() échoue ou bien la chaine est mal formaté (n'est pas une liste d'entiers séparés par des virgules) - Écrire une fonction iorder(int *tab, int len) qui trie un tableau d'entiers (len est le nombre d’entiers) par ordre croissant puis l'affiche à l'écran Créez un fichier sorder.c qui contient deux fonction sparse() et sorder() similaire aux fonctions précédentes mais s'appliquant sur une liste de nom. Vous devez définir les paramètre d'appels et de retours de ces fonctions Créez les fichiers acquire.h iorder.h sorder.h qui contiennent les déclaration des fonctions précédentes ainsi que les constantes utilisées. Créez un fichier order.c qui contient la boucle principale du programme. A chaque itération un choix doit être affiché à l'écran pour demander à l'utilisateur s'il veut trier des entiers ou bien des noms puis les fonctions précédentes doivent être utilisées pour réaliser les actions nécessaires. L'utilisateur doit aussi avoir la possibilité de stopper le programme (sortir de la boucle). Ecrire un makefile pour builder les trois exercices de ce TP Remarque: On peut faire une analogie entre ce programme et un programme de traitement de signal où l'on retrouve souvent les étapes "acquire", "filter-parse" et "calcul output-order" avec une boucle principale qui se répète périodiquement ESME - Programmation Système sous Linux - A.TALBI