Obligement - L'Amiga au maximum

Samedi 21 octobre 2017 - 23:27  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - principes de base et philosophie
(Article écrit par Diallo Barrou et extrait d'Amiga News - juillet 1993)


Parmi tous les langages existants sur Amiga, il en est un qui de par sa structure et sa puissance de programmation fait quasi l'unanimité parmi les utilisateurs professionnels ou non. Vous l'avez reconnu, c'est le langage C.

Suite à la difficulté d'un grand nombre d'amigaïstes à se mettre à la programmation de ce langage assez rebutant au début, voici une petite introduction aux principes de base et à la philosophie du C ANSI. Pourquoi ANSI ? Tout simplement parce que c'est une norme qui assure la portabilité des programmes écrits en C sur toutes les machines munies d'un compilateur ANSI. Que les programmeurs avertis se rassurent, des trucs et astuces ne manqueront pas de vous étonner en temps voulu !

Nous avons de très bons compilateurs C sur Amiga et même des gratuits (comme DICE ou ZC) dans la collection Fred Fish qui permettent à n'importe quel débutant ou programmeur confirmé de s'initier au C. Ceci dit, à vos clavier, et tout de suite quelques commandes pour compiler un programme C sur votre compilateur une fois installé :

Avec DICE :

dh0:1> dcc -f mon_programme.c -o mon_programme

Avec Aztec C 5.x :

dh0:1> cc mon_programme.c

Puis pour le "linkage" :

dh0:1> ln mon_programme -lc

Avec Lattice C :

dh0:1> lc mon_programme.c -L

Bien sûr, chaque compilateur possède ses options qui sont d'ailleurs souvent en très grand nombre. Nous ne rentrerons pas des les détails, les manuels sont là pour ça !

Prémices...

Le langage C est très pauvre en instructions (une trentaine), mais ce qui fait sa puissance, ce sont ses opérateurs et bien sûr ses bibliothèques de fonctions riches et variées. J'ai bien dit de fonctions, car contrairement au langage Pascal, le C n'a pas la notion de procédure, nous verrons cela plus tard.

/* Voici le plus petit programme C possible */
main()
{
}

Entre les accolades viennent se loger les instructions du programme et les appels aux fonctions des bibliothèques. Le passage d'un programme source C à l'exécutable nécessite au compilateur plusieurs passes, nous allons nous pencher sur la première qui est l'étape du préprocesseur.

Le préprocesseur

Souvent peu ou mal utilisé, cette étape de compilation est pourtant essentielle et apparaît comme l'un des atouts du C. Elle permet de charger des fichiers d'entêtes standards (les .h) qui contiennent, entre autres, les noms des fonctions que l'on veut utiliser, mais aussi de définir des constantes et des macros. Les directives du préprocesseur ont la particularité de commencer par un ou deux "#".

#include <stdio.h>
#define PI 3.14159265
main()
{
    printf("Hello world!\n");
    printf("PI = %f\n", PI);
}

Ce petit programme charge à la compilation le fichier d'entête "stdio.h" grâce à la directive "#include" qui contient la définition de la fonction "printf()", qui va être utilisée pour afficher les messages à l'écran. La seconde ligne définit la constante "PI", qui sera réutilisée pour être affichée. Le préprocesseur peut aller plus loin et tester si la constante PI existe et auquel cas ne pas la redéfinir. La syntaxe est la suivante :

#ifdef CONSTANTE
#else                    /* optionnel */
#endif

Ou :

#ifndef CONSTANTE
#else                    /* optionnel */
#endif

#ifndef PI               /* SI PI n'existe pas alors */
#define PI 3.14159265    /* la définir               */
#endif                   /* FIN-SI                   */

Ces directives de compilations peuvent bien sûr être imbriquées à souhait :

#ifndef VRAI             /* SI VRAI n'est pas défini */
     #ifndef FAUX        /* SI FAUX n'est pas défini */
         #define VRAI 1
         #define FAUX 0
     #else               /* SINON */
         #define VRAI 1
     #endif              /* FIN-SI */
#endif                   /* FIN-SI */

Les macro-constantes (car ce sont leur véritable nom) peuvent être elles aussi imbriquées :

#define PI 3.14159265    /* définition de PI */
#define PI2 PI/2.0       /* définition de PI/2 grâce à PI */
#define PI4 PI2/2.0      /* définition de PI/4 grâce à PI/2 */

Le préprocesseur connaît bien d'autres directives telles que "#elif" pour SINON-SI, ou "#undef" pour retirer la définition d'une constante. La syntaxe de base de "#define" est la suivante :

#define Identificateur Chaîne

A chaque fois que le compilateur trouvera l'Identificateur dans le programme, il le remplacera par "Chaîne". Exemple :

#define MESSAGE "Hello World"
#define ENTIER 2
...
printf(MESSAGE);          /* affiche Hello World */
resultat = 1 + ENTIER;    /* Calcul le résultat = 3 */
...

Attention ! Cela met en évidence certains pièges comme le fait que le préprocesseur n'évalue pas le contenu des macro-constantes, mais procède à un remplacement bête et méchant de la chaîne :

#define ENTIER7 2+5
...
resultat = 6 * ENTIER7 ;

Le préprocesseur fournira :

resultat = 6 * 2 + 5;

Ce qui donne 17 (12+5) et non 42 (6*7) comme on aurait pu l'imaginer...

Les #define peuvent servir aussi pour la compilation conditionnelle d'une partie de programme, en effet comment faire en sorte qu'un programme se compile et s'exécute aussi bien sous 68000 que sous 68040 en tenant compte de l'évolution entre ces deux processeurs ? Eh bien, tout simplement en déclarant une constante qui contient le nom du processeur et en définissant ce que le compilateur doit faire lorsqu'un cas critique se présente :

#define MC68030     /* on définit qu'on travail sur un A3000 */
...
#ifdef MC68040      /* programme pour 68040 */
#else
#ifdef MC68030      /* programme exécuté ici car on a définit MC68030 */
#endif
#endif

Bien sûr, on peut tester ce genre de chose invariante lors de l'exécution du programme de façon différente avec la directive :

#if <expression>:

Exemple pour ne compiler qu'à partir du 68020 :

#define PROCESSEUR 68010     /* J'ai un 68010 */
#if PROCESSEUR < 68020
    printf("Il vous faut absolument un A1200 pour ce programme \n");
#else
    printf(" Ok, vous avez le matériel requis...\n");
#endif

On peut ainsi dire au compilateur de ne compiler une partie de programme plutôt qu'une autre si une condition est vérifiée.

Les macro-fonctions préprocesseur

Maintenant que nous savons comment déclarer des constantes, poursuivons avec les macro-fonctions, qui ne sont, en fait qu'une extension de la directive #define :

#define Identificateur(parametres [, ...] instructions

Tout de suite, un petit exemple qui définit une macro qui renvoit le maximum de deux nombres entiers. On utilisera l'opérateur ternaire de comparaison qui s'écrit comme ceci : "Condition ? Vérifiée : Non Vérifiée" et qui corresponds à :

if (condition) Vérifiée
   else
        Non Vérifiée

Cet opérateur permet de faire un "IF THEN ELSE" sur une ligne ! Remarquons au passage que le C n'a pas d'instruction "THEN". Voici donc la fameuse macro "MAX" :

#define MAX(nbre1, nbre2) (((nbre1) > (nbre2)) ? (nbre 1) : (nbre2))
...
      resultat = MAX(9,3);
...

Le préprocesseur remplacera la dernière ligne par son équivalent avec les bons paramètres, ce qui donnera :

resultat = 9 > 3 ? 9 : 3 ;

9 est-il supérieur à 3 ? Oui alors resultat=9 ; Vous remarquerez que dans la macro les paramètres ont été soigneusement parenthésés, voici un exemple qui illustre que l'oublie des parenthèses peut provoquer des surprises :

#define TRIPLE(nbre) nbre+nbre+nbre
...
resultat = TRIPLE(5) * TRIPLE(2);
...

Le préprocesseur remplacera la ligne des résultats par :

resultat = 5+5+5 * 2+2+2 ;

La multiplication étant prioritaire sur l'addition, la variable "resultat" sera égale à 24 (10+10+4) au lieu de 90 (15*6).

Remarque : un #define doit tenir sur une ligne sans retour chariot, il faut donc, au besoin protéger la ligne du retour chariot avec un antislash comme ceci :

#define MOYENNES (a,b,c,d,e) ((a)+(b)+(c)+\(d)+(e))/5

Voilà pour ce qui est des macros, il faut y réfléchir à deux fois avant l'élaboration d'une macro un peu complexe, mais c'est tellement pratique !

Préprocesseur : encore plus !

Nous avons vu comment créer nos propres constantes et macros, mais qu'en est-il du standard ANSI ? Celui-ci propose des constantes et des directives (un peu spéciales) qui peuvent faciliter la vie d'un programmeur en C, c'est ce que nous allons aborder maintenant.

Tout compilateur C ANSI doit définir la constante __STDC__ qui garantit sa conformité à la norme. C'est, je le répète, très important pour la portabilité et la pérénité de votre programme ! Nous allons le tester avec une nouvelle directive : #error <Message d'erreur>

/* Stoppe la compilation à cet endroit et affiche le message */

Exemple :

#if __STDC__ == 1        /* Test si ANSI */
#define ON_EST_EN_ANSI
#else
#error    Le compilateur n'est pas un compilateur ANSI
#endif

Il existe évidemment d'autres constantes standards comme :
  • __TIME__ qui contient l'heure système (hh:mm:ss).
  • __DATE__ qui contient la date système (Mmm jj aaaa).
  • __LINE__ qui contient le numéro de ligne actuel.
  • __FILE__ qui contient le nom du fichier en compilation.
Exemple :

...
printf("On est le %s, et il est %s\n", __DATE__, __TIME__);
...

Allez, une petite dernière directive et nous allons aborder les maigres opérateurs du préprocesseur, il s'agit de "#line" qui, comme son nom l'indique, permet de modifier le numéro de ligne courant stocké dans __LINE__ mais aussi éventuellement (et on ne pouvait pas le deviner) la valeur de __FILE__. Sa syntaxe est :

#line numéro_de_ligne ["nom_du_programme"]

Tout de suite un exemple :

#line 2
printf("Ligne %d\n", __LINE__);                              /* on est à la ligne 2 */
#line 3 "essai.c"
printf("Ligne %d et fichier est %s \n", __LINE__, __FILE__); /* compilation en cours sur le fichier essai.c */

Les opérateurs

Comme promis, nous allons terminer sur les trois opérateurs du préprocesseur, qui malheureusement sont mal ou peu connus, ce sont "#", "##" et "defined". Ce dernier renvoit "1" (VRAI) si sont argument est défini et "0" dans le cas contraire :

#if defined(MC68040)    /* Si MC68040 est défini alors */
#define PROCESSEUR_OK
#else
#error    Ce programme ne marche que sur A4000 !
#endif

L'opérateur #parametre et qui s'utilise dans des #define permet de passer des paramètres littéraux aux macros, le paramètre sera automatiquement mis entre guillemets avant que la macro soit transformée dans le programme :

#define DEBUG_PRG(msg) printf("MESSAGE: Fichier %s ligne %d\n\t" #msg\ "\n" ,__FILE__, __LINE__)

Cette macro (qui peut être utile pour le déverminage) prend comme paramètre "msg", et celui-ci sera remplacé par "valeur de msg", ce qui nous permet d'afficher un message avec le nom du programme et le numéro de la ligne courante facilement :

...
DEBUG_PRG(Ici tout va bien!);

Encore plus fort, l'opérateur "##" permet la concaténation de valeurs. Exemple :

#define VALEUR(var) printf("La variable essai_" #var \ " est égale à %d...\n", essai_##var)

Ici l'argument "var" est utilisé une première fois pour afficher le nom de la variable, et une deuxième pour afficher sa valeur, en sachant que mes variables commencent par "essai_". Si on appelle cette macro comme ceci :

...
int essai_indice = 10;     /* déclaration d'un entier égal à 10 */
VALEUR(indice);            /* Appel de la macro pour savoir sa valeur */

...on obtiendra le résultat suivant : la variable essai_indice est égale à 10.

Puissant le préprocesseur, non ? Sur ce, nous voilà arrivé à la fin de cet article en guise d'introduction au C ANSI. Maintenant, à vous de jouer, la prochaine fois nous verrons les types complexes et les pointeurs. Ciao !


[Retour en haut] / [Retour aux articles] [Article suivant]