Les bases du C - 1ère partie

Écrit le 19/04/2004 par BlackSmurf
Dernière mise à jour : 01/02/2006

Histoire du C

Le langage C a été mis au point par D.Ritchie et B.W.Kernighan au début des années 70. Leur but était de permettre de développer un langage qui permettrait d'obtenir un système d'exploitation de type UNIX portable. D.Ritchie et B.W.Kernighan se sont inspirés des langages B et BCPL, pour créer un nouveau langage:
le langage C.

La première définition de ce langage a été donnée dans leur livre commun "The C programming language".
Toutefois, suite à l'apparition de nombreux compilateurs C, l'ANSI (abréviation de American National Standards Institute) a décidé de normaliser ce langage pour donner ce que l'on appelle le C-ANSI. Suite à cette norme, Ritchie et Kernighan ont sorti une deuxième édition du livre en intégrant les modifications apportées par l'ANSI.

Le langage C reste un des langages les plus utilisés actuellement. Cela est dû au fait que le langage C est un langage comportant des instructions et des structures de haut niveau (contrairement à l'assembleur par exemple) tout en générant un code très rapide grâce à un compilateur très performant.
Les performances du langage C sont en réalité dues au fait que les instructions du langage sont proches du langage machine, ce qui se traduit par un programme compact (donc rapide) dans la mesure où le programmeur respecte une syntaxe proche des spécifications...

Ainsi, les programmes en C sont très peu longs. En contrepartie le langage C reste un langage assez compliqué.

(Source : http://www.commentcamarche.com)

Après vous avoir présenté le langage C, nous allons commencer par le début de votre histoire de programmeur. La route sera longue, et nombreuses seront les épreuves à franchir.

Les couches Pampers

Bien tout d'abord, nous allons attaquer par le principe de création d'un programme mais d'abord, il faut comprendre qu'il existe quatre niveaux que l'on peut qualifier de "couches".

Vous apprendrez que sur les ordinateurs on parle souvent de couches, pour représenter des niveaux qui s'empilent et qui reposent sur la couche inférieure. Bref, tout d'abord on a la Couche Matériel qui comme son nom l'indique représente tout ce qui "hardware" en d'autres termes, le matériel pur et dur de votre ordinateur.

Sur cette couche, s'empile une autre couche qui est celle du Système d'Exploitation (OS). Le système d'exploitation jouera le rôle d'interface entre le matériel et la couche suivante ainsi qu'avec l'utilisateur, c'est à dire vous.

La 3ème couche, est celle des programmes ! Eh oui enfin... lool.En effet un programme est comme dans un moule, en informatique on parlera de format, afin que la couche inférieure, c'est à dire le système d'exploitation puisse le reconnaitre et l'exécuter. Sous Windows le format est le .EXE qui précède du défunt .COM (utilisé autrefois au début du DOS, mais qui perdure...).

Enfin la dernière couche et celle des documents. Un document ne peut s'exécuter qu'en fonction d'un programme. Il est aussi dans un certain format. Chaque format de document peut donc être lu par des programmes bien spécifique, comme par exemple sous windows les fichiers au format .TXT se lise avec le programme NOTEPAD.EXE (par défaut).

Le Principe des programmes

Qu'est-ce qu'un programme ? (Tiens ça me rappelle quelque chose...:P) Un programme est tout simplement une suite d'instructions qui s'exécutent. Ces instructions sont envoyé au microprocesseur, qui se charge de les traiter, et de contrôler l'ensemble de votre Unité Centrale (UC). Donc si vous me suivez toujours, il s'agit en fait d'un dialogue avec le microprocesseur: le programme communique avec lui.

Comme vous vous en doutez, pour communiquer il vous faut un langage de communication. Et paf! Voilà le problème posé ! Quel est le langage qu'il faut utiliser pour parler avec ce microprocesseur ? Ce langage se nomme le langage machine. Il faut savoir que le langage machine est fait de 0 et de 1 (binaire). Pourquoi du binaire ? La simplicité même. Comme le microprocesseur est fait de composants électroniques, il ne circule que du courant. Et donc par convention, faire passer du courant dans une certaine fourchette de Volts (par exemple entre 3V et 4V) équivaut à 1, en faire passer dans une autre fourchette (par exemple entre 1V et 2V) équivaut à 0.

Bref nous voilà avec notre assemblage de circuits électroniques, notre convention pour communiquer avec lui, bah on va pouvoir écrire des programmes avec des 0 et des 1 !!!! C'est pas génial ?! Hum oué, je vois que vous aimez pas ! C'est exactement ce que les développeurs se sont dis ! Et c'est de la qu'est parti la vie...

Afin de pouvoir contrôler un microprocesseur par programmation, les développeurs ont ajouté un jeu d'instructions permettant donc de mieux commander le sujet. Ces instructions sont appelées Assembleur. En voici un exemple :

add di, bx

mov es:[di], al

in al,dx

test al, 08h

jz 0011423

N'est-ce pas beau ? Moué... Il faut savoir que l'assembleur est le langage naturel de l'ordinateur et sa maîtrise vous permettra de faire beaucoup de choses. Mon professeur (un livre !) disait : "Le langage Assembleur, c'est vraiment simple, c'est comme de programmer en ADN !". Je trouve cette remarque tout à fait vraie. Toutefois même si ce langage est certes très puissant, il rebute beaucoup de personnes, et sa programmation est relativement longue.

Et l'homme se remua les fesses, et inventa les langages dis de "haut niveau", plus proche du langage de l'homme. Et c'est la que des langages comme le Basic, le Fortran, l'Ada, le Cobol, le Pascal, le C... virent le jour. Mais comment fonctionnent tous ces langages ? Comment peut-on écrire des EXE (pour windows) à partir d'un langage ? Et bien avec des Compilateurs.

Les compilateurs, prennent un fichier avec du code écrit dans le langage spécifique du compilateur même, et vont transformer ce jolie fichier (qui est du simple texte) en un exécutable. Et pour la peine on va prendre forcement l'exemple des compilateurs de C. Voici les étapes de la "compilation" d'un fichier C:

http://www.game-lab.com/images/tuts/c_bases1/compil00.jpg

Le compilateur prend le fichier C le compile en fichier OBJ. C'est à dire qu'il le traduit en langage machine ! C'est jolie tout ça, cependant ce fichier OBJ ne peut s'exécuter ! Et pourquoi ? Eh bien tout simplement parce que ce fichier n'a pas encore été converti en exécutable pour un système d'exploitation. Je parle du format qu'on les fichiers EXE dans un système d'exploitation. Exemple, si je veux qu'il s'exécute sous Windows, il faut que je convertisse ce fichier OBJ dans le format EXE pour Windows. Cet étape s'appelle donc l'Édition de liens.

Et voilà ! Maintenant vous savez comment un programme est créé à partir d'un langage !

Expliquons maintenant les règles de programmations en C.
Ce langage possède certains mot clé. Les mots clé sont des commandes déjà intégré au langage et par conséquent
ils ne peuvent être qu'utilisé. Par exemple pour créé une nouvelle variable en C on procède comme suit:

int maVariable;

Ceci déclare une nouvelle variable. Une variable est comme une sorte de boite d'une certaine taille dans laquelle on va ranger
une valeur. La taille des ordinateurs ? L'octet. Une variable de type int, signifie qu'elle est un ENTIER et donc qu'elle prend en mémoire
(dans la RAM) 2 ou 4 octets.

Bien revenons au problème expliquer plus haut. Si par exemple j'écris en C ceci:

int main;

cela provoque une erreur. Pourquoi ? Parce que main est un mot clé il fait parti intégrant dans le langage du C et ne peux donc être
redéfini. On ne peut juste l'utiliser. Le C différencie les majuscules des minuscules. Exemple:

// ces deux caractères définissent que ce qui suit sera un commentaire

int toto; // ok
int Toto; // ok
int tOto; // ok
int toto; // faux car int est un mot clé et il est défini en minuscule

La dernière commande est bien entendu fausse. Chaque ligne d'instructions en C fini par un ; afin d'indiquer d'indiquer au compilateur
que la ligne est fini et qu'il faut passer à la suivante.

Comme je vous sens impatient pour faire du code, je vais donc commencer avec un exemple connu à travers le monde entier, j'ai nommé : HELLO THE WORLD!

#include <stdio.h>

void main(void)
{
printf("Hello the World!rn");
}

Ce code source a pour but d'afficher le message: hello the world!

Tout d'abord nous allons traité la commande " void main(void) ". Il s'agit en fait d'une déclaration d'une fonction. Une fonction exécute l'ensemble des instructions qu'elle contient. Ici cette fonction n'est pas n'importe laquelle, c'est le point d'entré du programme, c'est-à-dire que lorsque nous exécuterons notre programme (une fois compilé) il exécutera les instructions qui se trouveront dans cette fonction. Vous pouvez remarquer que le symbole qui marque le début de ma fonction est "{" et celui qui marque la fin est "}".
La déclaration de fonctions sera traitée dans le chapitre "Fondements du C".

Ensuite dedans, on trouve un nom bizarre " printf ". Il s'agit en fait d'une fonction déjà existante dans le langage C ! En effet, dans tous les langages on trouve des fonctions et des opérateurs permettant d'effectuer toutes les opérations possible de base. Ici la fonction "printf" a la capacité d'afficher en message le 1er paramètre que l'on va lui donner. Ici le 1er paramètre est : "hello the world!". Pour l'instant retenez que chaque fonction a cette syntaxe: <nom_de_la_fonction>(param1, param2, ...). Ici le 1er paramètre est une chaine de caractères. On le remarque notamment grâce aux guillemets " " qui l'entoure.

Pour indiquez à C qu'une instruction est terminée et qu'il doit passer à l'instruction suivante, on la termine par le caractère point-virgule " ; ".

Enfin, tout en haut vous voyez l'instruction : " #include <stdio.h> ". Il s'agit d'une commande dite commande préprocesseur. A quoi elle sert ? Eh bien il s'agit tout simplement d'indiquer à C d'ouvrir le fichier
" stdio.h " qui se trouve dans le dossier include de votre compilateur. Cette opération est réalisée grâce à la commande : " #include <nom_du_fichier_h> " (cf. préprocesseur). Pourquoi ajouter ce fichier alors ? Parce qu'il est en fait une librairie qui contient des définitions de types, de fonctions, et beaucoup d'autres choses. On peut donc en déduire, que " stdio.h " nous sers tout simplement pour déclarer la fonction " printf ". Toutes les fonctions de base sont définies dans des fichier h. Le fait d'appeller une de ces fonctions de bases, nécessite d'indiquer à C où chercher sa définition.
Les commandes préprocesseur seront traités dans le chapitre "Préprocesseur".

Les commentaires

Qu'est-ce qu'un commentaire ? Il s'agit d'une ou plusieurs ligne où l'utilisateur pourra saisir tout ce qu'il lui passe par la tête ! En bref, les commentaires, sont des messages en francais ou en chinois, ou en pékinois, ou encore en schtroumpf, tout ce dont vous voulez, qui vont vous aidez dans votre tâche de programmeur ;^)

A quoi servent-ils ? Lorsque vous créez des programmes qui font plus de 10 000 lignes de code, vous serez peut être d'accord avec moi, insérer quelques commentaires par-ci, par-là dans le code nous permettrais de bien comprendre ce que l'on fait.

Comment écrire un commentaire ? En C il existe deux facons de déclarer des commentaires. Si votre commentaire doit tenir sur 1 seule ligne, on utilisera alors : " // ", sinon pour un commentaire sur plusieurs ligne, on utilisera pour marquer le début : " /* ", et pour marquer la fin : " */ ". Voici un exemple dans le programme "HELLO THE WORLD!":

// ceci est un commentaire sur une ligne
/* ceci est un commentaire sur 
    plusieurs 
    lignes
*/



// définition du point d'entrée
void main(void)
{
printf("Hello the World!\\r\\n");
}

Le compilateur, lorsqu'il traite votre fichier C saute les lignes commentaires, et s'occupe uniquement de votre code. Les commentaires servent énormement à tous bon programmeur, alors sachez en abuser, car c'est gratuit !!

Les types de données

Dans tous langages, lorsque vous programmez, vous devez manipuler des données, afin de traiter les informations. Toutes ces données, doivent être ranger dans des types de données.

Tout d'abord, voici un schéma représentant la codification d'un octet en mémoire:

http://www.game-lab.com/images/tuts/c_bases1/binaire00.jpg

Un octet équivaut à 8 Bits (0 ou 1). Avec un octet on possède 256 façons de placer des 1 et des 0, on peut donc obtenir alors un nombre compris entre 0 et 255, soit 256 nombres possibles.

Pourquoi doit-on déclarer le type des données ? Pour que C puisse réservé la mémoire dans une certaine taille, et sa forme de mémorisation. En C, il existe deux types de données: celles qui sont standard (prédéfinis) ou celle que les utilisateurs crée (définis).

Bref, dans C on trouve donc comme types :

Type Entier (sans décimales)

- type char : défini un type entier occupant 1 octet en mémoire
- type short : défini un type entier occupant 2 octets en mémoire.
- type int : défini un type entier occupant 2 ou 4 octets en mémoire.
- type long : défini un type entier occupant 4 octets en mémoire.

Tous les types peuvent être déclarés soit signed (par défaut) soit alors unsigned. Dans le premier cas, la donnée est signée, c'est à dire que sa valeur peut être négative. Tandis que dans le deuxième cas, la valeur sera positive. On obtient donc le résultat suivant:

- type signed char : la valeur de cette donnée sera comprise entre -128 et 127.
- type signed short : la valeur de cette donnée sera comprise entre -32 768 et 32 767.
- type signed int : la valeur de cette donnée sera soit de type signed short, soit de type signed long.
- type signed long : la valeur de cette donnée sera comprise entre -2 147 783 648 et 2 147 483 647.

- type unsigned char : la valeur de cette donnée sera comprise entre 0 et 255.
- type unsigned short : la valeur de cette donnée sera comprise entre 0 et 65535.
- type unsigned int : la valeur de cette donnée sera soit de type unsigned short, soit de type unsigned long.
- type unsigned long : la valeur de cette donnée sera comprise entre 0 et 4 294 967 295.

Voila ! Mais peut-être vous vous posez la question suivante: Comment selon que le type soit signée ou pas, la fourchette de nombre diffère pour un même type ??? Et bien c'est simple ! On va prendre le type le plus simple: Le type char.

char occupe en mémoire un et un seul octet, et en binaire il n'existe pas de nombre négatif. Alors comment peut-on dire que le type signed char existe ? Le type signed, réserve simplement le 8ème bit pour dire si notre type est positif ou négatif. Si le 8ème bit vaut 1 alors notre nombre est négatif, sinon il est positif. Donc si on refais nos calculs pour connaitre le nombre de possibilités d'un type, le fait qu'on réserve tout le temps le 8ème bit pour le signe, alors il ne nous reste plus que 7 bits pour notre nombre ! Soit 2^7 = 128 possibilités.

On obtient donc pour le type signed char (7 bits) : -128 à +127. Tandis qu'avec le type unsigned char (8 bits) : 0 à +255. C'est le même principe pour tous les autres type entiers.

En C, le type par défaut est signed (c'est à dire sans préciser si il est signed ou unsigned).