Cours gratuits » Cours informatique » Cours programmation » Cours langage C » Support de cours de Langage C

Support de cours de Langage C


Télécharger



★★★★★★★★★★3.5 étoiles sur 5 basé sur 1 votes.
Votez ce document:

Support de cours de  Langage C

Version provisoire du 27 septembre 2004

LICENCE EEA-IE

Responsable pédagogique du cours : Eric Magarotto

CM : E. Magarotto

TD – TP : E. Pigeon – P. Dorleans – J.M. Janik - E. Magarotto

Durée approximative : 10 h de cours

Pour tout renseignements relatifs au cours : 

e-mail :

web : tel : 02-31-45-27-09 (LAP-ISMRA)

 

I.         ELEMENTS DE BASE.................................................................................................2

I.1 - Commentaires.........................................................................................................................2

I.2 - Déclarations & types de variables (C : langage typé)......

...................................................2

I.3 - Initialisation de variables ......................................................................................................3

I.4 - Assignations............................................................................................................................4

I.5 - Entrées / Sorties Conversationnelles : printf et scanf.........................................................5

I.6 - Opérateurs arithmétiques condensés +=, -=, *=, /=, %=....................................................6

I.7 - Opérateurs séquentiels : « ; » composition : « , »................................................................6

I.8 - Opérateurs relationnels.........................................................................................................6

I.9 - Opérateurs logiques...............................................................................................................6

I.10 - Opérateurs de traitement du bit (uniquement sur int, long et char) ..............................7

I.11 - Priorité des opérateurs ........................................................................................................7

I.12 - Opérations d'incrémentation et de décrémentation .........................................................7

II.        STRUCTURES DE CONTROLE (BOUCLES)...........................................................8

II.1 - Condition SI ... ALORS ... SINON ...(IF … ELSE)...........................................................8 II.2 - Sélection (SWITCH)...........................................................................................................10

II.3 - Boucle tant que (WHILE)..................................................................................................10 II.4 - Boucle faire .... tant que (DO WHILE).............................................................................11 II.5 - Boucle pour (FOR).............................................................................................................11

II.6 - Ruptures de séquence.........................................................................................................12 III. LES TABLEAUX ........................................................................................................14

III.1 - Déclaration de tableau (Statique – 1D = vecteur)..........................................................14 III.2 - Accès aux éléments du tableau.........................................................................................14 III.3 - Tableaux multidimensionnels (tableaux 2d)...................................................................15

III.4 - Chaînes de caractères (string) .........................................................................................15 IV. LES POINTEURS.......................................................................................................18

IV.1 - Introduction.......................................................................................................................18 IV.2 - Opérations arithmétiques sur les pointeurs....................................................................19 IV.3 - Pointeurs et tableaux.........................................................................................................20

IV.4 - tableau de pointeurs et pointeur de tableau ...................................................................23 V. ALLOCATION DYNAMIQUE...................................................................................25

VI.      LES FONCTIONS ......................................................................................................27

VI.1 - Introduction.......................................................................................................................27 VI.2 - Passage d'arguments par adresse....................................................................................29 VI.3 - Evolution de la pile lors de l'appel à une fonction .........................................................31 VI.4 - Résumé (types)...................................................................................................................33

VI.5 - Portée, visibilité et classe d’allocation.............................................................................33

VII. PRE-PROCESSEUR...................................................................................................36 VIII.   LES STRUCTURES ..............................................................................................38

VIII.1 - Déclaration d'une structure ..........................................................................................38

VIII.2 - Utilisation d'une structure ............................................................................................38 VIII.3 - Utilisation des types synonymes : TYPEDEF..............................................................39 VIII.4 - Imbrication de structures..............................................................................................40 VIII.5 - Transmission d'une structure en argument d'une fonction.......................................42

VIII.6 - Transmission d'une structure en valeur de retour d'une fonction............................43

VIII.7 - Portée d’un structure.....................................................................................................44

VIII.8 - Structure et listes chaînées............................................................................................44 IX. GESTION DE FICHIERS..........................................................................................45

IX.1 - Introduction.......................................................................................................................45

IX.2 - Fichiers niveau haut..........................................................................................................45

IX.3 - Action sur le pointeur de fichier......................................................................................47

X.        LISTES CHAINEES...................................................................................................48

X.1 - Introduction.........................................................................................................................48

X.2 - Création d'une liste chaînée...............................................................................................48

X.3 - Affichage de la liste.............................................................................................................49

X.4 - Recherche d'un élément .....................................................................................................50

X.5 - Rangement par ordre alphabétique..................................................................................50

XI.      POUR ALLER PLUS LOIN … ..................................................................................51


GENERALITES

 : présentation du C

•   crée en 1972, langage de haut niveau compilé (? interprété)

•   progs C réutilisables, compilateurs respectent norme ANSI (88-90)

 : création d'un programme en C (3 phases)

•   édition d'un fichier source (nom.c) : éditeur de texte

•   compilation () : traduction du programme source en langage machine, génère fichier objet (obj)

•   édition de liens (linkage) : liaison entre obj, bibliothèque standard et fonctions. Groupe fichier obj et bin pour création d'un exécutable exe • éxecutable dépendant du système d’exploitation (recompiler le fichier source si changement d’OS) : un programme C se compose d'un ou plusieurs fichiers de la forme 

< directives préprocesseur > 

     include (préprocesseur : préparation à la compilation), < et > (resp. ‘’) indique de rechercher hors (resp. dans) répertoire actif

< définition de types >

< variables globales >

< fonctions >

structures des fonctions en norme ANSI : 

        < type > nom_de_fonction (<déclaration des arguments>)

         {    <déclaration locales>

              <instructions>        }

programme principal (main) ex : main(){Bloc d’instructions} void main(void) ne renvoie pas de valeur : principales caractéristiques

-   programmation modulaire multi-fichier

-   jeu d'opérateurs très riche

-   faible contrôle des types de données

-   uniquement des fonctions

I.

ELEMENTS DE BASE

On distingue :

-   les variables scalaires (entières, réelles et pointeurs)

-   les variables structurées (tableaux, strutures et unions)

I.1 - Commentaires

          /* ceci est un commentaire */       tout commentaire comporte une ouverture et une fermeture

I.2 - Déclarations & types de variables (C : langage typé)

Toutes les variables doivent être déclarées avant utilisation (2 paramètres)

                                      <type> <identificateur>

type de base

signification

taille (octets)

DOS - WINDOWS

valeurs limites

(DOS)

char

caractère

1 – 1

-128 à 127

unsigned char

caractère

1 – 1

0 à 255

int

entier

2 – 4

-32768 à 32768

short

entier court

2 – 2

-32768 à 32768

long

entier long

4 – 4

± 2147483648

unsigned int

entier non signé

2 – 4

0 à 65535

unsigned long

entier long non signé

4 – 4

0 à 4294967295

float

réel

4 – 8

± 10-37 à ±10+38

double

réel double précision

8 – 8

± 10-307à ±10+308

long double

réel double long

10 - 8

± 10-4932 à ±10+4932

 : noms de variables différents des mots réservés (instructions): break          char case continue    default       do double else   exit   extern          float for goto     if       int     long register      return

short           sizeof         static          struct          switch        typedef union     unsigned          while          const          enum          void identificateur

-   32 caractères max lettres ou chiffres mais pas de caractères accentués (codes ASCII étendus)

-   différence entre majuscules et minuscules

-   noms de variables réservés

type de base : - à l'exécution, une zone mémoire est réservée et contient la variable. La taille de la zone mémoire dépend du type

CHAR définit des variables de type caractère et ne stocke qu'un seul caractère à chaque fois. Un caractère peut être une lettre, un chiffre ou tout autre élément du code ASCII et peut être un caractère non imprimable dit de contrôle : 0 < ASCII <31. Un CHAR est codé sur 1 octet et ne peut prendre que des valeurs  - positives entre 0 et 255 s'il est non signé

-   négatives entre -127 et +127 s'il est signé

INT : Selon la valeur que l'on est amené à affecter à un entier et la présence ou non du signe, on peut opter pour INT, SHORT, UNSIGNED INT ...

FLOAT :  Selon la valeur que l'on est amené à affecter à un nombre à virgule flottante simple ou double précision, on peut opter pour FLOAT, DOUBLE,

LONG DOUBLE

I.3 - Initialisation de variables

une valeur initiale peut être spécifiée dès la déclaration de la variable

               main()

{ int i, j=5;

/* seul j est initialisé */

int k=2*3; int h=0x0A; float x=1.5, y=2.3e-4; char c='A'; char c='0x41'; long z=123L;

}

/* représentation scientifique */

différentes représentations pour les variables : les littéraux  : entiers :

codage

syntaxe

décimal

123

octal

0777

hexadécimal

0xff ou 0Xff

long

65538L

non signé

255U

Si un nombre est supérieur à 65535, le programmeur peut demander que ce nombre soit codé sur un entier LONG par "L" (ex : 65538L).  : réels :

codage

syntaxe

double précision

3.14159

long double (ANSI)

3.14159L

simple précision (ANSI)

3.14159F

 : caractères :

          caractères imprimables (chiffres - lettres ...)            caractères non imprimables dit de contrôle

caractères de contrôle

symbole

signification

'\n'

LF

saut de ligne (Line Feed)

'\a'

BEL

bip sonore (Bell)

'\t'

HT

tabulation horizontale

 (Horizontal Tabulation)

'\b'

BS

suppression arrière (Back Space)

'\f'

FF

saut de page (Form Feed)

'\r'

CR

retour chariot (Carriage Return)

I.4 - Assignations

•   Le signe = désigne l'opération d'assignation (affectation)

•   Possibilité d’en enchaîner plusieurs : i=j=2; (2 dans j puis dans i)

•   Assignation des constantes :  const int n=20; empêche toute modification future de la variable n 

 : variables liées à un bloc

             main()

{ int i=3;

{

int i=2;

? affichage de i   /* i=2 */

}

}

? affichage de i   /* i=3 */

 : conversions automatiques lors d'assignations

Entre char et int

 char ? int : le char se retrouve dans l'octet le - significatif (LSB) de int  int ? char : perte de l'octet le plus significatif (MSB) de l'entier

Entre int et long

 int ? long : conservation du bit de signe et donc la valeur de l'entier  long ? int : résultat tronqué, perte des 2 octets les plus significatifs Entre unsigned et long  unsigned ? long: les 2 octets les plus significatifs du long passent à 0

Entre int et float  int ? float : 10 ? 10.0  float? int : perte partie décimale : 3.5?3 (idem en  négatifs : -3.5? -3)

Entre float et double

 float ? double : aucun problème  double ? float : perte de précision

 Exemple : int n, long p, float x ; évaluer n*p+x ;

Conversion de n en long, puis multiplication par p, résultat de type long, conversion en float pour être additionné à x ? résultat de type float.

 : conversions forcées (opérateur cast)

int n=10, int p=3;

(double) (n/p) : calcul de n/p en int ? conversion en double ? 10/3=3

(double) n/p: conversion n en double puis conversion (implicite) p en double puis division en double? 10/3=3.33

I.5 - Entrées / Sorties Conversationnelles : printf et scanf

printf permet de réaliser un affichage formaté donc il faut spécifier

-   le nom de la variable

-   le format d'affichage avec caractères de contrôle

-   le texte d'accompagnement

                    syntaxe : printf("const char %format",nom_de_variable);

               exemple : printf("Leur somme est : %5d\n", somme) ;

                                    5 espaces réservés, justification à droite

printf("Leur somme est : %.2f\n", somme) ;

 2 chiffres après le point

printf("BONJOUR\n");

             %d : entier en décimal      %x ou %X : hexadécimal %o : octal

              %f : float (réel)                %e ou %E : réel (scientifique)      

              %u : entier non signé       %g (réel selon longueur valeur décimale)

             %lf : double                      %ld : long                             %Lf : long double

             %c : caractère                  %s : chaîne de caractères  %% affiche %

Rque : Action sur le gabarit d’affichage et sur la précision : voir TD

Rque : la macro putchar(c) joue le même rôle que printf("%c",c). Exécution plus rapide car pas d'appel au mécanisme de format.puts(string)

scanf est analogue à printf sauf lecture puis rangement. Pas de conversion automatique puisque l’argument transmis à scanf est l’adresse d’un emplacement mémoire (utilsation de &).

          syntaxe : scanf("const char %format",adresse_variable);    exemple : scanf("Leur somme est : %d\n", & somme) ;

Rque : Action sur le gabarit max, espace dans le format, caractère invalide et arrêt prématuré : voir TD

Rque : macro getchar() joue le même rôle que scanf("%c",&), mais plus rapide. kbhit utilisé en TD (très utile pour boucle)

I.6 - Opérateurs arithmétiques condensés +=, -=, *=, /=, %=

              Au lieu d'écrire :     i = i+20, on peut écrire i+=20

          de même :                     i = i+k, on peut écrire i+=k                      i = 3*i, on peut écrire i*=3

I.7 - Opérateurs séquentiels : « ; » composition : « , »

          « ; » plusieurs instructions à la suite, terminaison d’instruction       exemple :  int x = 5 ; x = x + 1;

                                     « ; » instruction vide. Attention boucles ! ! ! 

          « , » éxécution à la suite      exemple :  int x = 18, y;

I.8 - Opérateurs relationnels

             == et !=         test d'égalité et d'inégalité

              < et >             inférieur à et supérieur à

              <= et >=        (inférieur ou égal à) et (supérieur ou égal à)

I.9 - Opérateurs logiques

              Le langage C dispose de 3 opérateurs logiques classiques

                && et || : ET et OU logiques (ex (x=1)&&(y=10)? vrai et vrai=vrai)

              ! :                    non

I.10 - Opérateurs de traitement du bit (uniquement sur int, long et char)

             & ET (niveau binaire) (ex (x=1)&(y=10) )? 0001 & 1010=0000)

             |    OU inclusif (niveau binaire)

               ^ OU exclusif (niveau binaire

             >> et << décalages à droite/gauche (division/multiplication par 2)

               ~ complément à un

I.11 - Priorité des opérateurs

CATEGORIE

OPERATEURS

ASSOCIATIVITE

Référence

( )   [ ] ->   .

?

Unaire

+   -   ++   --   !   ~   *   & (cast)   sizeof

?

Arithmétique

*   /   % +   -

?

Décalage

<< >>

?

Relationnels

<   <=   >   >= = =    !=

?

Manipulation de bits

&, ^, |

?

Logique

&&

||

?

Conditionnel

?   :

?

Affectation

=   +=   -=   *=   /=   %= &=   ^=   |=   <<=   >>=

?

séquentiel

,

?

Exemples :          a+b*c        ?      a+(b*c)   -a/-b+c      ? ((-a)/(-b))+c       a<b==c<d ?    (a<b)==(c<d) 

I.12 - Opérations d'incrémentation et de décrémentation

             i = i+1 peut s'écrire i++ (postincrémentation)

                                                            incrémentation après utilisation de la valeur                                ou     ++i (préincrémentation)

                                                            incrémentation avant utilisation de la valeur

              i = i-1 peur s'écrire i--    (postdécrémentation)

                                      ou     --i     (prédécrémentation)

II.

STRUCTURES DE CONTROLE (BOUCLES)

II.1 - Condition SI ... ALORS ... SINON ...(IF … ELSE)

Instruction:          si (expression conditionnelle vraie)

          alors {BLOC 1 D'INSTRUCTIONS}  sinon {BLOC 2 D'INSTRUCTIONS}

Organigramme:

 

                                                   suite du programme                      

Syntaxe :

if (expression)

{

............;/* bloc 1 d'instructions */

............;

} else

{

............;/* bloc 2 d'instructions */

............;

}

Dans le cas d'imbrication de plusieurs if, la clause else se rapporte au if le plus proche.

Le bloc "sinon" est optionnel:              si (expression vraie)

                                                                 alors {BLOC D'INSTRUCTIONS}

 

suite du programme          

Rque: les {} ne sont pas nécessaires lorsque les blocs ne comportent qu'une seule instruction.

 : Rappels des opérateurs logiques (pour test de condition)

•   égalité: if (a==b)      « si a égal b »

•   non égalité:   if (a!=b) « si a différent de b »

•   relation d'ordre: if (a<b) if (a<=b) if (a>b) if (a>=b)

•   ET LOGIQUE: if ((expression1) && (expression2))   « si l'expression1 ET l'expression2 sont vraies »

•   OU LOGIQUE if ((expression1) || (expression2))   « si l'expression1 OU l'expression2 est vraie »

•   NON LOGIQUE if (!(expression1))

                                      « si l'expression1 est fausse »

Toutes les combinaisons sont possibles entre ces tests.

 : incrémentations et décrémentations dans une condition  if (--i==5) /* i est d'abord décrémenté et le test i==5 est effectué */

i=4; if (i++==5) printf("EGALITE (%d) ",i);

else printf("INEGALITE (%d) ",i);

/* le test i==5 est d'abord effectué, i est incrémenté avant même d'exécuter             printf("INEGALITE (%d)",i); */

II.2 - Sélection (SWITCH)

 

 

 

mot réservé variable de contrôle

 

C'est une instruction de choix multiples qui permet d'effectuer un aiguillage direct vers les instructions en fonction d'un cas (branchement conditionnel). Si la comparaison entre la variable de contrôle (de type int, short, char ou long) et la valeur des constante de chaque cas réussit alorsl’instruction du case est effectuée jusqu’à la première instruction d’arrêt rencontrée (break). Défault est une étiquette pour le cas ou aucune valeur n’est satisfaisante.

II.3 - Boucle tant que (WHILE)

Instruction:          tant que (expression vraie)

                               faire{BLOC D'INSTRUCTIONS}

 

La condition est examinée avant. La boucle peut ne jamais être exécutée. On peut rencontrer la construction suivante: while (expression); terminée par un ; et sans la présence du bloc d'instructions. Cette construction signifie: « tant que l'expression est vraie attendre ».

II.4 - Boucle faire .... tant que (DO WHILE)

exécuter

cette instruction (ou ce groupe d'instructions) est exécutée tant que

cette condition reste vérifiée

do  instruction; while   (condition);

                              ou         do  { instruction 1;

         . . . .

instruction N; }

                                               while  (condition) ;                                                                                    

La différence avec le WHILE réside dans le fait que la boucle DO WHILE est exécutée au moins une fois.

II.5 - Boucle pour (FOR)

for  (expr 1; expr 2; expr 3)   instructions;

 

 

 

 

 

ce qu'il faut faire à chaque fin de boucle (plusieurs instructions possibles,   séparées par des virgules)

la boucle est effectuée tant que cette condition est vérifiée (évaluation de la condition avant entrée dans la boucle) instructions de démarrage (plusieurs initialisations

Instruction :        possibles, séparées par des virgules)     Organigramme:

 

II.6 - Ruptures de séquence

              break :  - interruption de l'exécution d'une boucle FOR, WHILE, DO..

-   pour un FOR, la sortie s'effectue sans exécuter les         

                                instructions  de fin de boucle                  - fait également sortir d'un SWITCH

-   ne fait pas sortir d'une structure conditionnelle IF

          continue : - permet de passer à l'itération suivante d'une boucle     break et continue ne s'appliquent qu'à la boucle concernée

          exit (n) :sortie d'un programme et retour au système d'exploitation.                      La valeur de retour est n.

             return (n) : sortie d'un sous-programme avec la valeur n

goto (n) : branchement en un emplacement quelconque (à proscrire pour une « bonne programmation »).

Exemple 1 : résoudre ax2 + bx +c = 0.

#include <stdio.h>

#include <conio.h>

#include <math.h>     /* contient la fonction racine */

void main()

{ float a,b,c,delta,x1,x2;

/* saisie de A,B,C */

printf("\t\t\tRESOLUTION DE L'EQUATION DU SECOND DEGRE\n");

printf("\t\t\t                2\n");

printf("\t\t\t  AX +BX+C=0\n\n\n"); printf("SAISIR A B C SEPARES PAR RETURN\n");

printf("A = ");scanf("%f",&a); printf("B = ");scanf("%f",&b); printf("C = ");scanf("%f",&c);

/* debut du calcul */

/* cas particuliers */

if((a==0)&&(b==0)&&(c==0))printf("INFINITE DE SOLUTIONS\n"); if((a==0)&&(b==0)&&(c!=0))printf("PAS DE SOLUTIONS\n"); if((a==0)&&(b!=0))printf("UNE SOLUTION: X= %f\n",-c/b);

/*cas general */ if(a!=0)

{ delta = b*b-4*a*c; printf("DELTA= %f\n",delta); if(delta<0)printf("DELTA NEGATIF PAS DE SOLUTION\n");cc

else {

if(delta==0)printf("DELTA NUL, UNE SOLUTION X= %f\n",-b/2/a);

else{ x1= (-b+sqrt(delta))/2/a; x2= (-b-sqrt(delta))/2/a; printf("DELTA POSITIF DEUX SOLUTIONS\n"); printf("X1= %f X2= %f\n",x1,x2);

}

}

}

/* calculs termines */

printf("\n\nPOUR CONTINUER FRAPPER UNE TOUCHE");

getch();

}

Exemple 2 : Saisir une suite de caractères, compter et afficher le nombre de lettres e et d'espaces. Utiliser les propriétés du tampon.

#include <stdio.h> #include <conio.h> void main()

{

char c,compt_espace= 0,compt_e= 0;

printf("ENTRER UNE PHRASE:\n");/* l'utilisateur saisit la totalite de sa phrase */

while((c=getchar())!='\n')     /* lors du 1er passage, getchar ne prend */

/* en compte que le 1er caractere */

                      {                         /* les autres sont ranges dans le tampon */

                if(c=='e')compt_e++;      /* et recuperes par getchar lors */ 

/* des autres passages */

if(c==' ')compt_espace++; }

printf("NOMBRE DE e: %d\n",compt_e); printf("NOMBRE D'ESPACE: %d\n",compt_espace); printf("POUR SORTIR FRAPPER UNE TOUCHE ");

getch();

}

III. LES TABLEAUX

III.1 - Déclaration de tableau (Statique – 1D = vecteur)

<Type (int,char,etc…>   <Nom>   <[nb élements]>

 : utiliser un mnémonique pour la taille du tableau

             #define Nbval 20

          int tab[Nbval];   /*réservation mémoire pour 20*sizeof(int) = 40 octets */   float c[Nbval];   /*réservation mémoire pour 20*sizeof(float) = 80 octets */        char str[Nbval];         /*réservation mémoire pour 20*sizeof(char) = 20 octets */

III.2 - Accès aux éléments du tableau

•   Accès à chaque élément du tableau par l'intermédiaire d'un indice

•   L'indice 0 donne accès au premier élément

•   L'indice Nbval-1 donne accès au dernier élément

•   Il peut être une valeur, une variable ou une expression arithmétique

                                                      int i = 4;               tab[i] = 2     tab[i*2-1] = 3;

 : aucun contrôle de dépassement de bornes : tab[Nbval] ? ?

 : tous les éléments doivent être de même type  : aucune opération possible sur la totalité du tableau       tab1=tab2 ne recopie pas le tableau tab2 dans le tableau tab1          tab1==tab2 ne compare pas le tableau tab2 et le tableau tab1

 : initialisation des tableaux

          déclaration tableau = {v1, v2, ....} : liste de valeurs constantes qui  initialise le tableau

 int tab[3] = {0,1,2}  /* tableau de 3 entiers */  int tab[] = {0,1,2}

             int tab[5] = {0,1,2}           /* tableau de 5 entiers dont les 3

premiers sont initialisés. Les 2 autres sont initialisés à 0 si le tableau est déclaré en global.*/


 : taille du tableau exemple pour obtenir la dimension du tableau en nombre d'éléments :

int tab[10] = {1, 2, 3}

for (i=0;i<sizeof tab / sizeof(int); i++) printf("%d\n ",tab[i]);  sizeof tab / sizeof tab[0] donne le même résultat

 : adresse d'implantation d'un tableau

•   printf("%p\n",tab); affiche l'adresse d'implantation en mémoire

•   &tab[0] ; donne l’adresse de départ du tableau • On peut écrire &tab(Nbval) pour des tests de bornes (boucles) mais

ne pas accéder à sa zone  mémoire.

•   On peut additionner (ou soustraire un entier) aux adresses : 

Exemple : &tab[3]+2 équivalent à &tab[5].

III.3 - Tableaux multidimensionnels (tableaux 2d)

•   Descriptif : tableau de tableau ? Matrice

•   Syntaxe : type identificateur [dim1] [dim2] ... [dimn] 

•   peu usité car préférence pour les tableaux de pointeurs

Exemple : int tab[2][3] ={{1,2,3},{4,5,6}} ;

Le compilateur va réserver 2*3 places mémoire pour ce tableau. L’implantation en mémoire se fait de la manière suivante :

t[0][0]

t[0][1]

t[0][2]

t[1][0]

t[1][1]

t[1][2]

Les colonnes sont placées les une à la suite des autres, ce qui peut permettre d’identifier les éléments avec un seul indice si l’on utilise les adresses.

III.4 - Chaînes de caractères (string)

chaîne de caractère = tableau de caractères ou d'octets teminé par '\0'. 

La taille d'un tableau de caractère doit être suffisante pour contenir la sentinelle (le ‘\0’). exemple : ch[10] ne peut que contenir au plus que 9 caractères.

Saisie complète de la chaîne : 

Exemple : char ch[20]= ‘bonjour’; (8 caractères plus les autres à 0)

 : comparaison de deux chaînes de caractères

Les opérateurs = et == ne permettent pas de copier une chaîne dans une autre ni de comparer le contenu de deux chaînes. Il faut impérativement passer par une fonction de traitement de chaîne de caractères.

 strcmp(chaîne1, chaîne2)

retourne 0 si les 2 chaînes contiennent les mêmes caractères y compris '\0' retourne un entier négatif si chaîne1 < chaîne2 retourne un entier positif si chaîne1 > chaîne2

strncmp(chaîne1, chaîne2,lgmax) : limitation de la comparaison sur un nombre « lgmax » de caractères. stricmp(chaîne1, chaîne2) : pas de différence entre majuscules et minuscules.

strncmp(chaîne1, chaîne2,lgmax) : idem plus contrôle longueur max.

 : copie de chaînes de caractères

 strcpy (cible, source) 

copie le contenu d'une chaîne (source) dans une autre (cible). Le second argument peut être une variable de type chaîne de caractères ou une constante de ce type.

 : détermination de la taille d'une chaîne de caractères

 strlen

Cette fonction retourne le nombre de caractères présents dans une chaîne sans la sentinelle. 

 : lecture d'une chaîne de caractères

 gets(buf)

Tous les caractères introduits au clavier sont copiés dans buf jusqu'au retour chariot. Le caractère de retour chariot est remplacé par la sentinelle. (péférable à scanf("%s",&buf) pour qui l’espace est un caractère invalide)

 : affichage d'une chaîne de caractères

 puts(buf)

Cette fonction non formatée puts ajoute automatiquement le caractère \n de saut de ligne en fin d'affichage. C’est équivalent à printf("%s\n",buf).

 : concaténation de 2 chaînes de caractères

 strcat(destination,source)

Cette fonction recopie la seconde chaîne (source) à la suite de la première (destination) après avoir effacé le caractère '\0'. 

Il est donc nécessaire que l'emplacement réservé pour la première chaîne soit suffisant pour y recevoir la partie à lui concaténer.

strcat() est intéressante pour les noms de fichier avec des extensions différentes.

Strncat(destination,source,long) permet de contrôler la longueur du nombre de caractère de la chaîne d’arrivée

 : duplication de 2 chaînes de caractères

 char strdup(source) 

La duplication crée la chaîne qui va recevoir la copie et va prévoir la place mémoire nécessaire.

 : recherche d'une chaîne

 strstr(chaîne1,chaîne2)

Cette fonction entraîne la recherche de la chaîne chaîne2 dans la chaîne chaîne1.


IV. LES POINTEURS

IV.1 - Introduction

Pointeur = variable qui contient l'adresse d'un autre objet (variable ou fonction). Il est possible d'accéder à une variable par :

-   son nom ou identificateurc

-   son adresse en utilisant un pointeur

Il est conseillé mais pas obligatoire de faire commencer chaque identificateur de pointeur par « ptr_ » pour les distinguer des autres variables.

 : déclaration d'un pointeur

ex 1 :       int a; a=98;          initialisation de la variable a

adresse : &a=0x50                                                                                      Mémoire

valeur : 98

                                                                                    adresse

ex 2 :      int a, *ptr_p;                                    de la casemémoire

a=98;    a : variable allouée par le système dont l'adresse est 0x50 par exemple ptr_p=&a; p : pointeur sur un entier dont l'adresse est 0x20

•   a désigne le contenu de a

•   &a désigne l'adresse de a

•   ptr_p désigne l'adresse de a

•   *ptr_p désigne le contenu de a

•   &ptr_p désigne l'adresse du pointeur ptr_p • *a est illégal (a n'est pas un pointeur)  : affichage du contenu d'un pointeur  affichage en hexadécimal par le format %p ou %x de printf. printf("VOICI p: %d\n",i);Exemple :

printf("VOICI SON ADRESSE EN HEXADECIMAL: %p\n",&ptr_p);

 : initialisation d'un pointeur     à la définition du pointeur           à l'exécution par une affectation

IV.2 - Opérations arithmétiques sur les pointeurs

 : incrémentation/décrémentation d'un pointeur         ptr_p++ : adresse avant l'incrémentation (valeur actuelle de p)

              ++ptr_p :      adresse après l'incrémentation

          ptr_p-- : adresse avant la décrémentation (valeur actuelle de p)      --ptr_p :       adresse après la décrémentation

L'unité d'incrémentation et de décrémentation d'un pointeur est toujours la taille de la variable pointée (intérêt pour les tableaux). Si ptr_p est un pointeur sur un entier, ptr_p++ entraîne l'incrémentation de p de la taille d'un entier soit 4 octets.

 : comparaison de 2 pointeurs

un pointeur est défini à la fois par une adresse mémoire et un type. On ne peut donc comparer que des pointeurs de même type.

 : soustraction de 2 pointeurs

La différence de 2 pointeurs (de même type) fournit le nombre d’éléments du type en question situés entre les 2 adresses correspondantes.

 : pointeur nul

Pour certains besoins, il peut être utile de vouloir comparer un pointeur avec la valeur nulle. On utilisera alors la constante NULL prédéfinie dans <stdio.h>.

Exemple : int*ptr_n ;if (ptr_n==NULL)…

 : conversion de pointeurs

Il n’existe aucune conversion implicite d’un type dans un autre pour les pointeurs ? opérateur cast mais danger ! 

Attention aux contraintes d’alignement : un entier (4 octets) sera toujours placé à une adresse paire tandis q’un char (1 octet) pourra être placé à n’importe quelle adresse ? PB à la conversion d’un char* en int* ! ! !

IV.3 - Pointeurs et tableaux

Les pointeurs sont particulièrement utilisés lors d'accès aux tableaux.

              adresse d'un tableau = nom du tableau = adresse du 1er élément


              main()

{                                     Mémoire

 int tab[10]={1,2,3,4,5}, *p, x;  p=&tab[0]; équivaut à p=tab :  x=*tab;  valeur du 1er élément, x=1  x=*(tab+0); équivaut à tab[0] càd x=1  x=*(tab+i); équivaut à tab[i]     tab+i équivaut à &tab[i]

             }

 : un tableau peut être déclaré de 2 façons


          type identificateur [dimension]    /* vrai tableau */         type *identificateur                        /* pointeur seul */

Remarque : seule, la 1ère méthode assure l'allocation mémoire du tableau

exemple : affichage d'un tableau de réels

-   indices entiers

             #define dim 15

main()

{ int y;double x[dim]={1,2,3,4,5,6,7,8,9,10};

Cls();for (y=0;y<dim;y++)

{printf("%lf \n",x[y]);

/* ou bien */

printf("%lf \n",*(x+y));}

}

-   indices pointeurs

#define dim 15 main()

{double x[dim]={1,2,3,4,5,6,7,8,9,10},*ptr_p=NULL;

Cls(); for (ptr_p=x;ptr_p<x+dim;ptr_p++) printf("%lf \n", *ptr_p); for (ptr_p=x;ptr_p<x+dim;) printf("%lf \n", *ptr_p++);

}

 

 

Step4 : 7ème boucle version2

 : pointeur, adresse de pointeur et variable pointée    p désigne le contenu du pointeur p           &p désigne l'adresse du pointeur p

              *p désigne le contenu de la variable pointée par p

 : tableau multidimensionnel

              int tab [dim1],[dim2];

          tab : adresse du tableau                   p    tab[i] : adresse de la (i+1)ème ligne  *(p+i) 

                    attention :? de (*p)+i qui rajoute i au contenu pointé par p    tab[i][j] : élément i,j                       *(*(p+i)+j)

double x[dim]={1,2,3,4,5,6,7,8,9,10}; double *ptr_p=NULL;

for (ptr_p=x;ptr_p<x+dim;ptr_p++)

{

printf("%x \t\t %x\n", ptr_p,x); printf("%x \t\t %x\n", &ptr_p,&x); printf("%lf \t %lf\n", *ptr_p+3,x[3]); }

IV.4 - tableau de pointeurs et pointeur de tableau

IV.4.1

tableau de pointeurs : 

type *nom[taille] : 

exemple :

int tab[3]={1,5};

int *ptr_p[4]  = {tab, tab+1, tab+2, tab+3};

 

size of tab : 12 octets size of tab[0] : 4 octets size of ptr_p : 16 octets

size of ptr_p[0] : 4 octets, pointe sur 12 octets (3 éléments) size of ptr_p[0][0] : 4 octets

IV.4.2

pointeur de tableau : 

type (*nom)[taille] : 

exemple

int tab[1][2]={5,2} ;

int (*ptr_p[3]) [4]  = {tab, tab+1, tab+2} ;

 

size of tab : 8 octets size of tab[0] : 8 octets size of tab[0][0] : 4 octets size of ptr_p : 12 octets

size of ptr_p[0] : 4 octets, pointe sur 16 octets (4 éléments) size of *ptr_p[0]) [0]: 4 octets

V.

ALLOCATION DYNAMIQUE

La création de données dynamiques ou leur libération s'effectuent lors de l'exécution d'un programme. Intérêt : allocation ou libération en fonction des besoins.

 : allocation d'un élément de taille définie

Malloc reserve une zone mémoire et renvoie un pointeur de type indéfini (pointeur générique ou universel void*).

•   Mettre un « cast » si on veut forcer le type.

•   Retourne pointeur NULL si l’allocation n’a pas eu lieu.

•   Syntaxe : (void*)malloc(size) alloue "size" octets et retourne le pointeur (adresse) du 1er octet alloué.

 : allocation de plusieurs éléments consécutifs

(void*)calloc (nb, size) réalise l'allocation de "nb" éléments de taille "size" • Retourne l'adresse du 1er octet alloué

•   Initialise la mémoire à 0..

•   Retourne un pointeur NULL si l’allocation n’a pas eu lieu.

remarque : p=calloc(152,sizeof(int)) équivaut à p=malloc(152*sizeof(int))

 : changement de taille d'un tableau alloué dynamiquement realloc (pointeur sur le bloc mémoire à modifier, size) permet de réallouer de la mémoire en conservant les valeurs initiales.

Le nouveau bloc peut être plus grand ou plus petit (attention à la perte de données) que l’original.

 : libération de l'espace mémoire

L'un des intérêts essentiels de la gestion dynamique est de pouvoir récupérer des emplacements dont on n'a plus besoin.  free(pointeur sur le 1er emplacement) libère une zone mémoire préalablement alloué. Attention : vous devez posséder une trace du pointeur !

 : copie de blocs de mémoire

memcpy(pointeur cible, pointeur source, nb octets) recopie les nb premiers octets de pointeur source sur les nb premiers octets de pointeur cible.  Possible uniquement si les deux blocs ne se recoupent pas !

Exemple (allocation dynamique)

#include <ansi_c.h> #include <utility.h> #include <stdio.h> #include <stdlib.h> /* Déclaration d'un pointeur d'entier */ int *ptr_p1, *ptr_p2; int taille = 3; main() {

Cls();

/* Réservation mémoire pour un entier */ ptr_p1 = (int *) malloc(taille*sizeof(int));

ptr_p2 = (int *) calloc(taille,sizeof(int));                 

/* Test si l'allocation a eu lieu */ if (ptr_p1==NULL || ptr_p2==NULL) printf("Pas assez de mémoire\n"); else

{/* Initialisation de valeur pointé par ptr_int */

*ptr_p1=6;

                *(ptr_p1+1)=15;                                          

/* *ptr_p2=*ptr_p1; */ memcpy(ptr_p2,ptr_p1,taille*sizeof(int)); 

/* Affichage de la valeur pointé par ptr_int */ printf("La valeur pointé par ptr_p2 est %d\n",*ptr_p2);

/* Libération de la zone mémoire pointé par ptr_int */

free(ptr_p1);    free(ptr_p2);

}

}

 

 

 

 

VI. LES FONCTIONS

VI.1 - Introduction

Les fonctions permettent de décomposer un programme en entités plus limitées et donc d'en simplifier à la fois la réalisation et la mise au point.

Une fonction peut :

-   se trouver dans le même fichier que main() ou dans un autre

-   être appelée à partir de main(), d'une autre fonction ou d'elle même 

-   admettre ou non des arguments (paramètres d’entrée)

-   retourner une valeur ou non (paramètres de sortie)

-   posséder ses propres variables

Mais on ne peut pas définir une fonction à l'intérieur d'une autre fonction (toutes les fonctions sont "au même niveau").

Chaque fonction doit avoir un prototype et une déclaration

Remarque : main() n'est autre qu'une fonction qui joue un rôle particulier

(riguoureusement : void main(void)). Les variables déclarées au début de la fonction principale main sont locales à main !!!

Exemple de programme faisant appel


72