• Aucun résultat trouvé

1. Ce qu'est un Programme C

N/A
N/A
Protected

Academic year: 2022

Partager "1. Ce qu'est un Programme C "

Copied!
171
0
0

Texte intégral

(1)

Préface

P REFACE

C est un langage de programmation algorithmique très utile et très utilisé dans de nombreuses applications. Développé initialement (1972 déjà) pour le système Unix1, lequel est écrit en C, il a vite été adopté par une large communauté d'utilisateurs aussi bien sous Unix que dans d'autres environnements.

C se caractérise par une concision d'écriture, ses structures de contrôles très élaborées, ses possibilités en structure de données et un ensemble riche d'opérateurs. Au premier abord, C se manifeste par sa syntaxe quelque peu décourageante, mais a laquelle on s'habitue assez vite du fait de l'orthogonalité des concepts mis en oeuvre dans le langage. Selon Brad J. COX, et en paraphrasant, le langage C est comme un gros couteau bien aiguisé. Pour un novice, il est dangereux, mais placé en des mains expertes, il peut se révéler d'une redoutable efficacité.

Par certains aspects, C peut être jugé comme un langage de

"haut niveau" et par d'autres (dus à son origine historique), il peut l'être moins, dans le sens où C traite des objets jusqu'au niveau machine. Ce fut d'ailleurs un jeu chez des informaticiens que d'écrire les programmes C les plus courts ou les plus illisibles possibles. Peuvent être considérées comme "bas niveau" des écritures telles que:

z = (a>b) ? a : b;

++p->c;

les détails des entrées/sorties avec formats, printf("%d %-6.2f ...);

ou les manipulations proches des caractéristiques machines...

Mais, les fonctionnalités offertes dans le langage, en font un outil de programmation très puissant aussi bien pour les applications systèmes (accès aux mécanismes d'un système d'exploitation) que pour les applications conventionnels. Un simple exemple, comme

(2)

ii

celui de l'écriture d'un programme qui a pour résultat lui-même (i.e. son source) atteste de cette puissance (cf. exemples en fin de chapitre III).

Le langage C utilisé aujourd'hui est le produit de plusieurs années d'évolution. Il a été conçu en 1970 par Ken Thompson pour l'écriture sur PDP-11 du premier Unix.

A propos de ce livre

Ce livre est une introduction progressive à la programmation avec le langage C. Il vise deux objectifs:

- En s'appuyant sur des exemple, introduire de façon simple et concise les mécanismes du langages.

- Rappeler au fur et à mesure et autant que possible, quelques règles importantes dans la programmation, illustrées aussi par les exemples.

Des exercices progressifs accompagnent la présentation des différentes notions ainsi que des exemples complets de programmes ayant aussi un caractère pédagogique. Le lien http://www.emi.ac.ma/~ntounsi/C/ contiendra les corrections des exercices.

Le livre se présente en deux parties: une partie composée de trois chapitres pour les mécanismes de base du langage, et une partie composée de deux autres chapitres pour les concepts plus avancés de structures de données et de fonctions.

(3)

Préface

AVERTISSEMENT

Dans la présentation de certaines phrases du langage C, nous emploierons la notation suivante:

<nom> est une catégorie syntaxique en italique. Les crochets [ ] entourent une clause facultative.

struct [<nom>] {<liste declarations>}

signifie qu'il y a un nom, c'est à dire un identificateur C qui vient après struct et qui est facultatif, ensuite une liste de déclaration entre accolades. Exemple:

struct tuple {int numero; char[20] nom;}

struct {float x, y; }

Les exemples sont donnés et vérifiés sous compilateur C ANSI des systèmes à base de Unix (e.g. Mac OS, Linux, etc. dits Unix- like). Parfois le script complet de vérification est montré tel quel.

Prompt %

% cat prgme.c pour afficher le source

% cc prgme.c pour le compiler

% a.out pour la trace d'exécution

Si certains exemples sont donnés utilisant un script Unix avec des commandes cc, cat, a.out... cela n'est pas lié au langage C lui-même. Les mêmes commandes ou utilisations peuvent être faites sous d'autres OS (Windows en mode Command-line), ou avec des outils environnements intégrés de développement, comme DevCpp, ou autre.

(4)

Table des matières

iv

Table des matières

A propos de ce livre  ii 

1. Ce qu'est un Programme C  1 

2. Exemples  2 

2.1. Exemple 1  2 

2.2. Exemple 2  3 

3. Structure d'un Programme C  5 

3.1. Directives de Compilation  6 

3.2. Déclarations de Noms Externes  7 

3.3. Textes de  Fonctions  7 

4. Compilation d'un Programme C  9 

5. Compilation Séparée  11 

1. Introduction  13 

1. Objets de Base de C  14 

1.1. Caractère: char  14 

1.2. Entiers: int, short int, long int, unsigned  15 

1.3. Réels: short float , long float  18 

1.4. Initialisations  18 

2. Opérateurs Arithmétiques  19 

3. Les Tableaux  21 

4. Les Chaînes de Caractères char* idf ou char idf[n]  23 

5. Des Opérateurs de C  27 

6. Règles de Conversion  28 

6.1. Conversion Implicite  28 

6.2. Conversion Explicite  31 

7. Expressions Conditionnelles  32 

7.1. Opérateurs Logiques sur les Bits.  34 

8. Récapitulatif des Opérateurs de C  36 

1. Blocs d'Instructions  39 

2. La Conditionnelle if-else  40 

3. Choix Multiple switch  43 

4. Itérations while, for, do-while  44 

4.1. Boucle while  45 

4.2. Boucle for  45 

4.3. Boucle do...while  47 

5. Les Ruptures de Contrôle  48 

5.1. Break  48 

5.2. Continue  49 

5.3. Return  50 

(5)

Table des matières

5.4. Goto goto <label>  50 

6. Exemples de Programmes  51 

1. Les Pointeurs  77 

1.1. Opérateurs * et &  77 

1.1.1. Notion d'Objet et d'Adresse  77 

1.1.2. Notion de Pointeur  79 

1.2. Arithmétique des Pointeurs  80 

1.3. Valeur NULL  83 

1.4. Initialisation d'un Pointeur  83 

1.5. Pointeurs **  86 

2. Pointeurs et Tableaux  88 

3. Tableaux et Chaînes de Caractères  91 

4. Tableaux et Pointeurs  93 

4.1. Tableau de Chaînes de Caractères  95 

5. Types Structurés  98 

5.1. La Structure struct  98 

5.2. Tableaux de Structures  101 

5.3. Pointeurs vers les Structures  102 

5.4. Combinaison de pointeurs tableaux et structures  103 

5.5. Structures Récursives  104 

5.6. Unions  105 

5.7. Typedef  106 

5.8. Exemples de Programmes  109 

6. Types Enumérés  112 

1. Les Fonctions  117 

1.1. Déclaration de Fonction  117 

1.2. Définition de Fonction  119 

1.3. Appel de Fonction  119 

1.4. C ANSI vs C Classique  120 

1.5. Mode de Passage des Paramètres  122 

1.6. Fonctions à Nombre Variable de Paramètres  124 

1.7. Passage de Tableaux en Paramètres  130 

1.8. La Récursivité  136 

2. Statut des Variables et Classes d'Allocation  138 

2.1. Portée vs Durée de Vie  138 

2.1.1. Portée  138 

2.1.2. Durée de Vie  139 

2.2. Variables extern et Variables registre  142 

3. Les Fonctions en Paramètres  145 

4. Les Fonctions d'Entrées/Sorties de Base  151 

4.1. Getchar() et Putchar()  152 

4.2. Getc() et Putc()  153 

4.3. Gets() et Puts()  154 

4.4. scanf() et printf()  154 

(6)

Table des matières

vi

4.5. sscanf() et sprintf()  159 

(7)

Structure Générale d'un Programme C

CHAPITRE I

STRUCTURE GENERALE D'UN PROGRAMME C

The only way to learn a new programming language is by writing programs in it.

La seule façon d'apprendre un nouveau langage de programmation, c'est d'écrire des programmes avec.

-- Brian Kernighan

1. Ce qu'est un Programme C

Un programme C se présente comme un ensemble de fonctions réparties sur un ou plusieurs fichiers nom.c. Pour compiler un programme, il suffit d'indiquer au compilateur C (commande Unix-like2 cc, C Compiler) cette liste de fichiers:

cc pgme1.c pgme2.c etc...

Le compilateur génère alors un fichier exécutable a.out correspondant au programme. On peut changer ce nom a.out en un autre, comme monProgramme, avec l'option -o de la commande cc:

cc -o monProgramme pgme1.c pgme2.c

Limitons-nous pour l'instant à un seul fichier source C.

Parmi les fonctions d'un programme C, une est obligatoirement présente et doit s'appeler main(). Sans arguments pour l'instant.

Elle correspond au programme principal (d'où son nom), i.e. c'est la première exécutée et le nom main est alors obligatoire3. Donc un programme C contient au moins cette fonction. L'exécution

2

(8)

Langage C

2

d'autres fonctions se fait lors de leurs appels comme il est courant.

Certaines fonctions, très utilisées et déjà écrites, peuvent être rajoutées à un programme C par une directive spéciale:

#include, qui insère le texte de ces fonctions dans celui d'un programme.

2. Exemples

2.1. Exemple 1

Un premier exemple, classique, de programme C est:

main() {

printf("hello, world\n");

}

qui imprime le texte:

hello, world

La première ligne est la déclaration de l'entête d'une fonction, ici main(). Les accolades {} délimitent le corps de la fonction (cf. begin ... end). Celui-ci est constitué de l'instruction

printf("hello, world\n");

qui est un appel à une fonction printf() de bibliothèque, avec l'argument "hello,world\n". Cette fonction a pour effet d'imprimer ses arguments sur le terminal (ou la sortie standard du programme). Le seul argument ici est la chaîne de caractères entre guillemets. La séquence \n à une signification spéciale: \ sert à indiquer un caractère de contrôle, comme n, qui représente newline.

Si le programme main() ci-dessus se trouve dans un fichier hello.c on le compile par (% étant le caractère invite ici):

% cc hello.c et on l'exécute par:

(9)

Structure Générale d'un Programme C

% a.out

hello, world

% _

a.out étant le fichier exécutable résultat de la compilation.

Remarque: En réalité la fonction printf() existe dans un fichier stdio.h (pour STandarD Input Output) faisant partie de la bibliothèque du compilateur C et qui contient des utilitaires d'Entrées/Sorties. L'extension .h signifie Header (entête).

2.2. Exemple 2

Un deuxième exemple est un programme qui lit deux entiers et imprime leur somme. Il fait appel à une fonction somme(a,b):

#include <stdio.h>

/* inclusion en tête du programme du fichier bibliothèque

C stdio.h */

int somme(int, int);

/* Déclaration d'une fonction somme */

main() {

int a,b,s;

scanf("%d %d", &a,&b); /* lecture des deux entiers */

s = somme (a,b); /* appel de la

fonction somme */

printf(" Voici leur somme : %d\n", s);

}

/* La fonction somme avec deux paramètres formels x et y */

int somme (int x, int y) { return (x+y);

}

La première ligne de la fonction main() est une déclaration d'entiers a b et s. le type est int pour integer, placé avant. Ce sont des variables locales dont la portée est limitée au bloc de la

(10)

Langage C

4

la lecture des variables a et b. Le symbole & sera justifié plus bas (§ 3.3). Entre guillemets dans scanf(), il est explicité ce qu'on appelle un format, introduit par le symbole %. Comme il y a deux variables, a et b, il y a deux formats %d, ou d signifie décimal (on utilise %f pour les réels, %c pour caractère et %s pour les chaînes string etc... voir plus loin les détails au chapitre V). Ainsi il faut présenter en entrée pour scanf deux entiers (séparés par un

"blanc": espace, tabulation tab ou entrée newline, qui jouent un rôle de séparateurs de champs).

La ligne suivante du programme est l'appel de la fonction somme avec les arguments (paramètres effectifs) a et b. Fonction dont on connaît l'interface,

int somme(int, int);

composé du type du résultat (int) et des paramètres (int aussi), et déclaré en début de programme.

Ensuite printf() imprime la valeur de la variable s, avec un format décimal %d, précédée du texte Voici leur somme :.

La fonction somme consiste à renvoyer à la fonction appelante, main() ici, la somme x+y. Voyons comment elle se présente: la ligne

int somme(int x, int y)

est l'entête de la fonction qui spécifie son nom, le type de son résultat et de ses paramètres formels x et y. Cette entête est suivie immédiatement du corps de la fonction entre {}. Une autre écriture de l'entête de la fonction est

int somme (x, y) int x, y;

où on déclare les paramètres après leur énumération entre ().

La première écriture est la syntaxe C ANSI (et C++), plus intéressante ( cf. Chapitre V).

Les symboles /* et */ encadrent un commentaire en C.

/* Ceci est un commentaire */

/* Ceci est /* n'est vraiment pas */

un autre commentaire */

(11)

Structure Générale d'un Programme C

On peut remarquer déjà que chaque déclaration ou instruction en C se termine par point-virgule ";" . On verra que contrairement à PASCAL par exemple, un ";" ne sépare pas deux instructions ou déclarations, mais termine une instruction ou déclaration.

Voici la compilation et l'exécution de ce deuxième programme:

% cc somme.c

% a.out 2

3 <--- entrée des données 2 pour a et 3 pour b

Voici leur somme : 5 <--- Résultat Une deuxième exécution:

% a.out

2 3 <--- { mêmes données sur même ligne cette fois-ci }

Voici leur somme : 5

On peut imprimer un texte pour inviter l'utilisateur à rentrer des données: on doit sortir un message par printf avant de faire scanf

printf(" Renter a ensuite b > ");

scanf("%d%d", &a,&b);

ou mieux

printf(" Renter a > ");

scanf("%d", &a);

printf(" Renter b > ");

scanf("%d", &b);

Ce qui donne dans ce dernier cas

% a.out

Renter a > 2 Renter b > 3

Voici leur somme : 5

%

3. Structure d'un Programme C

(12)

Langage C

6

<directives de compilation>

<déclarations de noms externes>

<textes de fonctions>

Seule la partie textes de fonctions est obligatoire avec au minimum la fonction main() pour un programme complet4.

3.1. Directives de Compilation

Classiquement on y retrouve ce dont le compilateur a besoin pour compiler correctement un programme. A savoir les #include pour les fichiers fonctions de bibliothèque à inclure dans le programme, des définitions de symboles de types ou de con- stantes, ou des macros. Cette partie est traitée par un préprocesseur: programme invoqué pour un premier passage sur un texte source. C'est une autre caractéristique de C.

Exemples:

#define begin {

#define end }

#define then

#define MAX 1000

#include <math.h>

#include <ctype.h>

#include <string.h>

begin est défini comme symbole synonyme de {. Ainsi, si begin apparaît dans un programme, le préprocesseur le remplacera par {. end est défini comme synonyme de } et then comme synonyme de rien (il sera ignoré car il n'y a pas le mot then en C).

MAX est défini comme synonyme de la constante entière 1000.

Il est ensuite demandé d'inclure dans le texte du programme les fichiers de bibliothèque math.h, ctype.h et string.h. Respectivement, des fonctions mathématiques, des utilitaires sur les types de donnés et la manipulation de chaînes.

4 Se rappeler néanmoins qu'un fichier C peut être seulement compilé avec l'une des parties citées

(13)

Structure Générale d'un Programme C

3.2. Déclarations de Noms Externes

Dans cette partie, on peut déclarer des noms globaux dont la portée peut être l'ensemble d'un programme C. Ces noms correspondent à des variables aussi bien qu'à des fonctions. Pour ces dernières, seule l'interface (l'entête sans le corps) peut être fournie. On l'appelle aussi prototype de fonction (voir Chapitre V).

Exemple:

int i;

float f( int, char*, float);

3.3. Textes de Fonctions

Viennent enfin les définitions de fonctions, entête et corps, qui constituent le programme proprement dit, i.e. la partie calcul.

Leur structure est la suivante:

[<type résultat>] <nom de fonction> ([<liste arguments typés>])

{

<texte des instructions>

} Exemple:

float f (int i, char* s, float x) { float y;

...

return y;

}

Noter que la notion de ligne de texte n'existe pas en C. Mais il est de bonne habitude d'écrire le corps d'une fonction sous forme d'une instruction par ligne, indentée selon le besoin. La plupart des éditeurs font l'indentation automatique. Il y a aussi les commandes Unix-like cb ou indent qui indentent un source C après écriture.

Une fonction rend un résultat et son type est celui précédant le nom de la fonction. Ce type est pris par défaut comme int pour integer. Ainsi on aurait pu définir la fonction

(14)

Langage C

8 somme(int x, int y) { ...

}

sans avoir besoin de la déclarer en début de programme.

Noter aussi qu'en C les arguments sont passés par valeurs, comme il convient pour une fonction. (C++ a introduit, entre autre, le passage des paramètres par référence, voir remarque plus bas). Plusieurs questions se posent alors.

- Comment faire pour avoir une procédure qui n'a pas de résultat?

On dit alors que le type de la fonction est void , c'est à dire neutre. La fonction est alors une simple routine et ne doit pas être appelée comme une fonction à résultat dans une expression de calcul.

- Comment faire si on veut une procédure qui modifie la valeur de ses paramètres, ou procédure à plusieurs résultats, comme par exemple permut(x, y) qui permute les valeurs x et y (et modifie donc x et y)?

On passe alors comme paramètres à l'appel non pas les variables mais l'adresse de ces variables,

permut(&a, &b);

Le programmeur dispose d'un opérateur unaire & qui permet d'obtenir l'adresse d'une variable. Ainsi, c'est la variable elle- même qui est manipulée, à travers son adresse, et non pas une copie. (On doit dans ce cas déréférencer x et y dans la fonction avec la notation *x et *y.)

C'est pourquoi les paramètres, a et b, de la fonction scanf() du programme précédent sont précédés de &. Ce sont des résultats de la fonction. Cette possibilité sert aussi si la taille d'un objet est suffisamment grande pour être passée en paramètre par valeur.

Remarque: C++ a introduit le passage par référence, pour une commodité de notation. On n'a pas besoin de déréférencer explicitement les paramètres dans le corps de la fonction.

(15)

Structure Générale d'un Programme C

4. Compilation d'un Programme C

Nous avons vu que l'appel au compilateur C se fait par la commande cc. Nous l'avons utilisée telle que pour compiler un seul fichier et pour générer directement un module exécutable a.out. En fait, cette commande cc enchaîne le préprocesseur, la compilation, l'assemblage et l'édition de lien d'un programme.

Chacune de ces phases peut être faite individuellement bien sûr mais, dans les cas simples, on fait le tout d'un coup. Sinon on sépare la phase d'édition de lien, au cas où un programme se compose de plusieurs fichiers, pour ne pas avoir à recompiler des morceaux déjà compilés et corrects. C'est ce qu'on appelle la compilation séparée.

La forme générale de cette commande cc est:

% cc [<options>] <nomFichiers> ... [-l<Librairie>] ...

Principalement, les options sont -c et -o et les fichiers se ter- minent par .c et .o Les fichiers .c sont des sources C et les fichiers .o sont des modules objets résultats de la compilation de fichiers .c. Ce sont des fichiers codes binaires non encore édités ou liés, donc non prêts à l'exécution.

Remarque: Ces fichiers .o sont toujours créés mais ne sont pas toujours sauvegardés. Ils peuvent l'être à la demande ou à la compilation de plusieurs sources C. On verra plus bas l'utilité de les garder.

La partie -l est nécessaire (lors de l'édition de lien) pour faire appel à des modules objets de bibliothèque, quand on utilise des fonctions qui s'y trouvent. Exemple, -lm pour la bibliothèque mathématique, -lX11 pour celle X11, etc ... Nous en ferons abstraction ici. Ainsi la commande cc se présente:

[1] Sans options et avec des fichiers .c

% cc pgme.c

Le fichier pgme.c est compilé et un exécutable a.out correspondant est généré

(16)

Langage C

10

% cc pgme1.c pgme2.c ...

Les fichiers sources pgme1.c, pgme2.c... sont compilés et, pour chacun, un module objet pgme1.o, pgme2.o ... est généré en plus de l'exécutable (général) a.out. Ces fichiers sont générés dès qu'il y a plusieurs sources .c .

Soit, sans options, la commande cc avec un seul fichier source .c crée un fichier exécutable a.out, et avec plusieurs fichiers sources .c, elle crée en plus les fichiers modules objets pgme1.o, pgme2.o ... correspondants.

[2] Sans option, avec fichiers .o

% cc pgme1.o pgme2.o ...

Les fichiers objets pgme1.o, pgme2.o ... sont édités pour (re)créer un exécutable a.out.

[3] Avec option -c

% cc -c pgme.c ...

Le (ou les) fichier(s) source(s) est (sont) compilé(s) pour générer un (ou des) module(s) objet(s) pgme.o Il n'y a pas de a.out en sortie.

[4] Avec option -o

% cc -o nomExec ...

Le fichier exécutable a.out est nommé par nomExec. Cette option est ignorée si l'option -c est présente (il n'y a pas d'exécutable à générer). Se rappeler qu'il faut une fonction main() pour former un exécutable.

On peut constater que le (1) correspond à la partie enchaînement de l'ensemble: préprocesseur, compilation/assemblage et édition de lien le (2) correspond à la partie édition de lien uniquement et le(3) correspond à la partie préprocesseur et compilation/assemblage uniquement.

On peut aussi noter au passage l'existence de la commande cpp (C PreProcessor) pour effectuer uniquement le passage du

(17)

Structure Générale d'un Programme C

préprocesseur, et de la commande ld (Link eDitor) pour l'édition de lien des fichiers .o la génération d'un exécutable.

En Résumé:

COMMANDE RESULTAT

cc pgme.c

cc pgme1.c pgme2.c cc pgme1.o pgme2.o cc pgme1.c pgme2.o cc -c pgme.c

cc -o exec pgme.o cc -o exec pgme.c

a.out

a.out, pgme1.o, pgme2.o a.out

a.out, pgme1.o (parfois) pgme.o

exec exec

Remarque: Il y a parfois un effet de bord, quand un fichier exécutable est généré et quand on compile simultanément des fichiers .o et .c. Cet effet est celui de créer, s'ils n'existent pas, ou de supprimer s'ils existent, les fichiers .o correspondant aux fichiers .c.

5. Compilation Séparée

Il est très utile —et très conseillé— quand un programme est assez long, de l'écrire en morceaux compilés séparément en fichiers .o par la commande cc -c. Fichiers à éditer plus tard par cc (ou cc -o) pour générer un programme exécutable, si une fonction main() est présente. D'ailleurs, celle-ci peut se limiter aux appels nécessaires pour lancer et contrôler l'application, et être ainsi la dernière écrite.

Les différents morceaux du programme peuvent aussi être écrits en plusieurs versions chacun, laissant le choix au programmeur d'éditer les morceaux voulus et de composer l'application lors de la dernière compilation qui crée l'exécutable final. Nous y reviendrons plus loin quand on verra l'utilitaire MAKE, qui permet la recompilation (entre autre) automatique

(18)

Langage C

12

La compilation séparée répond au besoin de la programmation modulaire: l'écriture de programmes en plusieurs morceaux ou modules. C'est un domaine à part entière qui déborde du cadre de ce cours. Disons simplement La décomposition modulaire la plus simple c'est d'écrire des sous- fonctions qui réalisent des tâches plus élémentaires qui concourent à la réalisation de la fonction globale d'un programme.

Les modules qui en résultent doivent être les plus indépendants et les plus autonomes possibles, (leurs relations ou interconnections dans le cadre du programme globale est réduite au strict nécessaire) et par conséquent pouvant servir pour d'autres programmes. La tâche de chaque module doit être alors très bien spécifiée, i.e., définie avec précision. Mis à part qu'il doit être fortement commenté aussi. Le talent d'un(e) programmeur(se) se reconnaît à la conception modulaire de ses programmes.

Remarque: Un module réalisant une certaine tâche ne se limite pas forcément à une fonction au sens langage de programmation.

Au contraire, On peut envisager des modules à la Parnas (du nom de D.L. Parnas, pionnier du génie logiciel, qui développa le principe de la programmation modulaire), c'est à dire constitués d'un ensemble de fonctions logiquement reliées et manipulant un ensemble de données (dites ressources) communes déclarées au sein du même module, et accessibles uniquement en son sein. Ce qui s'appelle "Information Hiding", ou protections des informa- tions contre les accès et modifications imprudents et involontaires. Cette caractéristique, dite aussi encapsulation, avec d'autres comme l'héritage, est à la base de la programmation orientée objets, et a été retenue dans le langage C++ sous la notion de classe.

(19)

Expressions De Base

CHAPITRE II

EXPRESSIONS DE BASE

A person who is more than casually interested in computers should be wellschooled in machine language, since it is a fundamental part of a computer.

-- Donald Knuth

1. Introduction

En C une instruction est une expression suivie de point- virgule ";"

<expression> ;

";" qui a, en quelque sorte, le sens "évaluer cette expression".

Une expression est une combinaison bien formée5 d'opérandes et d'opérateurs. Les opérandes sont des variables, constantes ou des appels de fonctions. Les opérateurs sont ceux classiquement connus. Cependant, on remarquera un opérateur spécial qui est l'affectation, symbole =. Pour bien comprendre cela il faut imaginer qu'en C une expression a non seulement une valeur, celle résultant de son évaluation, et un type, celui hérité de ses opérandes, mais aussi un effet —de bord en quelque sorte.

Voici des exemples d'expressions/instructions: soit la déclaration int i;

2 + 3 ; est une expression de type entier évaluée à 5 qui est son résultat

i + 5 ; idem, expression entière évaluée à i+ 5

(20)

Langage C

14

i++ ; est une expression qui a pour type et valeur résultat ceux de i, et pour effet

d'incrémenter i de 1.

i = 2+3; est une expression entière de valeur 5 (2+3) et qui a pour effet d'affecter le résultat 5 à i. C'est l'usage le plus fréquent de l'opérateur =, i.e., instruction d'affectation.

L'expression précédente ayant comme valeur 5 de type int, l'expression

x = (i = 2 +3);

a pour effet d'affecter 5 à x (en plus de l'affecter à i).

On aura noté que les deux premières écritures sont sans intérêt (résultat perdu) quoique correctes.

1. Objets de Base de C

1.1. Caractère:

char

char est le type d'un caractère quelconque US-ASCII codé sur un octet. C'est l'objet C le plus élémentaire.

\0 pour caractère "null" 0 (le premier du code ascii) \a pour son bip BELL code ascii 7 ('\007')

\b pour espace arrière BACKSPACE code ascii 8 ('\010') \t pour tabulation TAB code ascii 9 ('\011')

\n pour nouvelle ligne LINEFEED code ascii 10 ('\012') \f pour nouvelle page FORMFEED code ascii 12 ('\014') \r pour retour chariot RETURN code ascii 13 ('\015') \" pour double quote ou guillemets "

code ascii 34 ('\042')

\' pour quote ou apostrophe ' code ascii 39 ('\047') \\ pour le \ lui-même code ascii 92 ('\134')

\ddd pour une constante octale ddd désignant une valeur ASCII ( 0 =< d <= 7 )

Fig-II-1 Caractères Spéciaux de C

(21)

Expressions De Base

Exemple:

char c,b;

c='b' ; b='+'; c=b; c='\'';

Les constantes sont entre apostrophe '' comme 'a' ou en code octal '\134' (valeur ascii en base 8 de \). Le caractère apostrophe lui-même est introduit par le symbole \ (considéré méta caractère) comme dans '\''. En quelque sorte, \x signifie traiter le caractère x spécialement s'il est normal, ou normalement s'il est spécial. Ainsi '\\' est le caractère normal barre oblique (backslash) \ et '\n' est le caractère spécial nouvelle ligne (newline). La figure II-1 donne la liste des caractères spéciaux (avec leur code ascii en décimal et en octal).

Exercice: Expérimenter printf de \x où x est de votre choix.

Des fonctions utiles sur le type char sont données par la figure II-2. Ces fonctions retournent 0 pour faux et une valeur différente de 0 pour vrai. Elle servent essentiellement à connaître le type de caractère: chiffre, lettre, majuscule etc...

1.2. Entiers:

int, short int, long int, unsigned

Les entiers sont du type int pour l'usage courant, short int (ou short tout court, sans jeu de mots) pour les entiers codés sur deux octets (16 bits) appartenant à l'intervalle:

[-32768 , 32767], i.e. [-215 , 215 - 1]

et long int (ou long ) pour des entiers sur 4 octets (32 bits) appartenant à l'intervalle:

[-2147483648 , 2147483647], i.e. [-231 , 231 - 1]

En général int équivaut à long, dans les ordinateurs récents. On peut utiliser aussi unsigned pour des entiers non signés, i.e., positifs. Par exemple unsignedshort appartient à [0, 65535].

(22)

Langage C

16

#include <ctype.h> /* l'entête à mettre */

isalpha(c) c est une lettre

isupper(c) c est une lettre majuscule islower(c) c est minuscule

isdigit(c) c est chiffre

isalnum(c) c est alphanumérique (lettre ou chiffre)

isspace(c) c est blanc, tab, retour-chariot, newline ou formfeed

ispunct(c) c est caractère de ponctuation isprint(c) c est caractère imprimable

iscntrl(c) c est caractère de contrôle ou delete isascii(c) c est caractère ASCII (0 <= c <128

Fig-II-2 Fonctions sur le Type char

Exemples de déclarations:

int i; long int j; (ou long j;) short int j; (ou short j;)

unsigned int k; unsigned short l, m, n;

Exemples de constantes:

1, 25, 189 en notation décimale.

0123, 0717 en notation octale (0 suivi de chiffres à base 8 ).

0xfF 0XfaC en notation hexadécimale (0 suivi de x ou X suivi de chiffres dans

[0 , 9] ∪ [A , F] ∪ [a , f]).

Exemple de programme:

Le programme de la figure II-3 calcule le pgcd de deux entiers a et b. Voici les résultats du programme sur les couples d'entiers (a, b): (18,12) (72,81) (233,144)

(23)

Expressions De Base

a | b a | b a | b --- --- --- 18 | 12 72 | 81 233 | 144 12 | 6 81 | 72 144 | 89 6 | 0 72 | 9 89 | 55 PGCD = 6 9 | 0 55 | 34 PGCD = 9 34 | 21 21 | 13 13 | 8 8 | 5 5 | 3 2 | 1 1 | 0 PGCD = 1 Le dernier jeu d'essai, (233,144) a donné l'itération la plus longue. En effet ce sont deux termes successifs de la suite de Fibonacci, comme le montrent les différentes valeurs de a (exercice: montrer pourquoi).

main (){

int pgcd (int, int);

printf(" PGCD = %4d\n",pgcd(18,12));

printf(" PGCD = %4d\n",pgcd(72,81));

printf(" PGCD = %4d\n",pgcd(233,144));

}

int pgcd(int a, int b) {

/* PGCD de a b par algorithme d'Euclide.

Version itérative.*/

int reste;

printf(" a | b \n"); printf("---\n");

printf("%4d | %4d\n",a,b);

reste = b;

while (reste != 0){

/* si a < b, le premier tour de la boucle permute a et b */

reste = a % b; /* % est le modulo */

a = b;

b = reste;

printf("%4d | %4d\n",a,b);

}

return a;

}

(24)

Langage C

18

1.3. Réels:

short float , long float

Ils sont short float (ou float), long float (ou double). La précision est de 6 ou de 16 chiffres décimaux selon le cas. Soit 4 ou 8 octets en général.

Exemples de déclarations:

short float delta; (ou float delta;) long float pi; (ou double pi;)

Exemples de constantes:

3.14 -5.0 5e17 -3E-12 etc...

Remarque: On peut utiliser la fonction sizeof(<type>) ou sizeof (<expr>) pour connaître la taille d'un objet. Par exemple sizeof(short) est 2 et sizeof(3.14) est 4.

Exercice: tester sizeof() pour certains types ou expressions.

1.4. Initialisations

On peut initialiser une variable dès sa déclaration comme dans:

int i = 3;

qui déclare i entier et lui donne 3 comme valeur initiale; ou encore

double pi = 3.1415926535897932384626433;

(16 chiffres seront retenus) char c = 'c';

(25)

Expressions De Base

#include <math.h>

int abs (int i); valeur absolue de i double fabs (double x); valeur absolue de x réel double

double floor(double x); plus grand entier ≤ x (partie entière)

double ceil (double x); plus petit entier ≥ x double exp (double x); e puissance x

double log (double x); le logarithme de x double log10 (double x); log à base 10 de x double pow (double x,double y); x puissance y (power)

double sqrt (double x); racine carré de x (Square Root)

double sin (double x); sinus de x

/* les autres fonctions trigonométriques sont * cos, tan, acos, asin, atan, sinh, cosh, tanh */

void srand (unsigned germe); initie la fonction aléatoire rand()

int rand (void) génère une valeur aléatoire (random) dans [0 , 2^31 - 1]

Le générateur est initialisé par srand(1),

ensuite réinitialisé par srand(i) i quelconque.

Fig-II-4 Fonctions Mathématiques de C.

2. Opérateurs Arithmétiques

C offre les opérateurs arithmétiques classiques à savoir:

+ et - addition et soustraction binaires (ou - unaire)

* et / pour la multiplication et la division. Dans la division d'entiers la partie décimale est tronquée! Ainsi 5.0/2.0 donne 2.5 et 5/2 donne 2, i.e. le quotient entier.

% est l'opérateur modulo (reste de la division entière) sur les entiers.

5%2 donne 1.

L'élévation à la puissance n'existe pas. Il faut utiliser la

(26)

Langage C

20

fonctions mathématiques de C. On doit se rappeler que les fonctions retournent par défaut un entier (e.g. abs(i)). Il est très utile de jeter un coup d'œil sur le fichier Unix /usr/include/math.h pour ces fonctions et d'autres.

L'exemple suivant, est un programme sur des réels. C'est le calcul d'un nombre réel y par fractions continues.

y = x1 + 1/( x2 + 1/(x3 + 1/(x4 + ... + 1/(xn))))

Il a pour données: n et xi, pour i de n à 1. Le résultat est y, le réel cherché. Nous l'expérimenterons sur des réels connus. Il donne pour les valeurs de xi suivantes:

[1; 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

résultat = 1.61805556 = nombre d'or k6 [1; 2, 2, 2, 2, 2, 2, 2]

résultat = 1.41421569 = racine de 2 [2; 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8]

résultat = 2.71828184 = nombre e

La notation [x1; x2, ..., xn] représente les termes successifs du calcul, qu'on commence à l'envers de xn à x1 (rentrer xn, xn-1, ..., x1).

6 k = (L+l)/L = L/l. L et l sont les dimensions d'un rectangle parfait.

(27)

Expressions De Base main()

{ float x;

double y;

/* y est le résultat de chaque itération, * i.e. le cumul intermédiaire et final

*/

int i,n;

printf("combien de termes?");

scanf("%d",&n);printf("\n");

printf("dernier terme >");

scanf("%f",&x);printf("\n");

y = x;

for (i=1; i < n; i++){

y = 1.0 / y;

printf("nouveau terme >");

scanf("%f",&x);printf("\n");

y = x + y ; }

printf("résultat = %12.8f\n",y);

}

Fig-II-5 Programme de Calcul de Réels par Fractions Continues.

Exercices:

II.2.1 Faire un programme qui lit et écrit des entiers short et long, des réels float et double. Utiliser les formats %ld %lf pour les long int et long float .

II.2.3 Vérifier que

[3; 7, 15, 292, 1, 1, 6, 1, 3] donne π et que [1; 1, 2, 1, 2, 1, 2, 1, 2] donne √3.

Chercher l'algorithme inverse qui, pour un réel donné, donne les termes successif de la suite xn, xn-1, ..., x1.

3. Les Tableaux

Les tableaux sont des variables indicées, à une ou plusieurs dimensions. La notation [] sert à introduire l'indexation.

int t[20]; déclare un tableau de 20 entiers.

(28)

Langage C

22

float m[3][5]; déclare une matrice de réels ayant 3 lignes et 5 colonnes.

L'accès à un élément se fait par la notation x[entier], x[entier][entier]...

t[i], t[5] etc... pour une dimension, avec i dans 0..19

! (t déclaré t[20])

m[2][3] en deux dimension, pour l'élément de la 3ième ligne 4ième colonne !

En effet les indices de tableau commencent à 0 en C. Un tableau t[5] contient 5 éléments et il est indicé de 0 à 4. De même pour toute dimension. On se rendra compte vite de cette praticité. On verra pourquoi plus loin quand on étudiera les pointeurs.

remarque importante: Les tableaux en C ne supportent pas l'opération d'affectation. On ne peut pas affecter en bloc un tableau à un autre. Le compilateur vérifie cela et n'acceptera pas que le nom d'un tableau figure en partie gauche d'une affectation.

Si on a

int r[5], s[5]; on ne peut pas faire

r = s;

Il faut le faire dans une boucle par exemple:

for (i=0; i<5; i++) r[i] = s[i];

Cela est dû au fait qu'un nom de tableau n'est pas une variable comme les autres qui contient une valeur —qui serait dans ce cas tout le tableau. Elle contient en fait l'adresse du tableau et pour cela elle ne doit pas changer sinon on ne peut plus accéder au tableau. Nous en reparlerons au chapitre IV.

On peut initialiser un tableau, totalement ou partiellement, à la déclaration.

int t[4] = {1, 2, 3, 0};

On énumère entre {} la liste_virgule des éléments.

float u[5] = {0., 3.1415};

Les deux premiers éléments sont initialisés, les autres étant nuls.

(29)

Expressions De Base

float mat[3][2] = { {0, 1}, {1, 0}, {2, 5}};

Liste de liste d'initialisations, i.e. listes des lignes.

char s[5] = {'t', 'o', 't', 'o'}; qu'on peut écrire en bref comme

char s[5] = "toto";

N.B. L'initialisation de tableau n'est valable que si ce dernier est déclaré static, par exemple:

static char s[5] = {'t', 'o', 't', 'o'};

static char s[5]="toto"; en plus bref.

ou en dehors de toute fonction, dans la partie déclarations de noms externes (voir §3 Chapitre I).7

4. Les Chaînes de Caractères

char* idf ou char idf[n]

Les chaînes sont des tableaux de caractères, qu'on peut manipuler par indexation comme un tableau ou à travers un pointeur (qui pointe vers le premier caractère). Une discussion plus détaillée sur les pointeurs et les tableaux se trouve plus loin avec les opérateurs * et & (Voir Chapitre IV). Contentons nous dès à présent de donner quelques cas simples qui illustrent l'usage d'un tableau de type char:

char s[20];

Déclare s un tableau de 20 caractères maximum8. char* ps ;

Déclare ps un pointeur vers (le premier caractère d') une chaîne.

char x[10] = "toto\0";

Déclare une chaîne d'au maximum 10 caractères et l'initialise avec le mot toto (ou plus exactement, avec une adresse contenant le mot toto).

7 Contrainte levée en C ANSI

(30)

Langage C

24

On a x[0]='t' x[1]='o' x[2]='t' x[3]='o' et x[4]='\0'. Le caractère \0 est, par convention, le terminateur d'une chaîne. Penser à ne pas l'omettre. Parfois il est rajouté par le compilateur. En tout cas il faut prévoir sa place dans la taille du tableau lors de l'initialisation.

Il est plus pratique d'utiliser les chaînes de caractères avec les pointeurs que comme tableaux. L'affectation

ps = "toto\0";

marche si ps est déclaré char* ps; mais elle n'est pas permise si ps est déclaré char ps[n]; Un tableau ne supporte pas d'affectation globale. Il faut procéder élément par élément. Mais la notation est permise pour initialiser une déclaration tableau de caractères.

#include <strings.h>

char *strcat (s1, s2) char *s1, *s2;

Concatène s2 au bout de s1. Le résultat est identique à s1.

char *strncat(s1, s2, n) char *s1, *s2; int n;

idem avec une limite totale égale à n.

char *strcpy(s1, s2) char *s1, *s2;

Copie s2 dans s1 jusqu'au caractère null '\0' compris.

Résultat identique dans s1.

char *strncpy(s1, s2, n) char *s1, *s2; int n;

idem avec copie de n caractères.

Il y a troncature ou remplissage avec \0 (null).

char *index(s, c) char *s, c;

Retourne la chaîne à partir de 1ère occurrence de c dans s.

char *rindex(s, c) char *s, c;

idem mais dernière occurrence de c.

int strcmp (s1, s2) char *s1, *s2;

Compare s1 et s2.

résultat = 0 (s1 = s2), <0 (s1 < s2), >0 (s1 > s2).

int strncmp (s1, s2, n) char *s1, *s2; int n;

compare les n premiers caractères.

int strlen(s) char *s;

Retourne la longueur de s.

Fig-II-6 Fonctions sur les Chaînes de Caractères

(31)

Expressions De Base

Remarque: Une différence (à ne jamais oublier) existe toutefois entre une chaîne considérée comme tableau de caractères (char[]) ou comme pointeur vers caractère (char*): dans le cas d'un tableau, la chaîne de caractères se trouve dans l'espace d'adressage normal du programme, et dans le cas d'un pointeur, la chaîne se trouve dans un espace dynamique, appelé tas (Heap).

Par conséquent, le pointeur en question doit être initialisé pour lui allouer de la place. Plus exactement, allouer la place à l'objet pointé et affecter l'adresse de cet objet au pointeur. On utilise pour cela la fonction C malloc() définie dans #include

<malloc.h>. (Nous détaillons cela dans le Chapitre IV).

Exemple: soit char* ps;

ps = malloc (80); alloue 80 octets pour ps. On peut alors écrire:

ps = .../* expression chaîne */

Noter au passage que "chaine", constante tableau de caractères, est une expression à valeur d'adresse . L'écriture:

PointeurChar = "chaine";

alloue un espace pour la chaîne chaine et affecte son adresse (celle du premier caractère) au pointeur. Ce dernier peut, ainsi, se passer d'une initialisation préalable par malloc().

L'écriture, inhabituelle:

"chaine"[3]

quand à elle, est le 4e caractère 'i' de la chaîne chaine. Remarquer aussi que l'expression

"chaine"[3] = 'î' est correcte (que fait-elle et quel est son type?)

Les fonctions sur chaînes de caractères sont données dans la figure II-6. La plupart rendent un pointeur sur une chaîne. Les trois dernières fonctions retournent, on l'aura compris, un entier.

Pour les autres fonctions les paramètres, ainsi que le résultat, sont des pointeurs sur (le 1er caractère de) la chaîne. Les paramètres effectifs peuvent être des tableaux de char ou des pointeurs char*.

(32)

Langage C

26 Exemple:

char *x,*y ,*z ;

x="toto\0"; y="titi\0";

z= strcat (x,y);

donnera pour z (et x!) "tototiti\0". Donc on peut se contenter d'écrire

strcat(x,y);

et récupérer le résultat dans x. Remarques:

1) On ne peut pas comparer (<, >, ==, <=, >=) deux chaînes de caractères (les opérateurs de comparaison ne marchent que pour des objets scalaires). Il faut utiliser la fonction strcmp().

2) Comme les tableaux en C ne sont pas affectables, strcpy() peut servir pour affecter un tableau de caractères à un autre.

3) Toutefois, quand x et y sont des chaînes, x = y; n'est pas la même chose que strcpy(x, y). Dans un cas, (figure II-7), x et y designeront le même objet chaîne, celui pointé par y auparavant, dans l'autre, il y a copie de l'objet. x et y désigneront deux objets différents —mais ayant la même valeur. (Exercice le vérifier).

x y

toto titi

x y

x y toto

titi

titi titi

(a) (b) (c)

Fig-II-7 Affectation vs Copie

(a) est la situation avant; (b) est la situation après x = y; (c) est la situation après strcpy(x,y). Noter que dans le cas (b), le premier espace alloué à x est inutilisable9.

9 En termes plus modernes, le cas (c) est appelé deep copy (copie en profondeur).

Le cas (b) est alors une simple affectation.

(33)

Expressions De Base

5. Des Opérateurs de C

Dans la figure II-8 on peut voir les opérateurs qu'on peut retenir dès à présent. Ils sont listés par ordre de priorité décroissante et par groupe de même priorité. L'associativité signifie qu'en l'absence de parenthèses et pour des opérateurs de même priorité, l'évaluation d'une expression se fait dans le sens droite-gauche ( <--- ) ou gauche-droite ( ---> ).

Opérateur Sens Exemple Associativité

&

* - ++

-- sizeof()

Adresse de Indirection

- unaire Incrémentation Decrémentation

Taille de

&a

*p -4, -(b*b)

++i, j++

--i, j-- sizeof(i) sizeof(float)

<---

* / /

%

Multiplication Division (réelle) Quotient (entier) Modulo (reste)

2 * 4 4.0 / 5

9 / 3 9 % 7

--->

+

- Addition

Soustraction

2 + 4

3 - 5 --->

=

op= Affectation Changement

x = 8, x=y=0 x += 5 y /= 2

<---

Fig-II-8 Quelques Opérateurs de C Opérateur =

Comme il a déjà été mentionné, l'affectation est considérée en C comme un opérateur normal. Quoiqu'à l'usage il sert à écrire l'instruction d'affectation classique comme dans

delta = b*b - 4*a*c;

Mais il n'est pas rare de voir des écritures comme x = y = 1;

(34)

Langage C

28 while ( s[i] = s[j] ) ...

qui affecte s[j] à s[i] tout en testant si s[j], valeur de l'expression, devient nulle... Cette écriture condensée s'est révélée très pratique, mais il faut s'en méfier (ne pas lire tant que s[i]

égale s[j] ! ).

Opérateur ++

i++ (resp. i--) incrémente (resp. décrémente) i de 1. Mais la valeur de l'expression i++ est celle de i avant l'incrémentation. De même pour i--. On parle de post incrémentation ou post décrémentation.

++i (resp. --i) incrémente (resp. décrémente) i de 1 aussi.

Mais la valeur de l'expression est celle après incrémentation (resp. décrémentation). Ainsi

i=5;

x=i++; donne 5 pour x et 6 pour i. x=++i; donne 6 pour x et pour i.

On notera que les deux instructions suivantes sont identiques (pourquoi?).

i++;

++i;

6. Règles de Conversion

Le résultat d'une expression est en générale de même type que ses opérandes. Mais il arrive que des fois on combine plusieurs types dans une même expression. Quel type doit l'emporter alors?

6.1. Conversion Implicite

Il existe des règles de conversions implicites10 faites par le compilateur. L'entier l'emporte sur le caractère et le réel l'emporte sur l'entier.

10 Y faire très attention, c'est parfois source d'erreurs.

(35)

Expressions De Base

De façon générale, pour chaque opérateur arithmétique binaire, si deux opérandes sont de type différents, alors il y a conversion. Dans le sens caractère vers entier vers réel. Plus exactement:

- S'il existe un opérande de type double ou float alors le calcul s'effectue en double, et le résultat est de type double.

- Sinon (on n'a pas de réels) s'il existe un opérande de type long alors calcul en long.

- Sinon s'il existe un opérande unsigned alors calcul en unsigned.

- Autrement le calcul est en int.

Mais il faut noter aussi qu'avant un calcul, short et char sont toujours convertis en int, et float est toujours converti en double.

La conversion se fait aussi lors de l'affectation. la valeur de la partie droite est convertie vers le type de la partie gauche qui est aussi celui du résultat final.

Exemples:

float x, y;

x = 7; x devient 7.0, conversion d'affectation y = x / 2; y devient 3.5 ( 7.0 / 2.0, calcul

effectué en double) int x, y;

y = 5;

x = y / 2; x devient 2 float x, y;

y = 5 / (2 + 1); y devient 1.0 car (division entière de 5 par 3 = 1)

x = y + 1; x devient 2.0 float x, y; int i;

y = 3 / 2; y devient 1.0

i = y + 1.5; i devient 2 (troncature de 2.5)

(36)

Langage C

30

La conversion la plus importante à noter, du point de vue réversibilité, est celle de char (8 bits) vers int (16 ou 32 bits).

En général c'est une extension du signe. Mais cela dépend de l'architecture de la machine. Si le caractère a une valeur (ASCII)

< 128, (dernier bit = 0), alors l'entier a la même valeur. Sinon (valeur ASCII >= 128) le bit de gauche est à 1, et il est recopié à gauche pour faire un entier négatif ! Valeur ASCII perdue.

La conversion inverse, de int vers char est une simple troncature des bits d'ordre > 7, qui ne fait pas perdre la valeur ASCII (entier positif).

int i; char c;

c = 'd';

i = c; extension de signe (positif) c = i; troncature

La valeur de c n'a pas changé, c = 'd' toujours.

Par contre l'inverse, char vers int, n'est pas toujours juste, comme le montre l'exemple suivant :

main(){

int i;

char c;

i = 200;

c = i; /* c = ascii(200) */

i = c; /* extension de signe pour i */

printf("%c %d\n",c,i);

i = 100;

c = i;

i = c;

printf("%c %d\n",c,i);

}

qui imprime

H -56 <--- H ou un autre caractère d 100

Dans la première partie de cet exemple on a

i= 200 = 128 + 64 + 8 = 00000000 11001000 et

(37)

Expressions De Base

c=11001000 = 100 1000 = 72 = 'H' (si la conversion ne considère que 7 bits)

i = 11111111 11001000 = -56 (extension de signe) et dans la deuxième partie

i = 100 = 64 + 32 + 4 =00000000 01100100 et c = 01100100 = 110 0100 = 'd'

i redevient 00000000 01100100 (extension de signe) donc 100

Sachez aussi que si x est float et i est int, alors x = i et i = x causent des conversions. float vers int est alors une troncature de la partie décimale. De même double est converti vers float par arrondi et troncature de la mantisse, et float est converti vers double par allongement avec des 0 de la mantisse.

Long int est converti vers short ou char par troncature des bits de poids fort.

Remarque: Les arguments des fonctions étant des expressions, la conversion se fait et les valeurs passées à une fonction sont toujours double ou int selon le cas.

En programmation, il est de bonne discipline de ne pas laisser trop s'opérer les conversions implicites. Avec float x, écrire plutôt x = x + 5.0 que x = x + 5 . La compréhension des programmes est meilleure et on contrôle soi-même ses instructions.

6.2. Conversion Explicite

C offre aussi un moyen de forcer une conversion en cas de nécessité. La ligne suivante convertit l'entier 2 en réel float.

(float ) 2 et

int n;

sqrt ( (double) n )

fait passer un flottant double à la fonction de bibliothèque sqrt() dont l'argument est double. De façon générale on écrit:

(38)

Langage C

32

pour convertir le résultat de l'expression vers le type nommé. Par exemple:

(float ) (i+5)

convertit le résultat de i+5 vers float et pt = (int*) malloc(sizeof(int));

convertit un pointeur char* , retour de malloc(), vers un pointeur int* sur entier.

Mais ce type de conversion, appelé cast, ne touche pas à la valeur de son argument:

(double) n

ne modifie pas la valeur de n. Elle produit la même valeur dans le type double.

7. Expressions Conditionnelles

On aura remarqué que le type booléen est absent de C.

Qu'importe, dans les tests on emploie des expressions numériques avec la signification FAUX si le résultat est 0, VRAI sinon (i.e.

différent de 0).

C permet l'utilisation des opérateurs relationnels et logiques pour former des expressions conditionnelles. Les voici par groupe de priorité décroissante. Ils s'associent tous de gauche à droite (--

>). Pour certains, la notation n'est pas très classique... Faire attention surtout au test d'égalité. Il n'est par rare d'écrire par erreur

if (a=b) au lieu de

if (a==b)

(39)

Expressions De Base

Symbole Signification

== égal à (deux signes =)

!= différent de

> supérieur à

< inférieur à

>= supérieur ou égal à

<= inférieur ou égal à

&& "et" logique

|| "ou" logique

! "non" logique Exemples:

x == y x égale y

(x + 4) != 3 (x + 4) différent de 3

(a < b) && (b < c) b supérieur à a et inférieur à c

a < b && b < c même chose car < est prioritaire sur &&

!b < c (non b) est inférieur à c !(b < c) b n'est pas inférieur à c a < b < c a un sens! (quel est-il?)

Une syntaxe particulièrement utile, quoiqu'illisible parfois est

exp_c ? exp_1 : exp_0

qui signifie: évaluer exp_c et si elle est non nulle alors évaluer exp_1, qui est alors le résultat, sinon évaluer exp_0 et c'est le résultat. Exemples:

max = (a > b) ? a : b ; signifie:

max = si a > b alors a sinon b On peut imbriquer ces expressions

z = (a > b) ? (a > c) ? a : c : b;

Une expression conditionnelle de ce genre peut être utilisée

(40)

Langage C

34

sont différents il y a conversion selon les règles précitées indifféremment de la valeur du test.

L'exemple suivant imprime les 100 premiers entiers à raison de dix par ligne, séparés par un espace.

for (i=1; i <= 100 ; i++)

printf("%3d%c", i, (i%10) == 0 ? '\n' : ' ');

Résultat:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

7.1. Opérateurs Logiques sur les Bits.

En C on peut manipuler la configuration binaire d'un objet.

On a les opérateurs:

& ET bit à bit (& binaire)

| OU logique bit à bit

^ OU exclusif bit à bit

<< décalage à gauche

>> décalage à droite

~ complément à un (les 1 deviennent 0 et inversement).

~ est un opérateur Unaire.

Ces opérateurs doivent s'appliquer quand ils ont un sens. Ils n'en ont pas sur float ou double par exemple.

On peut utiliser & pour masquer des bits comme par exemple

c = i & 0177 (0177 étant 01 111 111)

qui met à 0 tous les bits de i sauf les 7 bits de poids faible. Ainsi c devient ASCII

internationalement valide (entre 0 et 127).

(41)

Expressions De Base x = x | MASK

met à 1 les bit de x situés vis à vis des 1 de MASK, une constante donnée.

x & ~077

met à 0 les 6 bits de droite dex (~077 = 11 000 000).

Les opérateurs << et >> servent à faire des décalages de n bits où n est le deuxième argument.

x << 5 décale x de 5 bits à gauche x >> 2 décale x de 2 bits à droite

Dans le premier cas, x reçoit des 0 à droite et dans le second cas, des 0 ou des 1 selon son signe. Mais cela dépend des architectures machine.

Pour illustrer un usage de ces opérateurs, la figure II-9, est le code d'une fonction bin(n) qui retournent la configuration binaire de son argument entier n. Le résultat est une chaîne de caractères 0 ou 1.

char b[16]; /* tableau résultat */

char *bin(short n) {

/* routine qui convertit n en sa représentation * binaire b tableau de '0' ou '1' .

*/

short i,j,k;

k = 16;

i = n; /* i variable à décaler */

for(j=0; j<=15; j++){

if ((i % 2) == 0) /* si i est pair */

b[k--]='0'; /* son 1er bit est 0 */

else

b[k--]='1'; /* sinon il est 1 */

/* decaler i de 1 bit à droite */

i >>= 1;

}

return (b);

}

Fig-II-9, Configuration Binaire d'un Entier Court

Références

Documents relatifs

Pour les noyaux instables présentant un excès de protons, on observe l'émission d'un positon e + (encore appelé positon bêta ou. anti-électron) et d'un

Dans le cas d'un écoulement d'eau et d'air dans un milieu poreux, la variation de l,a perméabilité relative à l'eau (rapport ke/k de la perméabilité effective à la

e coursename : afficher le nom d’un professeur le plus ancien souhaitant enseigner le cours coursename f filename : recopie la liste chaînée qui en résulte dans le

siècle, Traité, page 10 : « la probabilité mathématique se forme en divisant le nombre de chances favorables à l'événement, par le nombre total des chances ; mais il faut bien

L’intégrale stochastique considérée comme une intégrale par rapport à une mesure vectorielle.. Annales scientifiques de l’Université de Clermont-Ferrand 2, tome 58, série

complètement vocalisé ; au contraire un texte arabe usuel ne peut être lu correctement que par celui qui le comprend, une forme telle que &lt;• &#34; t -.

En somme pour la chaîne borroméenne il n’y a pas de différence entre la chaîne tétraédrique et la chaîne faite de ronds.. Puisqu’elles sont univoques dans

- Ecrire une fonction récursive qui calcule le PGCD de deux entiers positifs. On suppose qu’on ne peut effectuer que