langage BCPL langage B C K&R C ANSI (C89) C99



Documents pareils
GESTION DES FICHIERS C/UNIX

Dans le chapitre 1, nous associions aux fichiers ouverts des descripteurs de fichiers par lesquels nous accédions aux fichiers.

Programmation système de commandes en C

Programmation système I Les entrées/sorties

Le système de gestion des fichiers, les entrées/sorties.

Programmation impérative

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

Introduction au langage C

Cours 6 : Tubes anonymes et nommés

Le Langage C Version 1.2 c 2002 Florence HENRY Observatoire de Paris Université de Versailles florence.henry@obspm.fr

SUPPORT DE COURS. Langage C

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

Seance 2: En respectant la méthode de programmation par contrat, implémentez les autres fonctions de jeu.

Algorithmique et Programmation, IMA

Conventions d écriture et outils de mise au point


Cours Langage C/C++ Programmation modulaire

Compression de Données - Algorithme de Huffman Document de Conception

Les chaînes de caractères

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

Rappels Entrées -Sorties

INITIATION A LA PROGRAMMATION

Les fichiers. Chapitre 4

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Programmation en langage C

Chapitre 1 : La gestion dynamique de la mémoire

Programmation C. J.-F. Lalande. 15 novembre 2012

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Cours Programmation Système

Bases de programmation. Cours 5. Structurer les données

Programmation en langage C Eléments de syntaxe

Langage C. Patrick Corde. 22 juin Patrick Corde ( Patrick.Corde@idris.fr ) Langage C 22 juin / 289

Le langage C. Séance n 4

Cours de Système : Gestion de Fichiers

IN Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

Chap III : Les tableaux

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

UE C avancé cours 1: introduction et révisions

SYSTÈME DE GESTION DE FICHIERS

3IS - Système d'exploitation linux - Programmation système

Travaux pratiques. Compression en codage de Huffman Organisation d un projet de programmation

Les structures. Chapitre 3

BTS IRIS Cours et Travaux Pratiques. Programmation C. A. Lebret, TSIRIS, Lycée Diderot, 1995/06. en conformité avec le référentiel du BTS IRIS

Brefs rappels sur la pile et le tas (Stack. / Heap) et les pointeurs

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

Programmation système en C/C++

SYSTÈME DE GESTION DE FICHIERS SGF - DISQUE

Cours de C/C++ par la pratique. Hugues Talbot

DE L ALGORITHME AU PROGRAMME INTRO AU LANGAGE C 51

Cours de programmation avancée. Le langage C. Université du Luxembourg

Algorithmique & Langage C IUT GEII S1. Notes de cours (première partie) cours_algo_lgc1.17.odp. Licence

COMPARAISONDESLANGAGESC, C++, JAVA ET

Langage Éric Guérin 5 octobre 2010

Initiation à la programmation en Python

MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES. Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année

Pensez à vous inscrire... si ce n est pas encore fait

Quelques éléments de compilation en C et makefiles

V- Manipulations de nombres en binaire

Les débordements de tampons et les vulnérabilités de chaîne de format 1

Le langage C. Introduction, guide de reference

Cours d initiation à la programmation en C++ Johann Cuenin

Programmation en C. École Nationale Supérieure de Techniques Avancées. Pierre-Alain Fouque et David Pointcheval

Anis ASSÈS Mejdi BLAGHGI Mohamed Hédi ElHajjej Mohamed Salah Karouia

Le langage C++ est un langage de programmation puissant, polyvalent, on serait presque tenté de dire universel, massivement utilisé dans l'industrie

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

Introduction à la programmation orientée objet, illustrée par le langage C++ Patrick Cégielski

Programmation C++ (débutant)/instructions for, while et do...while

INF 321 : mémento de la syntaxe de Java

UE Programmation Impérative Licence 2ème Année

Le prototype de la fonction main()

Le Langage C Licence Professionnelle Qualité Logiciel Pr. Mouad BEN MAMOUN ben_mamoun@fsr.ac.ma Année universitaire 2011/2012

Plan du cours. Historique du langage Nouveautés de Java 7

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

I. Introduction aux fonctions : les fonctions standards

Notions fondamentales du langage C# Version 1.0

Claude Delannoy. 3 e édition C++

Gestion de la mémoire

Introduction à MATLAB R

Introduction à l algorithmique et à la programmation M1102 CM n 3

Programmation Structurée en Langage C

Programmation impérative

Initiation. àl algorithmique et à la programmation. en C

Programmation en langage C d un µcontrôleur PIC à l aide du compilateur C-CCS Sommaire

Cours de C. Petits secrets du C & programmation avancée. Sébastien Paumier

INF111. Initiation à la programmation impérative en C amini/cours/l1/inf111/ Massih-Reza Amini

Cours 14 Les fichiers

Notes du cours 4M056 Programmation en C et C++ Vincent Lemaire et Damien Simon

Les structures de données. Rajae El Ouazzani

Cours Informatique Master STEP

Travaux Dirigés n 1 : chaînes de caractères

Analyse de sécurité de logiciels système par typage statique

INF 104 (SELC) Introduction au langage C

Programmation Classique en langage C

MICROINFORMATIQUE NOTE D APPLICATION 1 (REV. 2011) ARITHMETIQUE EN ASSEMBLEUR ET EN C

Cours de C. Allocation dynamique. Sébastien Paumier

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre Enrica.Duchi@liafa.jussieu.fr

TRAVAUX PRATIQUES Programmation Système Langage C / Système UNIX. 2 e année Génie Informatique

Transcription:

Formation langage C

langage BCPL Ken Thompson langage B C K&R Dennis Ritchie C ANSI (C89) Brian Kernighan C99

Unix, Linux et autres noyaux très nombreux programmes langage C majeure partie des bibliothèques majeure partie des programmes système

Chapitre 0 : de l'obscurité au binaire bash langage interprété C langage compilé fichier texte programme programme

Avantages : un code incorrect ne compile pas grande rapidité d'exécution Fractal Benchmark (source : http://www.timestretch.com/fractalbenchmark.html)

Hello world! le mauvais exemple : exemple.c délimitent la fonction void main (void) { printf ("bonjour le monde!\n"); } main () fonction obligatoire, première à être exécutée void ici, signifie «rien» printf() fonction affichant du texte les instructions sont terminées par des points virgules

Compilation gcc exemple.c -o exemple -Wall compilateur source C programme binaire affiche les avertissements option pour spécifier le nom exemple.c:1: attention : return type of 'main' is not 'int' exemple.c: In function 'main': exemple.c:3: attention : implicit declaration of function 'printf' exemple.c:3: attention : incompatible implicit declaration of built-in function 'printf'

Processus fonctions doit retourner une valeur à l'os doit être déclarée (fichier d'en-tête) contient la déclaration de printf() type de retour = int = entier #include <stdio.h> int main (void) { printf ("bonjour le monde!\n"); return (0); } retourne la valeur 0

Les variables langage C langage typé chaque variable a un type type description propriétés int nombre entier (32 bits) signé de -2 31 à 2 31-1 char nombre entier (8 bits) signé, caractère de -2 7 à 2 7-1 unsigned int nombre entier (32 bits) non signé de 0 à 2 32-1 unsigned char nombre entier (8 bits) signé de 0 à 2 8-1 float double nombre réel (flottant) simple précision nombre réel (flottant) double précision Déclaration : type nom_variable;

Exemple : int main (void) { int ma_variable; int autre_variable; autre_variable = 36 + 1; ma_variable = autre_variable + 5; déclarations de variables affectations } return (0); déclarations : au début des fonctions

Affichage des variables printf ("contenu de la variable : %d\n", ma_variable); indique : ma_variable = entier arguments séparés par des virgules printf ("var1: %d, var2: %d\n", ma_variable, autre_variable);

Exemple complet : #include <stdio.h> int main (void) { int ma_variable; int autre_variable; } autre_variable = 36 + 1; ma_variable = autre_variable + 5; printf ("var1: %d, var2: %d\n", ma_variable, autre_variable); return (0); [thaeron@synapse Sources]$ gcc tmp.c o tmp Wall [thaeron@synapse Sources]$./tmp var1: 42, var2: 37

Les autres opérateurs arithmétiques Opérateur Description + addition - soustraction * multiplication / division % modulo (reste de la division entière)

Structures conditionnelles Le if Exécute instructions si la condition est VRAIE if (condition) { instructions à exécuter si la condition est vraie; } else { instructions à exécuter si la condition est fausse; }

Opérateurs de comparaison Opérateurs valeur1 == valeur2 valeur1!= valeur2 valeur1 < valeur2 valeur1 <= valeur2 valeur1 > valeur2 Conditions Vraie si valeur1 et valeur2 sont égales Vraie si valeur1 n'est pas égale à valeur2 Vraie si valeur1 est inférieure à valeur2 Vraie si valeur1 est inférieure ou égale à valeur2 Vraie si valeur1 est supérieure à valeur2 valeur1 >= valeur2 #include <stdio.h> Vraie si valeur1 est supérieure ou égale à valeur2 int main (void) { int var1; var1 = 10; if (var1 == 10) { printf ("var1 est égale à 10\n"); } return (0); }

boucle while exécute des instructions TANT QUE la condition est vraie. while (condition) { instructions; } #include <stdio.h> commentaire int main (void) { int var; var = 0; /* exécuter la boucle tant que var est plus petite que 10 */ while (var < 10) { printf ("var = %d\n", var); var = var + 1; } return (0); }

Commentaires type C /* commentaire */ type C++ // commentaire Fin du chapitre 0

L'art du code avant de coder réfléchir organigramme idées structurées griffonnage IDENTER le code avant de compiler exécuter mentalement le code

Les variables type description propriétés int nombre entier (32 bits) signé de -2 31 à 2 31-1 char nombre entier (8 bits) signé, caractère de -2 7 à 2 7-1 unsigned int nombre entier (32 bits) non signé de 0 à 2 32-1 unsigned char nombre entier (8 bits) signé de 0 à 2 8-1 short int nombre entier (16 bits) signé de -2 15 à 2 15-1 unsigned short int long int long long int unsigned long int nombre entier (16 bits) non signé nombre entier (32 ou 64 bits) signé nombre entier (64 bits) signé nombre entier (32 ou 64 bits) non signé de 0 à 2 16-1 de -2 63 à 2 63-1 unsigned long long int nombre entier (64 bits) non signé de 0 à 2 64-1

Déterminer la taille sizeof (type); ou sizeof (variable); #include <stdio.h> int main (void) { char variable; printf ("int = %d octets\n", sizeof (int)); printf ("taille variable = %d octets\n", sizeof (variable)); } return (0); [thaeron@synapse Sources]$./tmp int = 4 octets taille variable = 1 octets

Déclaration et portée de variable Portée de fonction int main (void) { int ma_var; /* le reste du code ici */ } Variable globale, portée de programme #include <stdio.h> int ma_var; int main (void) { /* le code ici */ }

Variable de bloc, portée de bloc while (1 == 1) { int ma_var; } ma_var n'existe plus hors de la boucle

Déclarations de variables type nom_variable; type nom_variable1, nom_variable2, nom_variable3; type nom_variable1 = valeur1, nom_variable2 = valeur2; Elles ont toutes le mêmes type.

Les constantes Par défaut constante numérique int 42 int 42.0 double 42L long int 0x42 int (hexadécimal (base 16)) 042 int (octal (base 8))

Écriture sortie standard printf() peut afficher beaucoup de types de variables Séquence Type Format d'affichage %d (ou %i) int (short int, char) nombre entier %u unsigned (int, short int, char) nombre entier %c char caractères ASCII %ld long int nombre entier %lld long long int nombre entier %f float nombre réel %lf double nombre réel %e double notation scientifique %x int (short int, char) hexadécimal %s tableau de char chaîne de caractères %p adresse mémoire hexadécimal

Modificateurs affichage de 3 chiffres après la virgule printf ("%.3f\n", 3.1415926536); Exécution : 3.142

ASCII caractère = nombre 0 nombre 127 = caractère

Un caractère se marque entre apostrophes. Exemple Caractère A 'A' Exercices : Mettez le caractère ASCII 4 dans une variable de type char et affichez la valeur entière qui lui correspond en utilisant printf(). Mettez la valeur 50 dans une variable de type char et affichez le caractère ASCII qui lui correspond en utilisant printf().

Lecture de l'entrée standard scanf() lit saisie clavier, place dans une variable mêmes «séquences» que printf() Exemple : int mon_entier; scanf ("%d", &mon_entier); ne pas oublier le &

Exercices : Affichez le message «entrez un nombre entier :» puis le récupérer dans une variable en utilisant scanf(). Affichez la valeur puis affichez le message «nombre négatif» si la valeur est inférieur à 0 et le message «nombre positif» si la valeur est supérieure ou égale à 0. Utilisez dans un premier temps 2 if, puis un seul.

Les boucles boucle while exécute TANT QUE la condition est vraie initialisation variable de la condition; while (condition) { instructions; opération de fin de boucle; } int var = 0; while (var < 10) { /* instructions; */ var = var + 1; }

boucle for condensé de la boucle while for (initialisation; condition; opération de fin de boucle) { instructions; } int var; for (var = 0; var < 10; var = var + 1) { /* instructions; */ }

boucle do while comme while, mais exécute au moins 1 itération de la boucle do { instructions; } while (condition); Opérations sur les boucles mots clés : continue; revient à la condition break; sort de la boucle

Exercice : Écrivez un programme qui lit sur l'entrée standard un nombre entier N, puis qui affiche sur la première ligne une étoile, sur la deuxième ligne deux étoiles, etc, jusqu'à la Nième ligne. Exemple pour N = 4 : * ** *** ****

L'incrémentation var = var + 1; var++; post-incrémentation ++var; pré-incrémentation post-incrémentation = faible priorité, s'exécute après les autres instructions pré-incrémentation = grande priorité, s'exécute avant les autres instructions int variable = 42; printf ("variable = %d\n", variable++); printf ("variable = %d\n", variable); printf ("variable = %d\n", ++variable);

L'incrémentation var = var + 1; var++; post-incrémentation ++var; pré-incrémentation post-incrémentation = faible priorité, s'exécute après les autres instructions pré-incrémentation = grande priorité, s'exécute avant les autres instructions int variable = 42; printf ("variable = %d\n", variable++); printf ("variable = %d\n", variable); printf ("variable = %d\n", ++variable); lors de l'exécution : variable = 42 variable = 43 variable = 44

Les tableaux Ensemble de variables de mêmes types, consécutives en mémoire, et accessibles par un seul nom. Déclaration : type nom_tableau[nombre_de_valeurs]; nombre d'éléments du tableau C89 = constante obligatoire C99 = peut être une variable Exemple : int tableau_dentiers[10];

Pour accéder au Nième+1 élément du tableau nom_tableau[n] Pour accéder au 1er élément du tableau nom_tableau[0] Il est possible d'utiliser une variable en tant qu'indice : int indice = 12; int tableau_dentiers[14]; tableau_dentiers[indice] = 42; ATTENTION un tableau n'est pas automatiquement initialisé l'indice ne doit JAMAIS est supérieur à la taille du tableau

Initialisation lors de la déclaration type nom_tableau[taille] = {valeur1, valeur2, valeur3, valeur4}; Si TAILLE n'est pas indiquée, elle sera calculée par le nombre de valeurs d'initialisation. Exercice : Écrivez un programme, en utilisant un tableau, qui affiche les 10 premiers nombres de la suite de Fibonacci. La suite de Fibonacci est une suite très simple où chaque élément est la somme des 2 éléments qui sont avant lui. Le tout premier élément valant 0 et le deuxième valant 1. Si votre programme fonctionne il devrait afficher cette suite : 0 1 1 2 3 5 8 13 21 34

Chaînes de caractères tableau de variables de type char toujours terminé par le caractère nul '\0' (de valeur 0). Déclaration : char ma_chaine[taille_chaine + 1]; Déclaration et initialisation : pour le caractère nul char ma_chaine[10] = "ma chaine"; 9 caractères de «ma chaine» + 1 caractère pour le caractère nul. Le tableau à une taille de 10 caractères. Une chaîne est toujours entre apostrophes. char ma_chaine[20] = "ma chaine"; Idem, mais le tableau à une taille de 20 caractères.

Déclaration sans indiquer la taille char ma_chaine[] = "ma chaine"; La taille est déterminée par la longueur de «ma chaine» ATTENTION : Il est totalement interdit de faire directement une affectation d'une chaîne en dehors de la déclaration.

Fonctions de manipulation de chaînes Fonction strlen (chaine); strcpy (destination, source); strcat (destination, source); strcmp (chaine1, chaine2); strcasecmp (chaine1, chaine2); Comportement Renvoie la taille de chaine. Recopie la source dans la destination. Ajoute la source à la fin de la destination. Renvoie 0 si les deux chaînes sont identiques. Renvoie 0 si les deux chaînes sont identiques sans tenir compte de la casse. Fonctions déclarées dans le fichier d'en-tête string.h #include <string.h>

Fonction reprenant le comportement de strlen() #include <stdio.h> #include <string.h> int main (void) { int taille = 0; char chaine[] = "ceci est ma chaine"; while (chaine[taille]!= '\0') { taille++; } printf ("longueur de la chaine : %d\n", taille); } return (0);

Exercice : Essayez chacune des fonctions, et écrivez un programme qui reproduit le comportement de chacune de ces fonctions sauf strcasecmp(). Fonction sprintf() printf() dans une chaîne de caractères Exemple : float mon_flottant = 3.14; sprintf (ma_chaine, "flottant = %f", mon_flottant); Exercice : Écrivez un programme qui demande la saisie d'un nombre entier, puis convertissez le en chaîne de caractères avec sprintf() et affichez chaque chiffre qui compose ce nombre.

Le if plus complet forme simple if (condition) { /* instructions si la condition est vraie; */ } else { /* instructions si la condition est fausse; */ } Évaluation de plusieurs conditions Opérateur CONDITION1 && CONDITION2 CONDITION1 CONDITION2 Condition exprimée Si CONDITION1 ET CONDITION2 sont vraies Si CONDITION1 OU CONDITION2 sont vraies

Exemple : if ((valeur1 > 0 && valeur1 < 100 && valeur2 == 0) (valeur1 > 50 && valeur2 == 1)) VRAI si : valeur1 est positive, inférieure à 100 et que valeur2 vaut 0 ou si valeur1 est supérieure à 50 et valeur2 vaut 1 Exercice : Écrivez un programme qui converti en minuscule (ou en majuscule) une chaîne de caractères pouvant contenir n'importe quel caractère imprimable de la table ASCII. Il faudra donc traiter uniquement les caractères allant de A à Z (ou de a à z) et ne pas modifier les autres.

Chaînage des if if (condition1) { /* si condition1 est vraie */ } else if (condition2) { /* si condition1 est fausse et que condition2 est vraie */ } else if (condition3) { /* si condition1 et condition2 sont fausses et que condition3 est vraie */ } else { /* si toutes les précédentes conditions sont fausses */ }

Bloc à 1 seule instruction possibilité d'enlever les accolades if (condition) { instruction1; /* seule instruction affectée à if */ } instruction2; équivalent à if (condition) instruction1; /* seule instruction affectée à if */ instruction2;

Tests d'un entier switch / case switch (variable) { case valeur1: /* instructions si variable == valeur1 */ break; case valeur2: /* instructions si variable == valeur2 */ break; case valeur3: /* instructions si variable == valeur3 */ break; default: /* instructions si variable n'est égale à aucune des valeurs */ }

Exercice : Écrivez un programme qui demande d'entrer 2 nombres réels et un opérateur (+,-,*,/), puis utilisez switch / case pour effectuer l'opération et affichez le résultat. Demandez à l'utilisateur s'il désire recommencer ou s'il veut quitter le programme.

Tableaux à 2 dimensions Ensemble de tableaux consécutifs en mémoire Déclaration : type nom_tableau[nb_tableaux][taille_tableaux]; On y accède par des indices : tab[0] désigne le premier tableau tab[1][0] désigne le premier élément du 2ème tableau

Exemple tableau de chaînes : #include <stdio.h> #include <string.h> int main (void) { char tab[4][8]; strcpy (tab[0], "ceci"); strcpy (tab[1], "est"); strcpy (tab[2], "un"); strcpy (tab[3], "exemple"); } return (0);

Envoi de paramètres au programme int main (void) int main (int argc, char *argv[]) nombre de paramètres tableau de chaînes contenant les paramètres Exercice : Écrivez un programme qui affiche, par ligne, un paramètre et son numéro.

Transtypage (type cast) permet le changement de type d'une expression ou d'une variable transtypage implicite int mon_int; char mon_char = 40; mon_int = mon_char + 2; mon_char est converti automatiquement en int transtypage explicite float flottant = 3.1415; printf ("%d\n", (int) flottant); à l'exécution : 3

Sans transtypage explicite : float flottant = 3.1415; printf ("%d\n", flottant); à l'exécution : 1117209992 transtypage préfixer l'expression par le nouveau type entre parenthèses

Manipulation de bits Opérateur var1 & var2 var1 var2 var1 ^ var2 var1 << n var1 >> n ~var1 Comportement ET bit à bit de var1 et var2 OU bit à bit de var1 et var2 XOR (OU Exclusif) bit à bit de var1 et var2 Décalage de n bits vers la gauche de var1 Décalage de n bits vers la droite de var1 Inversion de chaque bit de var1

Exemples unsigned char var1 = 0xAA; /* en binaire 10101010 */ unsigned char var2 = 0x55; /* en binaire 01010101 */ unsigned char resultat; resultat = var1 & var2; /* resultat vaudra 0 */ resultat = var1 var2; /* resultat vaudra 0xFF */ resultat = var1 ^ var2; /* resultat vaudra 0xFF */ resultat = var1 << 4; /* resultat en binaire 10100000 */ resultat = 0xFF << 2; /* resultat en binaire 11111100 */ resultat = 0xFF >> 2; /* resultat en binaire 00111111 */ resultat = ~0xFF; /* resultat vaudra 0 */ resultat = ~var1; /* resultat vaudra 0x55 */

2 manipulations très utilisées Mettre à 1 dans resultat les bits à 1 de mask : resultat = resultat mask; Mettre à 0 dans resultat les bits à 1 de mask : resultat = resultat & (~mask); Exercice : Écrivez un programme qui demande à l'utilisateur d'entrer un nombre entier de 8 bits. Puis utilisez les manipulations de bits pour afficher le nombre en binaire (base 2).

Les fonctions Déclaration (prototype) type nom_fonction (type premier_argument, type deuxieme_argument); pas d'arguments void Exemple : type nom_fonction (void); pas de valeur de retour type void fichier d'en-tête (.h) contient principalement des prototypes.

Création de la fonction type nom_fonction (type premier_argument, type deuxieme_argument) { /* instructions; */ } Exemple, fonction valeure_absolue #include <stdio.h> float valeur_absolue (float nombre); /* prototype */ float valeur_absolue (float nombre) /* fonction */ { if (nombre > 0.0) return (nombre); else return (nombre * -1.0); }

Suite de l'exemple : int main (void) { float resultat; resultat = valeur_absolue (-12.0); printf ("%f\n", resultat); } return (0);

La mauvaise idée #include <stdio.h> void inc (int valeur) { valeur++; } int main (void) { int valeur; valeur = 5; inc (valeur); printf ("valeur vaut %d\n", valeur); } return (0);

Passage d'arguments = passage par valeur = création de nouvelles variables Cas des tableaux on préfère utiliser type *nom_tableau à type nom_tableau[] Exemple : void fibonacci (int *tableau, int taille); au lieu de void fibonacci (int tableau[], int taille);

Exercices : Reprenez le programme de la mini calculatrice, laissez les messages et les scanf() dans le main() et mettez le code principal, celui qui effectue l'opération selon l'opérateur, dans une fonction qui retournera le résultat, dont vous afficherez le résultat dans la fonction main(). Reprenez le programme qui copie le comportement de la fonction strcpy() et mettez ce code dans une fonction. Écrivez un programme comme «inc» de l'exemple mais qui modifie une variable globale (à portée de programme), et vérifiez dans le main que la valeur est bien modifiée.

Les pointeurs Un pointeur est une variable spéciale permettant d'être placée à n'importe quel endroit de la mémoire, ainsi elle permet de partager l'espace mémoire avec une autre variable et donc d'accéder à son contenu qui peut être lu et écrit. Déclaration : type *nom_pointeur; Propriétés nom_pointeur *nom_pointeur &nom_pointeur adresse mémoire pointée contenu de l'adresse mémoire pointée adresse mémoire propre au pointeur

Propriétés d'une variable «normale» type nom_variable; nom_variable &nom_variable contenu de la variable adresse mémoire de la variable Faire pointer un pointeur sur une variable nom_pointeur = &nom_variable; Si nom_pointeur pointe sur nom_variable : nom_variable = VALEUR; est équivalent à *nom_pointeur = VALEUR;

Exemple concret : #include <stdio.h> int main (void) { int variable = 40; int *pointeur; pointeur = &variable; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); *pointeur = 42; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); variable = 13; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); } return (0);

Exemple concret : #include <stdio.h> int main (void) { int variable = 40; int *pointeur; pointeur = &variable; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); *pointeur = 42; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); variable = 13; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); } return (0);

Exemple concret : #include <stdio.h> int main (void) { int variable = 40; int *pointeur; contenu de l'adresse pointée 40 contenu de la variable 40 contenu de l'adresse pointée 42 contenu de la variable 42 contenu de l'adresse pointée 13 contenu de la variable 13 pointeur = &variable; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); *pointeur = 42; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); variable = 13; printf ("contenu de l'adresse pointée %d\n", *pointeur); printf ("contenu de la variable %d\n", variable); } return (0);

Exercice : Reprenez le programme d'exemple «inc» et utilisez un pointeur de type int en argument afin de faire fonctionner correctement le programme. N'utilisez pas l'incrémentation ++, utilisez à la place l'opération développée.

Opérations sur les pointeurs tableau = pointeur int nom_tableau[10]; nom_tableau est un pointeur. On peut faire : int *pointeur; int nom_tableau[10]; pointeur = nom_tableau; pointeur[0] est équivalent à nom_tableau[0]

pointeur[0] est équivalent à nom_tableau[0] Ce qui est équivalent à : *(pointeur + 0) donc à *pointeur nom_tableau[0] pointeur[0] *pointeur nom_tableau[1] pointeur[1] *(pointeur + 1) nom_tableau[n] pointeur[n] *(pointeur + n)

pointeur++; incrémentation de l'adresse pointée On reprend : int *pointeur; int nom_tableau[10]; pointeur = nom_tableau; *pointeur pointeur++; *pointeur pointeur++; *pointeur pointeur[0] pointeur[0] pointeur[0] nom_tableau[0] nom_tableau[1] nom_tableau[2]

Exercice : Reprenez la fonction que vous avez écrite et qui reproduit le comportement de strcpy() et utilisez la notation des pointeurs et non plus celle des tableaux. Reprenez votre code qui reproduit le comportement de strcat(), mettez le dans une fonction et utilisez la notation des pointeurs au lieu de celle des tableaux.

Arithmétique des pointeurs On reprend : int *pointeur; int nom_tableau[10]; pointeur = nom_tableau; *(pointeur + 1) nom_tableau[1] Or un int a une taille de 4 octets le + 1 est automatiquement multiplié par 4 Généralisation : les opérations sont multipliées par sizeof (type)

Exercices : Reproduisez l'incrémentation d'un pointeur pour 2 types différents et affichez les adresses mémoire du pointeur avant et après l'incrémentation, vérifiez que c'est bien multiplié par la taille du type, on utilisera la «séquence» de format %p dans printf() ce qui affiche en hexadécimal une adresse mémoire. En utilisant un pointeur de type unsigned char sur une variable de type unsigned int à laquelle vous aurez affecté la valeur 0x11223344, affichez en hexadécimal les valeurs des 4 octets qui composent la variable.

Endianness (boutisme) Résultat de l'exercice précédent : variable = 0x11223344; En mémoire on a : 0x44 0x33 0x22 0x11 architecture little endian (petite boutiste) l'ordre des bits dans les octets est conservé les octets sont écrits «à l'envers», les octets de poids faibles sont en premier Architecture big endian (grand boutiste) en mémoire on aurait : 0x11 0x22 0x33 0x44

Allocation dynamique Permet de réserver de la mémoire, par exemple pour créer des tableaux dont la taille peut changer au cours de l'exécution. allocation de mémoire réserve de la mémoire à une adresse pointeur Fonction (prototype) void *malloc (size_t taille); void *realloc (void *pointeur, size_t taille); void free (void *pointeur); Comportement Alloue taille octets et renvoie un pointeur sur la mémoire allouée. Change la taille allouée au pointeur et renvoie le nouveau pointeur. Libère l'espace mémoire alloué au pointeur. Fichier d'en-tête : stdlib.h

void *malloc (size_t taille); pointeur void = pointeur de tout type taille en octets à réserver size_t unsigned long int malloc() renvoie l'adresse (= le pointeur) de la mémoire allouée ou NULL en cas d'échec adresse mémoire 0 void *realloc (void *pointeur, size_t taille); nouvelle adresse mémoire pointeur sur la mémoire à réallouer (ou NULL) nouvelle taille voulue en octets

void free (void *pointeur); #include <stdlib.h> #include <string.h> #include <stdio.h> adresse mémoire dont la mémoire doit être libérée int main (void) { char *chaine; /*on alloue 10 octets */ chaine = malloc (10 * sizeof(char)); strcpy (chaine, "exemple"); printf ("contenu du pointeur : %s\n", chaine); /* on réduit la taille de la chaîne à 8 octets */ chaine = realloc (chaine, 8 * sizeof(char)); printf ("contenu du pointeur : %s\n", chaine); } /* on libère la mémoire allouée */ free (chaine); return (0);

Valgrind permet de vérifier l'utilisation de la mémoire et des pointeurs. [thaeron@syndrome Sources]$ valgrind programme ==30997== Memcheck, a memory error detector ==30997== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==30997== Using Valgrind-3.6.0.SVN and LibVEX; rerun with -h for copyright info ==30997== Command: programme ==30997== contenu du pointeur : exemple contenu du pointeur : exemple ==30997== ==30997== HEAP SUMMARY: ==30997== in use at exit: 0 bytes in 0 blocks ==30997== total heap usage: 2 allocs, 2 frees, 18 bytes allocated ==30997== ==30997== All heap blocks were freed -- no leaks are possible ==30997== ==30997== For counts of detected and suppressed errors, rerun with: -v ==30997== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

Exercice : Reprenez le programme que vous avez écrit qui calcule la suite de Fibonacci, demandez à l'utilisateur combien de nombres de la suite le programme doit calculer. Utilisez l'allocation dynamique pour créer le tableau de type int de la bonne taille et procédez au calcul et à l'affichage des valeurs. Demandez à l'utilisateur s'il souhaite recommencer, ne libérez la mémoire que s'il souhaite quitter le programme, sinon utilisez realloc(). Écrivez une fonction qui prend 2 chaînes en arguments, et qui renvoie une chaîne dynamique contenant la concaténation des 2 chaînes passées en arguments.

Tableau à 2 dimensions dynamiques type tab[x][y]; type **tab;

Exercice : Écrivez un programme qui crée un tableau dynamique de chaînes dynamiques (cela se déclare ainsi : char **nom_tableau;) pour recopier les paramètres passés au programme (argv).

Les structures Ensemble de variables de types différents que l'on regroupe. Créer une structure revient à créer un nouveau type de variable contenant plusieurs variables. Déclaration de la structure struct nom_structure { type variable1; type variable2; }; Déclaration d'une variable du type de la structure struct nom_structure nom_variable;

#include <stdio.h> #include <string.h> struct structure_personne { int age; char nom[20]; }; int main (void) { struct structure_personne moi; moi.age = 25; strcpy (moi.nom, "Damien"); Accéder aux variables qui composent la structure : nom_variable.variable1 nom_variable.variable2 printf ("Je suis %s et j'ai %d ans\n", moi.nom, moi.age); } return (0);

Exemple de tableau de structure : int main (void) { int i; struct structure_personne personne[2]; personne[0].age = 25; strcpy (personne[0].nom, "Damien"); personne[1].age = 33; strcpy (personne[1].nom, "Jack"); for (i = 0; i < 2; i++) { printf ("%s, age : %d ans\n", personne[i].nom, personne[i].age); } } return (0); A l'exécution on a : Personne 0 : Damien, age : 25 ans Personne 1 : Jack, age : 33 ans

Pointeur de structure L'utilisation est la même qu'une structure sauf qu'on utilise -> et non. pour accéder aux variables membres. void remplir_pers (struct structure_personne *gens, char *nom, int age) { strcpy (gens->nom, nom); gens->age = age; } int main (void) { struct structure_personne personne; remplir_pers (&personne, "Damien", 25); printf ("%s, age : %d ans\n", personne.nom, personne.age); } return (0);

Problématique des tableaux de structures

Les listes chaînées structure contenant un pointeur du type de la structure permettant de pointer sur l'élément suivant, formant ainsi une chaîne de structures.

Suppression d'un élément de la chaîne cas 1 : l'élément n'est pas le premier élément

cas 2 : l'élément est le premier élément

Exercices : Reprenez ce code d'exemple et ajoutez des variables membres pour stocker un age et un nom. Écrivez une fonction permettant de lire ces données depuis la saisie clavier de l'utilisateur et de les ajouter dans un maillon. Écrivez une fonction qui parcours toute la chaîne et qui affiche les données de chaque maillon. Écrivez une fonction permettant de récupérer l'adresse d'un maillon à partir d'un nom. Écrivez une fonction qui permet de supprimer (en utilisant la fonction de suppression de l'exemple) un maillon à partir d'un nom. Écrivez une fonction qui supprime toute la chaîne (en libérant la mémoire). Ajoutez un menu permettant à l'utilisateur de choisir entre : ajouter un élément, afficher la liste, supprimer un élément, rechercher un élément ou quitter. Traitez le choix de l'utilisateur en utilisant les fonctions que vous avez créé précédemment.

Liste doublement chaînée struct liste_double { /* variables membres */ struct liste_double *precedent; struct liste_double *suivant; }; Suppression d'un maillon psuppr->precedent->suivant = psuppr->suivant; psuppr->suivant->precedent = psuppr->precedent;

Fonctions à nombre variable d'arguments Déclaration type nom_fonction (type argument1,...); Fonction void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); Comportement Crée une liste de type va_list des paramètres. Permet d'accéder aux paramètres un à un. Clos la liste de type va_list des paramètres. déclarées dans stdarg.h

void exemple_param_variable (int quantite,...) { int temp, i; va_list liste_param; /* la fonction va_start() prend en premier argument une liste de type va_list et en surtout en deuxième argument le premier argument, de notre fonction, qui va servir de point de repère. */ va_start (liste_param, quantite); for (i = 0; i < quantite; i++) { /* on indique le type de notre argument à récupérer est int */ temp = va_arg (liste_param, int); printf ("%d ", temp); /* on affiche notre entier */ } printf ("\n"); } va_end (liste_param); /* ne jamais oublier de clore cette liste */

suite de l'exemple int main (void) { exemple_param_variable (6, 0, 1, 2, 3, 4, 5); exemple_param_variable (3, 13, 42, 666); } return (0); Exercice : Écrivez une fonction ressemblant à printf(). Votre fonction prendra en premier argument une chaîne indiquant les types des variables à récupérer (s pour chaine, c pour caractère et d pour entier), selon l'exemple suivant, que vous afficherez. Exemple d'appel de votre fonction : votre_fonction ("ddsc", 13, 42, "test", 'B'); Ce qui devra afficher : entier : 13 entier : 42 chaine : test caractère : B

Remontée des erreurs la méthode choisie dans la bibliothèque standard est une variable globale nommée errno Cette variable contient le dernier code d'erreur écrit par la plupart des fonctions standards de la bibliothèque C. Ces codes sont des valeurs entières. définie dans errno.h Pour le transformer en message pour humain on a 2 fonctions : void perror (const char *s); Affiche le message correspondant à la dernière erreur, préfixé par le contenu de la chaîne s char *strerror (int errnum); Renvoie une chaîne correspondant au code d'erreur passé en argument (souvent la variable errno).

Manipulation de fichiers 2 méthodes méthode d'accès direct fonctions bufferisées (= avec tampon) Méthode d'accès direct Fonctions open read write lseek close Rôle Ouvre un fichier et renvoi son descripteur. Permet de lire des données d'un fichier. Permet d'écrire des données dans un fichier. Permet de se positionner dans un fichier. Ferme un fichier. #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>

open int open(const char *nom_fichier, int flags, mode_t modes); descripteur de fichier ou -1 en cas d'erreur nom du fichier mode d'ouverture permissions du fichier O_RDONLY O_WRONLY O_RDWR Ouverture en lecture seulement Ouverture en écriture seulement Ouverture en lecture et écriture Modes supplémentaires utilisables avec le OU logique O_CREAT Crée le fichier s'il n'existe pas déjà O_TRUNC Écrase le fichier s'il existe déjà (taille tronquée à 0) Permissions S_IRWXU (700 lecture / écriture / exécution owner) owner : S_IRUSR (lecture), S_IWUSR (écriture), S_IXUSR (exécution) groupe: S_IRGRP (lecture), S_IWGRP (écriture), S_IXGRP (exécution) autre : S_IROTH (lecture), S_IWOTH (écriture), S_IXOTH (exécution)

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <errno.h> int main (void) { int descripteur; /* on va ouvrir le fichier en lecture écriture et le créer si nécessaire */ descripteur = open ("monfichier.txt", O_RDWR O_CREAT, S_IRWXU); if (descripteur == -1) { perror ("open a échoué :"); return (-1); }

read ssize_t read(int fd, void *buffer, size_t count); nombre d'octets lus ou -1 en cas d'erreur descripteur pointeur ou tableau pour stocker les données nombre d'octets à lire int octets_lus; char tampon[101]; do { octets_lus = read (descripteur, tampon, 100); if (octets_lus > 0) { tampon[octets_lus] = '\0'; printf ("%s", tampon); } } while (octets_lus > 0);

write ssize_t write(int fd, const void *buffer, size_t count); nombre d'octets écrits ou -1 en cas d'erreur descripteur pointeur ou tableau à écrire nombre d'octets à écrire /* on est à la fin du fichier puisque on a tout lu */ if (write (descripteur, "fin de l'exemple", 16) == -1) perror ("l'écriture a échoué : ");

lseek Repositionner le curseur dans un fichier off_t lseek(int fd, off_t offset, int reference); décalage entre la position du curseur et le début du fichier descripteur décalage point de référence du décalage Valeur SEEK_SET SEEK_END SEEK_CUR Point de référence Début du fichier. Fin du fichier. Position actuelle.

Si nous voulons «rembobiner», retourner au début du fichier : lseek (descripteur, (off_t) 0, SEEK_SET); Allez à la fin du fichier : lseek (descripteur, (off_t) 0, SEEK_END); Revenir un octet avant la position actuelle : lseek (descripteur, (off_t) -1, SEEK_CUR); Mémoriser la position actuelle, il suffit alors de n'effectuer aucun décalage, lseek() renverra tout de même la position : off_t position; position = lseek (descripteur, (off_t) 0, SEEK_CUR); Si on désire revenir à cette position plus tard : lseek (descripteur, position, SEEK_SET);

Suite du grand exemple /* on repart tout au début du fichier */ lseek (descripteur, (off_t) 0, SEEK_SET); /* et on écrit */ if (write (descripteur, "début de l'exemple", 18) == -1) perror ("l'écriture a échoué : "); Close ferme le descripteur int close(int fd); 0 si la fermeture a fonctionné, -1 en cas d'erreur descripteur

Voici le contenu du fichier monfichier.txt : Ceci est un fichier texte pour montrer comment on utilise les fonctions en C de lecture de fichiers voilà Regardons l'exécution de notre exemple : Ceci est un fichier texte pour montrer comment on utilise les fonctions en C de lecture de fichiers voilà Le fichier est donc lu correctement, regardons le contenu du fichier après l'exécution : début de l'exempler texte pour montrer comment on utilise les fonctions en C de lecture de fichiers voilàfin de l'exemple

Les flux standards entrée standard (clavier), sortie standard (terminal), sortie d'erreur ont des descripteurs de fichier. entrée standard sortie standard sortie d'erruer STDIN_FILENO STDOUT_FILENO STDERR_FILENO Exemple d'utilisation : write (STDOUT_FILENO, "test", 4);

Exercices : Écrivez un programme qui détermine la taille du fichier en utilisant open(), lseek() et close(). Écrivez un programme avec une structure que vous remplirez, écrivez ensuite une fonction qui va écrire le contenu de cette structure directement dans un fichier. Puis écrivez une autre fonction qui va remplir une structure à partir du contenu du fichier.

Fonctions bufferisées (avec tampon) avantages : Fonctions fopen fread fwrite fgets fputs fprintf feof ftell fseek fclose utilisation plus souple performances élevées Comportement Ouvre un fichier et renvoie un pointeur de fichier. Lit des données depuis un fichier. Écrit des données dans un fichier. Lit une ligne (délimitée par un retour à la ligne) d'un fichier texte. Écrit une chaîne de caractères dans un fichier. Comme printf() et sprintf() mais la fonction écrit dans un fichier. Vérifie si on a atteint la fin du fichier. Renvoie la position du curseur dans le fichier. Permet de repositionner le curseur dans le fichier. Ferme le fichier. déclarées dans stdio.h

fopen ouverture de fichier FILE *fopen(const char *path, const char *mode); pointeur de fichier ou NULL en cas d'erreur nom du fichier mode d'ouverture du fichier "r" Mode "r+" Ouvre le fichier seulement en lecture. Description Ouvre le fichier en lecture et en écriture. Si le fichier n'existe pas il ne sera pas créé. "w" Ouvre le fichier en écriture et l'écrase s'il existe déjà (taille tronquée à 0). "w+" "a" "a+" Ouvre le fichier en lecture et en écriture. Si le fichier n'existe pas il sera créé dans le cas contraire il sera écrasé. Ouvre le fichier en écriture et le curseur est positionné à la fin du fichier. Le fichier est créé s'il n'existe pas déjà. Ouvre le fichier en lecture et en écriture. Le curseur est positionné au début du fichier mais chaque écriture sera faite à la fin du fichier. Si le fichier n'existe pas il sera créé.

Même exemple que le précédent mais avec les fonctions bufferisées #include <stdio.h> #include <errno.h> int main (void) { FILE *pointeur_fichier; pointeur_fichier = fopen ("monfichier.txt", "r+"); if (pointeur_fichier == NULL) { perror ("fopen a échoué : "); return (-1); }

fread lecture générique size_t fread(void *buffer, size_t size, size_t nmemb, FILE *stream); nombre d'éléments lus, -1 en cas d'erreur tableau ou pointeur où seront stockées les données nombre d'éléments à lire pointeur de fichier char tampon[101]; int elements_lus; taille d'un élément à lire do { elements_lus = fread (tampon, 1, 100, pointeur_fichier); if (elements_lus > 0) { tampon[elements_lus] = '\0'; printf ("%s", tampon); } } while (elements_lus > 0);

fwrite écriture générique size_t fwrite(void *buffer, size_t size, size_t nmemb, FILE *stream); nombre d'éléments écrits, -1 en cas d'erreur tableau ou pointeur contenant les données à écrire nombre d'éléments à écrire pointeur de fichier taille d'un élément à écrire /* on est à la fin du fichier puisque on a tout lu */ if (fwrite ("fin de l'exemple", 1, 16, pointeur_fichier) < 16) perror ("l'écriture a échoué : ");

fgets lecture d'une chaîne de caractères char *fgets(char *s, int size, FILE *stream); s ou NULL en cas d'erreur chaîne qui va contenir les données nombre de caractères à lire pointeur de fichier Remplaçons le code d'exemple de fread() par celui ci : char tampon[101]; char *retour; do { retour = fgets (tampon, 100, pointeur_fichier); if (retour!= NULL) printf ("%s", tampon); } while (retour!= NULL);

fputs écriture d'une chaîne de caractères int fputs(const char *s, FILE *stream); nombre de caractères écrits ou EOF en cas d'erreur chaîne à écrire pointeur de fichier Remplaçons le code d'exemple de fwrite() par celui ci : /* on est à la fin du fichier puisque on a tout lu */ if (fputs ("fin de l'exemple", pointeur_fichier) == EOF) printf ("l'écriture a échoué\n");

fprintf printf() dans un fichier int fprintf(file *stream, const char *format,...); pointeur de fichier s'utilise comme printf() et sprintf()

feof détermine si on a atteint la fichier du fichier int feof(file *stream); 0 si la fin du fichier n'est pas atteinte, 0 sinon pointeur de fichier if (feof (pointeur_fichier)!= 0) printf ("on a atteint la fin du fichier\n");

ftell renvoie le décalage (en octets) entre le curseur et le début du fichier long int ftell(file *stream); décalage pointeur de fichier fseek repositionne le curseur dans le fichier int fseek(file *stream, long offset, int reference); pointeur de fichier 0 si le positionnement est effectué, -1 en cas d'erreur décalage (en octets) point de référence du décalage (comme pour lseek())

fclose fermeture du fichier int fclose(file *fp); 0 si la fermeture est réussie, EOF en cas d'erreur pointeur de fichier

Les flux standards entrée standard (clavier), sortie standard (terminal), sortie d'erreur ont des pointeurs de fichier. entrée standard sortie standard sortie d'erruer stdin stdout stderr Exemple d'utilisation : /* lecture de l'entrée standard */ char buffer[51]; fgets (buffer, 50, stdin);

Exercices : Reprenez votre programme qui écrit la structure dans un fichier et adaptez le aux fonctions bufferisées. Écrivez un programme qui affiche sur la sortie standard le contenu de tous les fichiers textes (utilisez fgets()) passés en paramètres à votre programme (comme le ferait le programme cat).

Fonctions standards utiles void *memcpy(void *dest, const void *src, size_t n); Comme strcpy() mais générique, permet de recopier tous les types de données (tableaux de tous les types, structures) int memcmp(const void *s1, const void *s2, size_t n); Comme strcmp() mais générique, permet comparer tous les types de données (tableaux de tous les types, structures)

Recherches dans une chaîne char *strchr(const char *s, int c); Recherche le caractère (bien que ce soit un int) c dans la chaîne s et renvoie un pointeur à la première occurrence ou NULL si le caractère n'est pas trouvé. char *strstr(const char *haystack, const char *needle); Recherche la chaîne needle dans la chaîne haystack et renvoie un pointeur à la première occurrence ou NULL si la chaîne needle n'est pas trouvée.

Les noms symboliques directive préprocesseur #define associe un nom symbolique à une constante #define NOM_SYMBOLIQUE CONSTANTE Exemples : #define TAILLE 100 #define NOM_FICHIER "monfichier.txt" int mon_tableau[taille]; int autre_tableau[2*taille];

directive #ifdef terminée par #endif permet de tester si un nom symbolique existe, si c'est le cas le code entre #ifdef et #endif sera compilé dans le cas contraire il sera ignoré #define DEBUG_ACTIF int main (void) { int entier; /* des instructions du programme */ #ifdef DEBUG_ACTIF printf ("la valeur de entier est : %d\n", entier); #endif /* d'autres instructions du programme */ return (0); } #ifndef effectue le test inverse de #ifdef

Définition d'un nom symbolique par la ligne de commande de gcc option -DNOM_SYMBOLIQUE définit le nom symbolique comme s'il y avait un #define dans le code exemple : gcc source.c -o prog -Wall -DDEBUG_ACTIF

Programme en plusieurs fichiers sources.c cas mono source gcc source.c -o prog condensé de : gcc -c source.c gcc source.o -o prog source.o fichier objet = binaire compilé sans édition des liens donc non exécutable

cas multi sources gcc fichier1.c fichier2.c fichier3.c -o nom_programme condensé de : gcc -c fichier1.c gcc -c fichier2.c gcc -c fichier3.c gcc fichier1.o fichier2.o fichier3.o -o nom_programme

Fichier d'en-tête fichier d'en-tête (extension.h) ne contient pas de code mais uniquement : des prototypes des directives #define des directives #include parfois des variables globales Structure du fichier : /* on vérifie que le fichier n'a pas déjà été inclus */ #ifndef MONFICHIER_H #define MONFICHIER_H /* on place nos #define qui doivent être partagés */ /* on place ici les prototypes des fonctions */ /* on place ici nos variables globales à portées de programme */ #endif

source.h : #ifndef SOURCE_H #define SOURCE_H void afficher_globale (void); int ma_globale; #endif source0.c : #include "source.h" int main (void) { ma_globale = 42; afficher_globale (); return (0); } source1.c : #include "source.h" #include <stdio.h> void afficher_globale (void) { printf ("la variable vaut : %d\n", ma_globale); }

On compile : [thaeron@syndrome tmp]$ gcc -c source0.c -Wall [thaeron@syndrome tmp]$ gcc -c source1.c -Wall [thaeron@syndrome tmp]$ gcc source0.o source1.o -o prog -Wall On exécute : [thaeron@syndrome tmp]$./prog la variable vaut : 42 #include <fichier.h> #include "fichier.h" fichier.h est cherché dans /usr/include et /usr/local/include fichier.h est cherché dans le même répertoire que le source. On peut demander au compilateur de chercher dans un chemin particulier en plus de /usr/include avec l'option -Ichemin Exemple avec le chemin courant : gcc source.c -o prog -I.

Exercice : Reprenez l'un de vos derniers programmes contenant des fonctions. Placer certaines fonctions dans un autre fichier source et créez un fichier d'en-tête contenant les prototypes. Compilez votre projet.

Make et les Makefiles make = outils de compilation lisant les règles de compilation un fichier nommé Makefile make permet de ne pas tout recompiler lors de la modification d'un seul source. Principe des makefiles : indiquer les dépendances entre les fichiers On crée des variables (internes à make) contenant nos options de compilation et le nom du compilateur : CFLAGS=-c -Wall -I. LDFLAGS=-Wall CC=gcc

Les règles de compilation : nom_regle: dépendance1 dépendance2 dépendance3 commandes ATTENTION : la tabulation avant la commande est obligatoire. Si une dépendance est un fichier qui n'existe pas ainsi qu'une règle alors elle sera exécutée. La règle par défaut est «all»

CFLAGS=-c -Wall -I. LDFLAGS=-Wall CC=gcc all: programme programme: main.o routines.o extension.o $(CC) $(LDFLAGS) main.o routines.o extension.o -o programme main.o: main.c programme.h $(CC) $(CFLAGS) main.c routines.o: routines.c programme.h $(CC) $(CFLAGS) routines.c extension.o: extension.c programme.h $(CC) $(CFLAGS) extension.c

[thaeron@syndrome tmp]$ make gcc -c -Wall -I. main.c gcc -c -Wall -I. routines.c gcc -c -Wall -I. extension.c gcc -Wall main.o routines.o extension.o -o programme Si on refait make sans faire aucune modification : [thaeron@syndrome tmp]$ make make: Rien à faire pour «all». Si on supprime programme : [thaeron@syndrome tmp]$ make gcc -Wall main.o routines.o extension.o -o programme On voit très bien que make ne refait que l'édition des liens. Modifions maintenant main.c et refaisons make : [thaeron@syndrome tmp]$ make gcc -c -Wall -I. main.c gcc -Wall main.o routines.o extension.o -o programme make ne répète pas les autres compilations.

Exercice : Écrivez un Makefile pour le dernier programme que vous avez écrit qui est en plusieurs fichiers sources. Exemple d'un makefile automatisé : CC=gcc CFLAGS=-c -Wall -I. LDFLAGS=-Wall EXEC=programme all: $(EXEC) programme: main.o routines.o extension.o $(CC) $(LDFLAGS) $^ -o $@ %.o: %.c $(CC) $(CFLAGS) $<

Créer une bibliothèque bibliothèque statique : intégrée directement dans l'exécutable bibliothèque partagée : 1 fichier lié à tous les exécutables Exemple bibliothèque statique source.c : #include <stdio.h> void affichage (char *chaine) { printf ("%s\n", chaine); } exemple.h : void affichage (char *chaine);

Compilation gcc -c source.c Ensuite nous compilons notre fichier : gcc -c source.c Création bibliothèque statique(nommée libexemple.a) : ar r libexemple.a source.o On met à jour l'index de la bibliothèque statique : ar s libexemple.a

Programme utilisant la bibliothèque #include "exemple.h" int main (void) { affichage ("ceci est un test de la bibliothèque"); return (0); } Compilation : gcc programme.c -o programme -L. -lexemple indique que le chemin courant contient des bibliothèques indique que l'on doit «linker» l'exécutable à la bibliothèque libexemple

Bibliothèque partagée code source identique, seule la compilation change gcc -c -fpic source.c gcc -shared -Wl,-soname,libexemple.so -o libexemple.so source.o indique que la bibliothèque est partagée nom de la bibliothèque Compilation du programme utilisant la bibliothèque partagée est identique gcc programme.c -o programme -L. -lexemple Pour que la bibliothèque soit trouvée lors de l'exécution : export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)

Exercice : Reprenez votre dernier programme contenant plusieurs fichiers. Créez une bibliothèque de votre choix avec le fichier source ne contenant pas la fonction main() et compilez l'autre source en tant que programme exécutable lié à votre bibliothèque.