Académique Documents
Professionnel Documents
Culture Documents
Tutoriel langage C
Version 0.042
Mars 1999
Introduction
Le langage de programmation C a été initialement développé par Dennis Ritchie des Laboratoires Bell et
conçu pour fonctionner sur un PDP11 avec un système d'exploitation UNIX . Bien qu'il ait été initialement
prévu pour fonctionner sous UNIX, il y avait un grand intérêt à l'exécuter sur les PC IBM et les systèmes
compatibles, ainsi que sur d'autres systèmes. Le C est excellent pour écrire des programmes au niveau
système, et l'ensemble du système d'exploitation Applix 1616/OS est écrit en C (à l'exception de quelques routines assembleur).
C'est un excellent langage pour cet environnement en raison de la simplicité d'expression, de la compacité du
code et du large éventail d'applicabilités.
Ce n’est pas une bonne langue de « début » car elle est de nature quelque peu énigmatique. Il permet au
programmeur un large éventail d'opérations allant du niveau élevé au niveau très bas se rapprochant du
niveau du langage assembleur. Il semble y avoir aucune limite à la flexibilité disponible. Un programmeur C
expérimenté a déclaré : "Vous pouvez programmer n'importe quoi en C", et cette déclaration est bien étayée
par ma propre expérience du langage. Mais en plus de la liberté qui en résulte, vous assumez de nombreuses
responsabilités. Il est très facile d'écrire un programme qui se détruit luimême à cause de petites erreurs
idiotes que, par exemple, un compilateur Pascal signalera et appellera une erreur fatale. En C, vous êtes
vraiment seul, comme vous le découvrirez bientôt.
Puisque C n’est pas un langage pour débutants, je suppose que vous n’êtes pas un programmeur débutant
et je n’essaierai pas de vous ennuyer en définissant une constante et une variable. Vous devrez connaître ces
concepts de base. Cependant, on ne s'attend pas à ce que vous connaissiez quoi que ce soit du langage de
programmation C. Je commencerai par le plus haut niveau de programmation C, y compris les concepts
généralement intimidants de pointeurs, de structures et d'allocation dynamique. Pour bien comprendre ces
concepts, cela demandera un bon moment et un travail de votre part, car ce ne sont pas des outils
particulièrement faciles à appréhender, mais ce sont des outils très puissants. Assez parlé de cela, vous
verrez leur pouvoir quand nous y arriverons, mais ne vous permettez pas encore de vous inquiéter pour eux.
La programmation en C est un atout considérable dans les domaines où vous souhaiterez peutêtre utiliser le
langage assembleur, mais préférerez qu'il s'agisse d'un programme simple à écrire et facile à maintenir. Il a
été dit qu'un programme écrit en C paierait une augmentation de 50 à 100 % du temps d'exécution, car aucun
langage n'est aussi compact ou rapide que le langage d'assemblage. Cependant, le temps gagné en codage
peut être énorme, ce qui en fait le langage le plus souhaitable pour de nombreuses tâches de programmation.
De plus, comme la plupart des programmes passent 90 % de leur temps de fonctionnement dans seulement
10 % ou moins du code, il est possible d'écrire un programme en C, puis de réécrire une petite partie du code
en langage assembleur et d'approcher la vitesse d'exécution de le même programme s’il était entièrement
écrit en langage assembleur.
Environ 75 pour cent de tous les nouveaux programmes commerciaux introduits pour les PC IBM ont été
écrits en C, et ce pourcentage est probablement en augmentation. Le logiciel système Apple Macintosh était
autrefois écrit en Pascal, mais il est désormais presque toujours écrit en C. L'intégralité du système
d'exploitation Applix 1616 est écrite en C, avec quelques routines assembleur.
Puisque C a été conçu essentiellement par une seule personne, et non par un comité, c'est un langage très
utilisable mais pas trop précisément défini. Il n’existait pas de norme officielle pour le langage C, mais
l’American National Standards Association (ANSI) a développé une norme pour le langage, elle suivra donc
des règles rigides. Il est intéressant de noter, cependant, que même s’il n’existe pas de norme, les différences
entre les implémentations sont généralement minimes. Cela est probablement dû au fait que la définition non
officielle originale a été si bien pensée et soigneusement planifiée que des extensions du langage ne sont pas
nécessaires.
Machine Translated by Google
Même si le langage C bénéficie d'un bon historique lorsque les programmes sont transportés d'une implémentation
à une autre, il existe des différences entre les compilateurs, comme vous le constaterez chaque fois que vous
essayez d'utiliser un autre compilateur. La plupart des différences deviennent apparentes lorsque vous utilisez des
extensions non standard telles que des appels au BIOS MSDOS ou des appels système Applix 1616/OS, mais
même ces différences peuvent être minimisées par un choix judicieux des moyens de programmation.
Les constructeurs Applix 1616 disposent uniquement du compilateur HiTech C. Cette version du didacticiel est
personnalisée pour s'adapter à HiTech C. La version MSDOS originale de Gordon Dodrill a été portée sur Applix
1616 (avec beaucoup d'efforts) par Tim Ward et saisie par Karen Ward. Les programmes ont été convertis en
HiTech C par Tim Ward et Mark Harvey, tandis que Kathy Morton a grandement contribué au fonctionnement de
Visual Calculator. Tous ont été testés sur le système d'exploitation multitâche Applix 1616/OS. Les disques de
distribution Applix contiennent le texte original complet de ce didacticiel, ainsi que tout le code source C converti.
Le deuxième disque contient des versions exécutables et relocalisables de tous les programmes, prêtes à être
exécutées sur un Applix 1616. Il existe également un répertoire du code source IBM original, pour ceux qui utilisent
des ordinateurs IBM et qui souhaitent les essayer avec un compilateur différent. Cette version imprimée a été
éditée, indexée et joliment imprimée par Eric Lindsay, qui a ajouté le matériel spécifique à Applix.
Cette version imprimée du didacticiel comprend des copies de tout le code, pour une référence plus facile. Il
comprend également une table des matières complète et un index.
Machine Translated by Google
1
Commencer
Ce tutoriel peut être lu simplement sous forme de texte, mais il se veut interactif. Autrement dit, vous devez compiler, modifier
et utiliser les programmes présentés ici.
Tous les programmes ont été testés à l'aide du compilateur HiTech C, et nous supposons que vous en avez une copie. De
plus, vous devriez disposer d'une copie de diverses mises à jour et fichiers d'entête pour le compilateur C, qui apparaissent
sur les disques des utilisateurs Applix.
Vous pouvez utiliser soit l'édition intégrée de l'éditeur Applix 1616/OS, soit l' éditeur Dr Doc à 30 $ en mode nondocument.
Dr Doc est un peu plus puissant, mais lorsqu'il se charge à partir du disque, son démarrage est légèrement plus lent. Le
code source a été modifié pour s'adapter à un paramètre de tabulation de 5, alors appelez votre éditeur avec des tabulations
définies sur un espacement de 5. Par exemple, edit sourcecode.c 5 vous permettrait de modifier un fichier appelé
sourcecode.c.
Avant de pouvoir réellement utiliser C, certaines exigences en matière d’équipement doivent être remplies. Vous devez
disposer d'une carte coprocesseur de disque et d'au moins un lecteur de disque. Si vos lecteurs sont inférieurs à 800 Ko,
vous aurez probablement besoin de deux lecteurs de disque. Nous supposons que vous disposez soit du multitâche 1616/
OS version 4, soit d'un MRD d'attribution disponible sur votre disque de démarrage.
Vous devez utiliser XPath et les commandes Assign pour configurer votre disque de démarrage sous une forme adaptée à
une utilisation avec C. Cela doit être fait dans le fichier autoexec.shell sur votre disque de démarrage, comme indiqué ci
dessous.
Créez une nouvelle copie amorçable de votre disque utilisateur 1616, en suivant les instructions de votre manuel d'utilisation.
Pour garantir un espace suffisant, supprimez tous les fichiers manifestement indésirables que vous remarquez sur la copie.
Copiez le contenu de votre disque de distribution HiTech C sur le nouveau disque, en conservant les sousrépertoires
identiques à ceux du disque HiTech.
Si vous avez reçu des fichiers d'entête C mis à jour ou d'autres mises à jour, copiezles également dans leurs sous
répertoires respectifs sur votre nouveau disque.
En utilisant edit, modifiez le XPath et attribuez des commandes dans votre fichier autoexec.shell dans le répertoire racine de
votre nouveau disque.
Votre XPath doit inclure /F0/bin (s'il n'est pas déjà inclus).
Ajoutez les lignes suivantes à votre autoexec.shell pour recréer l'environnement utilisé par Tim Ward lors de l'exécution
initiale de ces programmes. assign /hitech /f0/bin assign /sys /
f0/include assign /temp /rd Cela permettra
d'écrire du code sans tenir compte de l'endroit
où vous placez réellement vos
fichiers. Si vous utilisez un deuxième lecteur ou un disque dur, modifiez simplement le point d'affectation /hitech sur le lecteur
correct. C a tendance à utiliser largement les fichiers temporaires. Si vous disposez de suffisamment de mémoire disponible
sur votre disque RAM, utilisez /rd pour les fichiers temporaires. Sinon, utilisez le lecteur et le répertoire actuels, comme
indiqué par assign /temp .
Assurezvous de copier le nouveau préprocesseur C relcc.xrel du disque utilisateur dans le sousrépertoire /bin de votre
nouveau disque C.
Si vous expérimentez, vous préférerez peutêtre capturer toutes les erreurs rencontrées dans un fichier, pour une étude
ultérieure. Si c'est le
cas, utilisez relcc v file.c } errorfile
Le cas des caractères alphabétiques est significatif. Utiliser « INDEX » pour une variable n’est pas la même
chose que d’utiliser « index » et aucun d’eux n’est la même chose que d’utiliser « InDex » pour une variable. Tous les trois
font référence à des variables différentes.
2. Comme C est défini, jusqu'à huit caractères significatifs peuvent être utilisés et seront considérés comme
significatifs. Si plus de huit sont utilisés, ils peuvent être ignorés par le compilateur. Cela peut être vrai ou non pour votre
compilateur. Vous devriez consulter votre manuel de référence pour savoir combien de caractères sont importants pour
votre compilateur. Le compilateur HiTech C utilisé avec l'Applix 1616 autorise 31 caractères significatifs et ajoute un trait
de soulignement (_)
Il convient de souligner que certains compilateurs C autorisent l'utilisation du signe dollar dans un nom d'identifiant, mais
comme il n'est pas universel, il ne sera utilisé nulle part dans ce tutoriel. Vérifiez votre documentation pour voir si elle est
autorisée pour votre compilateur particulier.
Certains tutoriels que j'ai vus donnent des exemples très peu nombreux et très complexes. Ils servent vraiment
davantage à confondre l'étudiant. Ce tutoriel est tout le contraire car il s'efforce de couvrir chaque nouvel aspect de
la programmation dans un contexte aussi simple que possible. Cette méthode conduit cependant à un manque de
connaissances sur la manière dont les différentes parties sont combinées. Pour cette raison, le dernier chapitre est
entièrement consacré à l’utilisation des fonctionnalités enseignées dans les chapitres précédents. Il illustrera
comment rassembler les différentes fonctionnalités pour créer un programme utilisable. Ils sont donnés pour votre
étude et ne sont pas complètement expliqués. Suffisamment de détails sur leur fonctionnement sont donnés pour
vous permettre de comprendre leur fonctionnement après avoir terminé toutes les leçons précédentes.
1.6 Liste.xrel
Ce fichier répertoriera pour vous les fichiers source avec les numéros de ligne et le nom du fichier. Pour l'utiliser,
tapez simplement "LISTE" suivi du nom de fichier approprié. Tapez list firstex.c maintenant pour un exemple.
Le code source C est donné plus loin dans le chapitre 14 avec une brève description de son fonctionnement.
Les utilisateurs d'Applix 1616 disposent toujours de la commande d'édition intégrée, ce programme n'est donc pas
vraiment essentiel.
Le mot « main » est très important et doit apparaître une et une seule fois dans chaque programme C.
C'est le point où l'exécution commence lorsque le programme est exécuté. Nous verrons plus tard que cela ne
doit pas nécessairement être la première instruction du programme, mais qu'elle doit exister comme point d'entrée.
Après le nom du programme "principal" se trouve une paire de parenthèses, qui indiquent au compilateur qu'il
s'agit d'une fonction. Nous expliquerons exactement ce qu’est une fonction en temps voulu. Pour l’instant, je
vous suggère d’inclure simplement la paire de parenthèses.
Les deux accolades { }, proprement appelées accolades, sont utilisées pour définir les limites du programme lui
même. Les instructions du programme ellesmêmes se situent entre les deux accolades et dans ce cas, il n'y a
aucune instruction car le programme ne fait absolument rien. Vous pouvez compiler et exécuter ce programme,
mais comme il ne contient aucune instruction exécutable, il ne fait rien. Gardez cependant à l’esprit qu’il s’agit
d’un programme C valide.
main( )
L'instruction exécutable est une autre fonction. Encore une fois, nous ne nous soucierons pas de ce qu’est une
fonction, mais seulement de la manière d’utiliser celleci. Afin d'afficher du texte sur le moniteur, il est placé entre
parenthèses de fonction et délimité par des guillemets. Le résultat final est que tout ce qui est inclus entre les
guillemets sera affiché sur le moniteur lorsque le programme est exécuté.
courir.
Notez le pointvirgule ; au bout de la ligne. C utilise un pointvirgule comme terminateur d'instruction, le point
virgule est donc requis pour signaler au compilateur que cette ligne est complète. Ce programme est également
exécutable, vous pouvez donc le compiler et l'exécuter pour voir s'il fait ce que vous pensez qu'il devrait faire.
Avec certains compilateurs, vous pouvez recevoir un message d'erreur lors de la compilation, indiquant que
printf() aurait dû être déclaré comme un entier. Ignorez cela pour le moment.
principal( )
{
printf("Ceci est une ligne de texte à afficher.\n"); printf("Et ceci en est un autre
"); printf("ligne de texte.\n\n"); printf("Ceci est la
troisième ligne.\n");
Remarquez le caractère amusant vers la fin de la première ligne, à savoir la barre oblique inverse. La barre oblique
inverse est utilisée dans l'instruction printf pour indiquer qu'un caractère de contrôle spécial suit. Dans ce cas, le « n »
indique qu’un « saut de ligne » est demandé. Ceci est une indication pour ramener le curseur sur le côté gauche du
moniteur et descendre d’une ligne. Il est communément appelé retour chariot/saut de ligne. N'importe quel endroit du
texte que vous désirez, vous pouvez mettre un caractère de nouvelle ligne et commencer une nouvelle ligne. Vous
pouvez même le placer au milieu d'un mot et diviser le mot entre deux lignes.
Le compilateur C considère la combinaison de la barre oblique inverse et de la lettre n comme un seul caractère. Les
caractères exacts utilisés pour indiquer un newlin et un retour chariot sont spécifiques au système d'exploitation.
MSDOS, Unix, 1616/OS et Macintosh peuvent différer les uns des autres.
Une description complète de ce programme est désormais possible. Le premier printf génère une ligne de texte et
renvoie le chariot. Le deuxième printf génère une ligne mais ne renvoie pas le chariot, donc la troisième ligne est
ajoutée à celle du deuxième, puis suivie de deux retours chariot, ce qui donne une ligne vide. Enfin, le quatrième printf
génère une ligne suivie d'un retour chariot et le programme est terminé.
Compilez et exécutez ce programme pour voir s'il fait ce que vous attendez de lui. Ce serait une bonne idée à ce
stade que vous expérimentiez en ajoutant des lignes supplémentaires d'impression pour voir si vous comprenez
comment fonctionnent réellement les déclarations.
index ; indice =
13 ; printf("La
valeur de l'index est %d\n",index); indice = 27 ; printf("La valve de l'index =
%d\n",index);
indice = 10 ; printf("La valeur de l'index = %d\n",index);
Le point d'entrée "main" devrait maintenant être clair pour vous, ainsi que l'accolade de début. La première nouveauté
que nous rencontrons est la ligne contenant "int index;", qui sert à définir une variable entière nommée "index". Le "int"
est un mot réservé en C, et ne peut donc être utilisé pour autre chose. Il définit une variable qui peut avoir une valeur
de 32768 à 32767 sur la plupart des implémentations de microordinateur MSDOS de C. Il définit une variable avec
une valeur de 2147483648 à 2147483647 dans HiTech C. Consultez le manuel d'utilisation de votre compilateur pour
la définition exacte de votre compilateur. Le nom de la variable, "index", peut être n'importe quel nom qui suit les règles
d'un identifiant
Nous verrons dans un chapitre ultérieur que des entiers supplémentaires pourraient également être définis sur la même
ligne, mais nous ne compliquerons pas la situation actuelle.
En observant le corps principal du programme, vous remarquerez qu'il existe trois instructions qui attribuent une valeur à
la variable "index", mais une seule à la fois. Le premier attribue la valeur 13 à "index" et sa valeur est imprimée. (Nous
verrons dans combien de temps.) Plus tard, la valeur 27 est affectée à "index", et finalement 10 lui est attribuée, chaque
valeur étant imprimée. Il devrait être intuitivement clair que « index » est bien une variable et peut stocker de nombreuses
valeurs différentes. Veuillez noter que les mots « imprimé » sont souvent utilisés pour signifier « affiché sur le moniteur ».
Vous constaterez que dans de nombreux cas, les programmeurs expérimentés prennent cette liberté, probablement à
cause de la fonction "printf" utilisée pour l'affichage du moniteur.
Pour tenir nos promesses, revenons aux instructions "printf" pour une définition de leur fonctionnement.
Notez qu'ils sont tous identiques et qu'ils commencent tous comme les instructions "printf" que nous avons vues
précédemment. La première différence se produit lorsque nous arrivons au caractère %. Il s'agit d'un caractère spécial
qui signale à la routine de sortie d'arrêter de copier les caractères vers la sortie et de faire quelque chose de différent, à
savoir afficher une variable. Le signe % est utilisé pour signaler le début de nombreux types de variables différents, mais
nous nous limiterons à un seul pour cet exemple. Le caractère qui suit le signe % est un "d", qui signale à la routine de
sortie d'obtenir une valeur décimale et de la sortir. L’origine de la valeur décimale sera abordée sous peu. Après le "d",
nous trouvons le \n familier, qui est un signal pour renvoyer le "chariot" vidéo, et le guillemet fermant.
Tous les caractères entre guillemets définissent le modèle de données à afficher par cette instruction, et après le modèle,
il y a une virgule suivie du nom de variable « index ». C'est là que l'instruction "printf" obtient la valeur décimale qu'elle
affichera en raison du "%d" que nous avons vu plus tôt. Nous pourrions ajouter plus de descripteurs de champ de sortie
"%d" entre parenthèses et plus de variables après la description pour provoquer l'impression de plus de données avec
une seule instruction. Gardez cependant à l'esprit qu'il est important que le nombre de descripteurs de champs et le
nombre de définitions de variables soient les mêmes, sinon le système d'exécution sera confus et s'arrêtera probablement
avec une erreur d'exécution.
Beaucoup plus d’informations seront abordées ultérieurement sur tous les aspects du formatage d’entrée et de sortie.
Une assez bonne compréhension de ce sujet est nécessaire pour tout comprendre du formatage de sortie à l'heure
actuelle, seulement une bonne compréhension des bases.
Chargez le fichier comments.c et observezle sur votre moniteur pour un exemple de la façon dont des commentaires
peuvent être ajoutés à un programme C.
/* Ceci est un commentaire ignoré par le compilateur */
principal( ) /* Ceci est un autre commentaire ignoré par le compilateur */
{
printf("Nous examinons comment sont les commentaires "); /* Un commentaire est
peut être continué
sur une autre ligne
*/
Des commentaires sont ajoutés pour rendre un programme plus lisible, mais le compilateur doit ignorer les commentaires.
La combinaison slash étoile est utilisée en C pour les délimiteurs de commentaires. Ils sont illustrés dans le programme
présenté. Veuillez noter que le programme n'illustre pas les bonnes pratiques en matière de commentaires, mais est
destiné à illustrer où les commentaires peuvent aller dans un programme. C'est un programme très bâclé.
La première combinaison barre oblique introduit le premier commentaire et l'étoile à la fin de la première ligne termine ce
commentaire. Notez que ce commentaire est antérieur au début du programme, illustrant qu'un commentaire peut précéder
le programme luimême. Une bonne pratique de programmation inclurait un commentaire avant le programme avec une
brève description introductive du programme.
Le commentaire suivant se trouve après le point d'entrée du programme "main()" et avant l'accolade ouvrante du code du
programme luimême.
Le troisième commentaire commence après la première instruction exécutable et continue sur quatre lignes. C'est
parfaitement légal car un commentaire peut continuer sur autant de lignes qu'il le souhaite jusqu'à ce qu'il soit terminé.
Notez soigneusement que si quelque chose était inclus dans les espaces vides à gauche des trois lignes de suite du
commentaire, cela ferait partie du commentaire et ne serait pas compilé.
Le dernier commentaire se situe après la fin du programme, illustrant que les commentaires peuvent aller presque
n'importe où dans un programme C.
Expérimentez avec ce programme en ajoutant des commentaires à d'autres endroits pour voir ce qui se passera.
Commentez l'une des instructions printf en plaçant des délimiteurs de commentaires avant et après et veillez à ce qu'elle
ne soit pas imprimée.
Les commentaires sont très importants dans n’importe quel langage de programmation car vous oublierez bientôt ce que
vous avez fait et pourquoi vous l’avez fait. Il sera beaucoup plus facile de modifier ou de corriger un programme bien
commenté dans un an qu'un programme avec peu ou pas de commentaires. Vous développerez très rapidement votre
propre style de commentaire.
Certains compilateurs vous permettent d'« imbriquer » des commentaires, ce qui peut être très pratique si vous devez
« commenter » une section de code pendant le débogage. Vérifiez la documentation de votre compilateur pour connaître
la disponibilité de cette fonctionnalité avec votre compilateur particulier. Compilez et exécutez comments.c à ce momentlà.
C'est un exemple de programme bien formaté. Même s'il est très court et donc très peu efficace, il est très facile de voir
d'un seul coup d'œil ce qu'il fait. Avec l'expérience que vous avez déjà acquise dans ce tutoriel, vous devriez être capable
de saisir très rapidement le sens du programme dans sa globalité. Votre compilateur C ignore tous les espaces
supplémentaires et tous les retours chariot, vous donnant une liberté considérable quant à la façon dont vous formatez
votre programme. L'indentation et l'ajout d'espaces dépendent entièrement de vous et sont une question de goût personnel.
Compilez et exécutez le programme pour voir s'il fait ce que vous attendez de lui.
Combien de temps vous faudratil pour comprendre ce que fera ce programme ? Le style de format que vous utilisez
n'a pas d'importance pour le compilateur, mais cela vous importera lorsque vous essaierez de déboguer votre
programme. Compilez ce programme et exécutezle. Vous pourriez être surpris de constater qu'il s'agit du même
programme que le précédent, à l'exception du formatage. Ne vous inquiétez pas encore trop du style de formatage.
Vous aurez tout le temps de développer votre propre style tout en apprenant la langue. Soyez attentif aux styles lorsque
vous voyez les programmes C dans les magazines, les livres et autres publications.
Cela devrait assez bien couvrir les concepts de base de la programmation en C, mais comme il y a beaucoup d'autres
choses à apprendre, nous allons passer à une structure de programme supplémentaire.
2. Modifiez le programme pour afficher votre adresse et votre numéro de téléphone sur des lignes distinctes en ajoutant
deux instructions "printf" supplémentaires.
0 ; while (count
< 6) { printf("La
valeur du count est
%d\n",count); compte = compte + 1 ;
}
}
Nous commençons par un commentaire et le nom du programme, puis définissons une variable entière "count" dans le
corps du programme. La variable est mise à zéro et nous arrivons à la boucle while ellemême.
La syntaxe d'une boucle while est exactement comme indiqué ici. Le mot clé « while » est suivi d’une expression de
quelque chose entre parenthèses, suivie d’une instruction composée entre accolades.
Tant que l’expression entre parenthèses est vraie, toutes les instructions entre accolades seront exécutées.
Dans ce cas, puisque le nombre de variables est incrémenté de un à chaque fois que les instructions sont exécutées,
la boucle sera terminée. Le contrôle du programme reprendra à l'instruction suivant les instructions entre accolades.
Nous aborderons l’expression de comparaison, celle entre parenthèses, dans le prochain chapitre. En attendant,
acceptez simplement les expressions décrivant ce que vous pensez qu’ils devraient faire et vous aurez probablement raison.
Plusieurs choses doivent être soulignées concernant la boucle while. Premièrement, si la variable count était initialement
définie sur un nombre supérieur à 5, les instructions de la boucle ne seraient pas exécutées du tout, il est donc possible
d'avoir une boucle while qui n'est jamais exécutée. Deuxièmement, si la variable n'était pas incrémentée dans la
boucle, alors dans ce cas, la boucle ne se terminerait jamais et le programme ne se terminerait jamais. Enfin, s'il n'y a
qu'une seule instruction à exécuter dans la boucle, elle n'a pas besoin d'accolades mais peut être autonome.
{ printf("la
valeur
de i
est maintenant %d\n",i);
Ce programme est presque identique au précédent sauf que la boucle commence par le mot réservé "do", suivi
d'une instruction composée entre accolades, puis du mot réservé "while", et enfin d'une expression entre
parenthèses. Les instructions entre accolades sont exécutées de manière répétée tant que l'expression entre
parenthèses est vraie. Lorsque l'expression entre parenthèses devient fausse, l'exécution est terminée et le
contrôle passe aux instructions qui suivent cette instruction.
Plusieurs choses doivent être soulignées concernant cette affirmation. Puisque le test est effectué à la fin de la
boucle, les instructions entre accolades seront toujours exécutées au moins une fois. Deuxièmement, si "i"
n'était pas modifié dans la boucle, la boucle ne se terminerait jamais et donc le programme ne se terminerait
jamais. Enfin, tout comme la boucle while, si une seule instruction doit être exécutée dans la boucle, aucune
accolade n'est requise. Compilez et exécutez ce programme pour voir s'il fait ce que vous pensez qu'il devrait
faire.
Cela ne devrait pas vous surprendre que ces boucles puissent être imbriquées. Autrement dit, une boucle peut
être incluse dans l'instruction composée d'une autre boucle et le niveau d'imbrication n'a pas de limite.
La boucle "for" est constituée du mot réservé "for" suivi d'une expression assez grande entre parenthèses. Cette
expression est en réalité composée de trois champs séparés par des pointsvirgules. Le premier champ contient
l'expression « index = 0 » et est un champ d'initialisation. Toutes les expressions de ce champ sont exécutées
avant le premier passage dans la boucle. Il n'y a essentiellement aucune limite quant à ce qui peut se passer ici,
mais une bonne pratique de programmation exigerait que cela reste simple. Plusieurs instructions d'initialisation
peuvent être placées dans ce champ, séparées par des virgules.
Le deuxième champ, contenant ici "index < 6", est le test qui est effectué au début de chaque boucle du
programme. Il peut s'agir de n'importe quelle expression qui sera évaluée comme vraie ou fausse. (Nous en
dirons davantage sur la valeur réelle de vrai et faux dans le prochain chapitre.)
L'expression contenue dans le troisième champ est exécutée à chaque fois que la boucle est exécutée, mais
elle n'est exécutée qu'après l'exécution des instructions du corps principal de la boucle. Ce champ, comme le
premier, peut également être composé de plusieurs opérations séparées par des virgules.
Après l'expression for() se trouve toute instruction simple ou composée qui sera exécutée comme corps de la
boucle. Une instruction composée est un groupe d'instructions C valides entourées d'accolades. Dans presque
tous les contextes en C, une instruction simple peut être remplacée par une instruction composée qui sera
traitée comme s'il s'agissait d'une instruction unique en ce qui concerne le contrôle du programme. Compilez et
exécutez ce programme.
3.4 L'instruction If
Chargez et affichez le fileifelse.cpour un exemple de notre première instruction de branchement conditionnel, le
"if".
Notez d'abord qu'il existe une boucle "for" avec une instruction composée comme partie exécutable contenant
deux instructions "if". Ceci est un exemple de la façon dont une instruction peut être imbriquée. Il devrait être
clair pour vous que chacune des instructions « if » sera exécutée 10 fois.
Considérons la première instruction « si ». Il commence par le motclé « if » suivi d'une expression entre
parenthèses. Si l'expression est évaluée et trouvée vraie, l'instruction unique qui suit le « if » est exécutée. Si
elle est fausse, l'instruction suivante est ignorée. Ici aussi, l'instruction unique peut être remplacée par une
instruction composée composée de plusieurs instructions délimitées par des accolades.
L'expression "data" == 2" demande simplement si la valeur de data est égale à 2, cela sera expliqué en détail
dans le prochain chapitre. (Il suffit pour l'instant que si "data = 2" était utilisé dans ce contexte , cela signifierait
une chose complètement différente.)
Le deuxième "if" est similaire au premier, avec l'ajout d'un nouveau mot réservé, le "else", après la première
instruction printf. Cela dit simplement que si l'expression entre parenthèses est évaluée comme vraie, la
première expression est exécutée, sinon l'expression qui suit le "sinon" est exécutée. Ainsi, l'une des deux
expressions sera toujours exécutée, alors que dans le premier exemple, l'expression unique était soit
exécutée, soit ignorée. Les deux trouveront de nombreuses utilisations dans vos efforts de programmation en
C. Compilez et exécutez ce programme pour voir s'il fait ce que vous attendez.
main( ) {int
xx; for(xx
= 5;xx < 15;xx = xx + 1){ if (xx == 8) break;
Notez que dans le premier "for", il y a une instruction if qui appelle un break si xx est égal à 8. Le break sortira
de la boucle dans laquelle vous vous trouvez et commencera à exécuter les instructions qui suivent la boucle,
mettant ainsi fin à la boucle. Il s'agit d'une déclaration précieuse lorsque vous devez sortir d'une boucle en
fonction de la valeur de certains résultats calculés dans la boucle. Dans ce cas, lorsque xx atteint 8, la boucle
se termine et la dernière valeur imprimée sera la valeur précédente, à savoir 7.
{ int camion ;
for (truck = 3;truck < 13;truck = truck + 1) { switch (truck) { case 3 :
printf("La valeur est
trois\n"); casser; : printf("La valeur est quatre\n"); casser; cas 5 :
cas 6 :
cas 4 cas 7 : cas 8 : printf("La valeur est comprise entre
5 et
8\n"); casser;
cas 11 :
printf("La
valeur est onze\n"); casser; par défaut : printf("C'est une des valeurs non
*/
Le changement n’est pas difficile, alors ne vous laissez pas intimider. Il commence par le mot clé "switch" suivi
d'une variable entre parenthèses qui est la variable de commutation, en l'occurrence "camion". Autant de boîtiers
que vous le souhaitez sont ensuite enfermés dans une paire d'accolades. Le mot réservé « case » est utilisé pour
commencer chaque cas saisi suivi de la valeur de la variable, puis de deux points et des instructions à exécuter.
Dans cet exemple, si la variable "truck" contient la valeur 8 lors de ce passage de l'instruction switch, le printf fera
afficher "La valeur est trois", et l'instruction "break" nous fera sortir du champ. changer.
Une fois qu'un point d'entrée est trouvé, les instructions seront exécutées jusqu'à ce qu'un "break" soit trouvé ou
jusqu'à ce que le programme passe par le bas des accolades du commutateur. Si la variable a la valeur 5, les
instructions commenceront à s'exécuter là où se trouve "case 5 :", mais les premières instructions trouvées sont là
où se trouvent les instructions case 8. Cellesci sont exécutées et l'instruction break dans la partie "case 8" dirigera
l'exécution vers le bas du commutateur. Les différentes valeurs de cas peuvent être dans n'importe quel ordre et si
une valeur n'est pas trouvée, la partie par défaut du commutateur sera exécutée.
Il doit être clair que n'importe laquelle des constructions cidessus peut être imbriquée les unes dans les autres ou
placées successivement, en fonction des besoins du projet de programmation particulier en question.
Compilez et exécutez switch.c pour voir s'il fait ce que vous attendez après cette discussion.
Pour utiliser une instruction "goto", vous utilisez simplement le mot réservé "goto", suivi du nom symbolique auquel vous
souhaitez accéder. Le nom est ensuite placé n'importe où dans le programme suivi de deux points. Vous n'êtes pas
autorisé à sauter dans une boucle, mais vous êtes autorisé à sortir d'une boucle. De plus, vous n’êtes pas autorisé à
passer d’une fonction à une autre. Ces tentatives seront signalées par votre compilateur comme une erreur si vous en
tentez une.
Ce programme particulier est vraiment un gâchis, mais c'est un bon exemple de la raison pour laquelle les auteurs de
logiciels tentent d'éliminer autant que possible l'utilisation de l'instruction "goto". Le seul endroit dans ce programme où il
est raisonnable d'utiliser le "goto" est celui de la ligne 17 où le programme saute hors des trois boucles imbriquées en un
seul saut. Dans ce cas, il serait plutôt compliqué de configurer une variable et de sauter successivement hors des trois
boucles, mais une seule instruction "goto" vous permet de sortir des trois.
Certaines personnes disent que l'instruction "goto" ne devrait en aucun cas être utilisée, mais c'est une pensée plutôt
étroite. S'il existe un endroit où un "goto" sera le meilleur, n'hésitez pas à l'utiliser. Il ne faut cependant pas en abuser, car
c'est le cas dans le reste du programme sur votre moniteur.
Des livres entiers sont écrits sur la programmation « gotoless », mieux connue sous le nom de programmation structurée.
Ceuxci seront laissés à votre étude. Un point de référence est la calculatrice visuelle décrite au chapitre 14 de ce
didacticiel. Ce programme est contenu dans quatre programmes compilés séparément et est un programme complexe
assez volumineux. Si vous passez du temps à étudier le code source, vous constaterez qu'il ne contient aucune instruction
"goto". Compilez et exécutez gotoex.c et étudiez son résultat. Ce serait un bon exercice de le réécrire et de voir à quel
point il est plus lisible lorsque les déclarations sont classées dans l'ordre.
Le formatage est particulièrement important. L'entête est simplement constitué de plusieurs lignes de commentaires décrivant ce
que fait le programme de manière à attirer l'attention des lecteurs tout en restant agréable à l'œil. Vous développerez éventuellement
votre propre style de formatage, mais c’est une bonne façon de commencer.
De plus, si vous observez la boucle for, vous remarquerez que tout le contenu de l'instruction composée est indenté de quelques
espaces à droite du mot réservé "for" et que l'accolade fermante est alignée sous le "f" dans " pour". Cela rend le débogage un peu
plus facile car la construction devient très évidente.
Vous remarquerez également que les instructions "printf" qui se trouvent dans les instructions "if" dans la grande boucle "for" sont
indentées de trois espaces supplémentaires car elles font partie d'une autre construction. C'est le premier programme dans lequel
nous utilisons plus d'une variable. Les trois variables sont simplement définies sur trois lignes différentes et sont utilisées de la même
manière qu'une seule variable était utilisée dans les programmes précédents. En les définissant sur des lignes différentes, nous
avons la possibilité de définir chacune d'entre elles par un commentaire.
maintenant.
main( )
{ int x1,x2,x3;
printf("Tableau des températures Celsius à Farenheit\n\n");
pour(x1 = 2;x1 <= 12;x1 = 1){ x3 = 10 * x1; x2
= 32 + (x3 * 9)/5 ;
printf(" C =%4d F =%4d ",x3,x2);
if (x3 == 0) printf("Point de congélation de l'eau");
}
}
Compilez et exécutez ce programme pour voir qu'il fait exactement ce que le dernier a fait.
Trois variables sont définies pour être utilisées dans le programme et le reste du programme n'est qu'une série d'illustrations
de diverses tâches. Les deux premières lignes des instructions d'affectation attribuent des valeurs numériques à « a » et
« b », et les quatre lignes suivantes illustrent les cinq fonctions arithmétiques de base et comment les utiliser. Le cinquième
est l'opérateur modulo et donne le reste si les deux variables étaient divisées. Il ne peut s'appliquer qu'aux variables de
type "int" ou "char", et bien sûr aux extensions "int" telles que "long", "short", etc. A la suite de cellesci, il y a deux lignes
illustrant comment combiner certaines des variables dans certaines expressions mathématiques complexes. Tous les
exemples cidessus ne devraient nécessiter aucun commentaire, sauf pour dire qu'aucune des équations n'est censée être
particulièrement utile, sauf à titre d'illustration.
Les deux expressions suivantes sont parfaitement acceptables telles qu'elles sont données, mais nous verrons plus loin
dans ce chapitre qu'il existe une autre façon de les écrire pour un code plus compact.
Il nous reste donc les deux dernières lignes qui peuvent vous paraître très étranges. Le compilateur C analyse l'instruction
d'affectation de droite à gauche (ce qui peut sembler un peu étrange puisque nous ne lisons pas de cette façon), ce qui
donne une construction très utile, à savoir celle donnée ici. Le compilateur trouve la valeur 20, l'attribue à "c", puis continue
vers la gauche en constatant que le dernier résultat d'un calcul doit être attribué à "b". Pensant que le dernier calcul a
abouti à un 20, il l'attribue également à "b" et continue le balayage vers la gauche en attribuant également la valeur 20 à "a".
C'est une construction très utile lorsque vous initialisez un groupe de variables. La dernière affirmation montre qu'il est
possible d'effectuer certains calculs pour arriver à la valeur qui sera attribuée aux trois variables.
Le programme n'a aucune sortie, donc compiler et exécuter ce programme sera très inintéressant.
Puisque vous avez déjà appris à afficher des résultats entiers à l'aide de la fonction "printf", il serait à votre avantage
d'ajouter des instructions de sortie à ce programme pour voir si les différentes instructions font ce que vous pensez qu'elles
devraient faire.
Le chargement et l'édition de mortotypes.c illustreront comment certains types de données supplémentaires peuvent être utilisés.
{int a,b,c; char /* 32768 à 32767 sans point décimal */ /* 0 à 255 sans point
x,y,z; float num, décimal */
jouet, chose ; /* 10E38 à 10E+38 avec point décimal */
a = b = c = 27 ; x = y =
z = « A » ; num = jouet
= chose = 3,6792 ;
une = /* a vaut désormais 65 (caractère A) */ /* x sera
oui ; x = désormais un nombre amusant */ /* num sera
b ; num = b; désormais 27,00 */ /* a sera désormais 3 */
a = jouet ;
}
Une fois de plus, nous avons défini quelques variables de type entier que vous devriez déjà connaître, mais nous avons ajouté
deux nouveaux types, le "char" et le "float".
Le type de données "char" est presque le même que l'entier, sauf qu'on ne peut lui attribuer que des valeurs comprises entre
zéro et 255, car il est stocké dans un seul octet de mémoire. Le type de données « char » est généralement utilisé pour les
données ASCII, plus communément appelées texte. Le texte que vous lisez a été écrit à l’origine sur un ordinateur équipé d’un
traitement de texte qui stockait les mots dans l’ordinateur à raison d’un caractère par octet. En revanche, le type de données
entier est stocké dans deux octets de mémoire informatique sur la plupart des microordinateurs 8 bits et MSDOS.
L'Applix 1616 utilise une puce 68 000 avec de vrais registres de 32 bits, donc les entiers sont de 32 bits. Cela signifie que la
plage d'un nombre entier n'est pas ±32767, mais ±2147483648, soit plus de deux milliards ! Sur Applix, un int court peut être plus
approprié à certains endroits.
Il serait intéressant à ce stade de discuter de la manière dont C gère les deux types "char" et "int".
La plupart des fonctions en C conçues pour fonctionner avec des variables de type entier fonctionneront aussi bien avec des
variables de type caractère car elles sont une forme de variable entière. Ces fonctions, lorsqu'elles sont appelées à utiliser une
variable de type "char", transformeront en fait les données "char" en données entières avant de les utiliser. Pour cette raison, il
est possible de mélanger des variables de type "char" et "int" à peu près comme vous le souhaitez. Le compilateur ne sera pas
confus, mais vous pourriez le faire. Il est bon de ne pas trop s’y fier, mais d’utiliser avec précaution uniquement les types de
données appropriés là où ils doivent être utilisés.
Le deuxième nouveau type de données est le type de données « flottant », communément appelé données à virgule flottante. Il
s'agit d'un type de données qui a généralement une très large plage, un grand nombre de chiffres significatifs et un grand nombre
de mots informatiques pour le stocker. Le type de données « float » est associé à un point décimal et, sur la plupart des
ordinateurs, sa plage autorisée est comprise entre 1038 et 10+38.
Tous les compilateurs n'ont pas la même plage disponible, alors consultez votre manuel de référence pour connaître les limites
de votre compilateur.
Les trois premières lignes du programme attribuent des valeurs aux neuf variables définies afin que nous puissions
manipuler certaines données entre les différents types.
Puisque, comme mentionné cidessus, un type de données "char" est en réalité un type de données "entier", aucune
considération particulière ne doit être prise pour promouvoir un "char" en variable "int". Dans l'autre sens, il n'y a pas
de norme, vous pouvez donc simplement obtenir des déchets si la valeur est comprise entre zéro et 255. Par
conséquent, dans la deuxième ligne, lorsque vous essayez de définir x (un caractère) sur 27, vous pouvez ou peut ne
pas obtenir une réponse bien définie, cela dépend de votre implémentation particulière de C.
La troisième ligne illustre la simplicité de traduire un entier en "float", attribuezlui simplement la nouvelle valeur et le
système effectuera la conversion appropriée. Cependant, dans l’autre sens, il y a une complication supplémentaire.
Puisqu’il peut y avoir une partie fractionnaire du nombre à virgule flottante, le système doit décider quoi en faire. Par
définition, cela va le tronquer.
Ce programme ne produit aucune sortie, et nous n'avons pas expliqué comment afficher les variables de type "char"
et "float", vous ne pouvez donc pas vraiment accéder à ce programme et jouer avec les résultats, mais le programme
suivant couvrira pour vous.
Un problème malheureux avec C (et certains autres langages) est que la taille réelle des variables dépend quelque
peu de la machine (et du compilateur) utilisée. Des difficultés sont parfois rencontrées lors du déplacement du code
d’une machine à une autre à cause de cela.
Une règle générale pour les compilateurs modernes est que char est d'au moins 8 bits, short est d'au moins 16 bits,
long est d'au moins 32 bits, int est identique à short ou long (ce qui entraîne le décollage de nombreuses conversions
de code) , float fait au moins 32 bits et double est au moins aussi large que float. En conséquence, vous devez utiliser
short ou long de préférence à int (malgré son utilisation intensive dans ce didacticiel), et éviter de doubler autant que
possible. Notez que l'utilisation de short au lieu de int peut économiser un accès mémoire supplémentaire et contribuer
à accélérer le code, en particulier dans les grandes boucles. caractère entier 8 bits (0 à 255)
L'ordre des octets longs est un autre problème rencontré lors de la conversion de programmes d'une machine à une
autre. Dans le 68000, l'octet de poids faible d'un mot de 16 bits est le deuxième octet impair. Les octets d'une longueur
de 32 bits sont disposés dans l'octet de poids fort à l'adresse, avec l'octet de poids faible en dessous. Les puces Intel
utilisent l'ordre inverse. Ce problème de sexe d’octets donne lieu à des arguments semblables à ceux de la religion et
de la politique, et tout aussi susceptibles de conduire à de vraies solutions.
une = 1023 ;
b = 2222 ;
c = 123 ;
d = 1234 ;
e = 'X';
f = 3,14159 ;
g = 3,1415926535898 ;
printf("a = %d\n",a); printf("a = /* sortie décimale */
%o\n",a); printf("a = %x\n",a); /* sortie octale */
printf("b = %1d\n",b); printf("c = /* sortie hexadécimale */
%d\n",c); printf("d = %u\n",d); /* Sortie décimale longue /* Sortie */
printf("e = %c\n",e); printf("f = décimale courte */
%f\n",f); printf("g = %f\n",g); /* sortie non signée */
printf("\n"); /* sortie de caractères */
/* sortie flottante */
/* sortie double float */
Ce fichier contient tous les types de données simples standard disponibles dans le langage de programmation C.
Il existe d'autres types, mais ce sont les types composés que nous aborderons en temps voulu.
Observez le dossier. Nous définissons d’abord un simple « int » suivi d’un « long int » et d’un « short int ». Consulter
votre manuel de référence pour une définition exacte de ceuxci pour votre compilateur, car ils ne le sont pas
cohérente d’une mise en œuvre à l’autre. Le "non signé" vient ensuite et est défini comme
la même taille que le "int" mais sans signe. Le "non signé" couvrira alors une plage de 0 à 65535
sur les microordinateurs MSDOS, mais comme 0 à 4294967296 dans HiTech. Il convient de souligner que
lorsque "long", "short" ou "unsigned" est souhaité, le "int" est facultatif et laissé de côté par la plupart
programmeurs expérimentés. Nous avons déjà abordé le "char" et le "float", ce qui laisse
seulement le "double". Le « double » couvre généralement une plus grande portée que le « flottant » et a plus de
chiffres significatifs pour des calculs plus précis. Il faut également plus de mémoire pour stocker une valeur
que le simple "flotteur". Consultez votre manuel de référence pour connaître la portée et la précision du « double ».
Une autre diversion s’impose à ce stade. La plupart des compilateurs ont des dispositions pour la virgule flottante
mathématiques, mais seulement mathématiques doubles flottantes. Ils favoriseront un "flottant" en "double" avant de faire
calculs et donc une seule bibliothèque mathématique sera nécessaire. Bien sûr, c'est totalement
transparent pour vous, vous n'avez donc pas à vous en soucier. Vous pensez peutêtre qu'il serait préférable de
définissez simplement chaque variable à virgule flottante comme double, car elles sont promues avant d'être utilisées dans n'importe quel
calculs, mais ce n’est peutêtre pas une bonne idée. Une variable "float" nécessite 4 octets de stockage et
un "double" nécessite 8 octets de stockage, donc si vous disposez d'un grand volume de données à virgule flottante à
store, le "double" nécessitera évidemment beaucoup plus de mémoire. Votre compilateur peut nécessiter un
nombre d'octets différent de 4 ou 8. Consultez votre manuel de référence pour connaître le nombre correct d'octets.
octets utilisés par votre compilateur.
Chacun d'entre eux est utilisé après un signe de pourcentage pour indiquer le type de conversion de sortie, et
entre ces deux caractères, les champs suivants peuvent être ajoutés.
gauche justification dans son domaine
(n) un nombre spécifiant la largeur minimale du champ
. séparer n de m
(m)l chiffres fractionnaires significatifs pour un flottant
pour indiquer un "long"
Ceuxci sont tous utilisés dans les exemples inclus dans le programme actuellement affiché sur
votre moniteur, à l'exception de la notation de chaîne qui sera abordée plus loin dans ce didacticiel.
Compilez et exécutez ce programme pour voir quel effet les différents champs ont sur la sortie.
Vous avez maintenant la possibilité d'afficher n'importe quel champ de données dans les programmes précédents et cela
Il serait avantageux de revenir en arrière et de voir si vous pouvez afficher l'un des champs comme vous le souhaitez.
Nous commençons par définir et initialiser neuf variables à utiliser dans les instructions de comparaison suivantes.
Cette initialisation est nouvelle pour vous et peut être utilisée pour initialiser des variables pendant qu'elles sont définies.
Le premier groupe d’instructions de comparaison représente les types de comparaisons les plus simples puisqu’elles
comparent simplement deux variables. L'une ou l'autre variable peut être remplacée par une constante tout en restant
valable, mais deux variables sont le cas général. La première comparaison vérifie si « x » est égal à « y » et utilise le
double signe égal pour la comparaison. Un seul signe égal pourrait être utilisé ici mais il aurait une signification différente
comme nous le verrons bientôt. La deuxième comparaison vérifie si « x » est supérieur à « z ».
Le troisième introduit l'opérateur "NON", l'exclamation, qui peut être utilisé pour inverser le résultat de toute comparaison
logique. Le quatrième vérifie si "b" est inférieur ou égal à "c" et le dernier vérifie si "r" n'est pas égal à "s". Comme nous
l'avons appris dans le chapitre précédent, si le résultat de la comparaison est vrai, l'instruction suivant la clause "if" sera
exécutée et les résultats seront donnés dans les commentaires.
Notez que « inférieur à » et « supérieur ou égal à » sont également disponibles, mais ne sont pas illustrés ici.
Il serait bon de mentionner le format différent utilisé pour l'instruction "if" dans cet exemple de programme. Un retour
chariot n'est pas requis comme séparateur d'instructions et en mettant la lisibilité conditionnelle de l'ensemble du
programme.
Regardez la première comparaison du deuxième groupe d’instructions de comparaison. L'expression "r is != s" sera
évaluée comme "vrai" puisque "r" a été défini sur 0,0 cidessus, donc le résultat sera une valeur non nulle, probablement
1. Même si les deux variables comparées sont " float", le résultat sera de type "entier". Il n'y a pas de variable explicite à
laquelle elle sera affectée, le résultat de la comparaison est donc un entier implicite. Enfin, le nombre résultant, 1 dans
ce cas, est attribué à la variable entière "x". Si des doubles signes égal étaient utilisés, la valeur fantôme, à savoir 1,
serait comparée à la valeur de "x", mais comme le seul signe égal est utilisé, la valeur 1 est simplement attribuée à "x",
comme si l'énoncé était pas entre parenthèses. Enfin, puisque le résultat de l'affectation entre parenthèses était différent
de zéro, l'expression entière est évaluée comme "vrai" et "z" se voit attribuer la valeur de 1000. Ainsi, nous avons
accompli deux choses dans cette instruction, nous avons attribué "x " une nouvelle valeur, probablement 1, et nous
avons attribué à "z" la valeur 1000. Nous avons abordé beaucoup de choses dans cette déclaration, vous souhaiterez
peutêtre la consulter avant de continuer. Les choses importantes à retenir sont les valeurs qui définissent « vrai » et
« faux », et le fait que plusieurs choses peuvent être assignées dans une instruction conditionnelle. La valeur attribuée à
"x" était probablement 1, mais différents compilateurs peuvent attribuer une valeur différente tant qu'elle est différente de
zéro.
Le troisième exemple, du deuxième groupe, compare « x » à zéro. Si le résultat est vrai, c'estàdire que si « x »
n'est pas nul, alors « z » se voit attribuer la valeur 333, ce qui sera le cas. Le dernier exemple de ce groupe illustre
le même concept, puisque le résultat sera vrai si « x » est différent de zéro. La comparaison à zéro n'est pas
réellement nécessaire et le résultat de la comparaison est vrai. Les troisième et quatrième exemples de ce groupe
sont donc identiques.
La comparaison suivante dans ce groupe introduit le "||" opérateur qui est le "OU". L'instruction se lit comme suit : si
"x" est supérieur à "y" OU si "z" est supérieur à 12, alors le résultat est vrai. Puisque « z » est supérieur à 12, peu
importe que « x » soit supérieur à « y » ou non, car une seule des deux conditions doit être vraie pour que le résultat
soit vrai. Le résultat est vrai, donc « z » se verra attribuer la valeur 22.
En passant à l'exemple suivant du groupe trois, nous trouvons trois variables simples utilisées dans la partie
conditionnelle de la comparaison. Puisque les trois sont différents de zéro, tous les trois sont « vrais », et donc le
« ET » des trois variables est vrai, ce qui conduit au résultat « vrai » et « z » se voit attribuer la valeur 11. Notez le
puisque les variables "r", "s" et "t" sont des variables de type "float", elles ne pourraient pas être utilisées de cette
façon, mais elles pourraient chacune être comparées à zéro et le même type d'expression pourrait être utilisé.
En continuant avec le quatrième exemple du troisième groupe, nous trouvons trois instructions d'affectation dans la
partie comparaison de l'instruction « if ». Si vous avez compris la discussion cidessus, vous ne devriez avoir aucune
difficulté à comprendre que les trois variables se voient attribuer leurs nouvelles valeurs respectives et que le résultat
des trois est différent de zéro, ce qui conduit à une valeur résultante de « VRAI ».
Le dernier groupe de comparaisons illustre trois possibilités pour avoir quelques ennuis. Tous les trois ont pour
résultat commun que « z » ne sera pas défini sur la valeur souhaitée, mais pour des raisons différentes.
Dans le cas du premier, la comparaison est évaluée comme "true", mais le pointvirgule suivant les secondes
parenthèses termine la clause "if", et l'instruction d'affectation impliquant "z" est toujours exécutée comme
instruction suivante. Le "if" n'a donc aucun effet à cause du pointvirgule mal placé. La deuxième affirmation est
beaucoup plus simple car "x" sera toujours égal à luimême, donc l'inégalité ne sera jamais vraie et l'énoncé dans
son ensemble ne fera jamais rien, mais constitue un effort inutile. La dernière instruction attribuera toujours 0 à "x"
et la comparaison sera donc toujours "false", n'exécutant jamais la partie conditionnelle de l'instruction "if".
L'instruction conditionnelle est extrêmement importante et doit être parfaitement comprise pour écrire des
programmes C efficaces. Si une partie de cette discussion n'est pas claire dans votre esprit, réétudiezla jusqu'à
ce que vous soyez sûr de bien la comprendre avant de continuer.
/* décrémentation */ y = y
1; /* Cela décrémente y */
/* Cela décrémente y */ y;
= y; /* Cela décrémente y */ y; y = 3 ; z
z = y;
/* z = 3, y = 2 */ /* z = 1, y =
1 */
/* opération arithmétique */
/* expression conditionnelle */
une = (b >= 3,0 ? 2,0 ; 10,5 ); /*Cette expression /* Et cette */
si (b >= 3,0) a = 2,0 ; expression */
sinon a = /* sont identiques, les deux */
10,5 ; /* provoquera la même chose */
/* résultat. */
c = (une > b?une:b); c = (a /* c aura le maximum de a ou b */
> b?b:b); /* c aura le min de a ou b */
}
Dans ce programme, certaines variables sont définies et initialisées dans les mêmes instructions pour être utilisées cidessous.
Le premier ne devrait pas vous surprendre. Les deux instructions suivantes ajoutent également un à la valeur
de "x", mais il n'est pas intuitif que ce soit ce qui se passe. C'est simplement une définition que cela est vrai.
Ainsi, par définition du langage C, un double signe plus avant ou après une variable
incrémente cette variable de 1. De plus, si les signes plus sont avant la variable, la variable
est incrémenté avant d'être utilisé, et si les signes plus sont après la variable, la variable est utilisée,
puis incrémenté. Dans l'instruction suivante, la valeur de "y" est affectée à la variable "z", puis
"y" est incrémenté car les signes plus se trouvent après la variable "y". Dans la dernière déclaration du
incrémentant un groupe d'exemples d'instructions, la valeur de "y" est incrémentée puis sa valeur est
affecté à la variable "z".
Le groupe d'instructions suivant illustre la décrémentation d'une variable de un. La définition fonctionne
exactement de la même manière pour décrémenter que pour incrémenter. Si les signes moins sont avant
la variable, la variable est décrémentée, puis utilisée, et si les signes moins sont après la variable,
la variable est utilisée, puis décrémentée.
Tout comme les opérateurs d'incrémentation et de décrémentation, l'opérateur arithmétique est largement utilisé
par des programmeurs C expérimentés et cela vous rapporterait beaucoup de le comprendre.
Ce chapitre a été long, mais il contenait des éléments importants pour vous aider à démarrer en utilisant C.
Dans le prochain chapitre, nous passerons aux éléments constitutifs du C, les fonctions. À ce stade, vous
vous disposerez de suffisamment de matériel de base pour vous permettre de commencer à écrire des programmes significatifs.
3. Écrivez un programme qui comptera de 1 à 100 et imprimera uniquement les valeurs comprises entre 32 et 100.
39, un à une ligne.
Chargez et examinez le fichier sumsqles.c pour un exemple de programme C avec des fonctions.
somme entière ; /* Ceci est une variable globale */
main( ) { int
index ;
entête(); pour /* Ceci appelle la fonction nommée header */
(index = 1;index <= 7;index++) carré(index); /* Ceci appelle
la fonction carréeending(); */
/* Ceci appelle la fonction de fin */
}
int numq;
numsq = nombre * nombre ; /* Cela produit le carré */ sum += numsq; printf("Le carré de %d est
%d\n",number,numsq);
En fait, ce n'est pas la première fonction que nous rencontrons, car le programme "principal" que nous
utilisons depuis le début est techniquement une fonction, tout comme la fonction "printf". La fonction
"printf" est une fonction de bibliothèque fournie avec votre compilateur.
Notez la partie exécutable de ce programme. Il commence par une ligne qui dit simplement "header()',
qui est le moyen d'appeler n'importe quelle fonction. Les parenthèses sont obligatoires car le compilateur
C les utilise pour déterminer qu'il s'agit d'un appel de fonction et pas simplement d'une variable mal
placée. Lorsque le Le programme arrive à cette ligne de code, la fonction nommée "header" est appelée,
ses instructions sont exécutées, et le contrôle revient à l'instruction suivant cet appel. En continuant on
arrive à une boucle "for" qui sera exécutée 7 fois et qui appelle une autre fonction nommée "square" à
chaque passage dans la boucle, et finalement une fonction nommée "ending" sera appelée et exécutée.
Pour l'instant ignorez "l'index" entre parenthèses de l'appel à "square". Nous avons vu que ce programme
appelle donc un entête, 7 appels carrés et une fin. Il faut maintenant définir les fonctions.
La première instruction définit la variable « somme » égale à zéro car nous l'utiliserons pour accumuler une somme
de carrés. Étant donné que la variable « somme » est définie comme une variable de type entier avant le programme
principal, elle peut être utilisée dans l'une des fonctions suivantes. C'est ce qu'on appelle une variable "globale", et
sa portée s'étend à l'ensemble du programme et à toutes les fonctions. Nous en dirons davantage sur la portée des
variables à la fin de ce chapitre. L'instruction suivante génère un message d'entête sur le moniteur. Le contrôle du
programme revient ensuite aux programmes principaux au cas où il n'y aurait pas d'instructions supplémentaires à
exécuter dans cette fonction.
Il devrait être clair pour vous que les deux lignes exécutables de cette fonction pourraient être déplacées vers le
programme principal, remplaçant l'appel d'entête, et le programme ferait exactement la même chose que ce qu'il fait
actuellement. Cela ne minimise pas la valeur des fonctions, cela illustre simplement que le fonctionnement de cette
fonction simple est d'une manière simple. Vous trouverez des fonctions très utiles dans la programmation C.
Dans l'appel à la fonction "square", nous avons une fonctionnalité ajoutée, à savoir la variable "index" entre
parenthèses. Ceci indique au compilateur que lorsque vous accédez à la fonction, vous souhaitez emporter la valeur
de l'index à utiliser dans l'exécution de la fonction. En regardant la fonction "carré", nous constatons qu'un autre nom
de variable est placé entre parenthèses, à savoir la variable "nombre". C'est le nom que l'on préfère appeler la
variable passée à la fonction lorsque l'on est dans la fonction. Nous pouvons l'appeler comme nous le souhaitons à
condition qu'il respecte les règles de dénomination d'un identifiant. Puisque la fonction doit connaître le type de la
variable, elle est définie après le nom de la fonction mais avant l'accolade ouvrante de la fonction ellemême. Ainsi,
la ligne contenant "int number;" indique à la fonction que la valeur qui lui est transmise sera une variable de type
entier. Une fois tout cela réglé, nous avons maintenant la valeur de l'index du programme principal transmise à la
fonction "square", mais renommée "number", et disponible pour une utilisation dans la fonction.
Après l'accolade ouvrante de la fonction, nous définissons une autre variable "numsq" à utiliser uniquement dans la
fonction ellemême (nous en parlerons plus tard) et procédons aux calculs requis.
Nous définissons "numsq" égal au carré du nombre, puis ajoutons numsq au total actuel stocké dans "sum". N'oubliez
pas que "sum += numsq" est identique à "sum = sum + numsq" de la dernière leçon. Nous imprimons le nombre et
son carré, et revenons au programme principal.
Je vous ai dit il y a peu de temps que la seule manière de récupérer une valeur dans le programme principal
était d'utiliser une variable globale, mais il existe une autre méthode dont nous discuterons après avoir chargé
et affiché le fichier nommé squares.c.
Dans ce fichier nous verrons qu'il est simple de renvoyer une seule valeur d'une fonction appelée à la fonction
appelante. Mais encore une fois, il est vrai que pour renvoyer plus d’une valeur, il faudra étudier soit des
tableaux, soit des pointeurs.
main( ) {int /* Ceci est le programme principal */
x,y;
pour(x = 0;x < 7;x++) { y = carré(x); /
* va récupérer la valeur de x*x printf("Le carré de %d est %d\n",x,y); */
{int carré ;
carré = po * po ;
retour(carré); /* Ceci définit squ() = square */
}
Dans le programme principal, nous définissons deux entiers et commençons une boucle "for" qui sera exécutée
8 fois. La première instruction de la boucle for est "y = squ(x);", qui est une construction nouvelle et plutôt
étrange. D'après l'expérience passée, nous ne devrions avoir aucune difficulté à comprendre que la partie
"squ(x)" de l'instruction est un appel à la fonction "squ" prenant la valeur de "x" comme variable. En regardant
la fonction ellemême, nous constatons que la fonction préfère appeler la variable "in" et elle met au carré la
valeur de "in" et appelle le résultat "square". Enfin, un nouveau type d'instruction apparaît, l'instruction "retour".
La valeur entre parenthèses est attribuée à la fonction ellemême et est renvoyée sous forme de valeur utilisable
dans le programme principal. Ainsi, l'appel de fonction "squ(x)" se voit attribuer la valeur du carré et est renvoyé
au programme principal de telle sorte que "y" soit alors égal à cette valeur. Si "x" se voyait donc attribuer la
valeur 4 avant cet appel, "y" serait alors mis à 16 suite à cette ligne de code.
Une autre façon de voir cela est de considérer le regroupement de caractères "squ(x)" comme une autre
variable avec une valeur qui est le carré de "x", et cette nouvelle variable peut être utilisée partout où il est légal
d'utiliser une variable. de son genre. Les valeurs de « x » et « y » sont ensuite imprimées.
Pour illustrer que le regroupement de « squ(x) » peut être considéré comme simplement une autre variable,
une autre boucle « for » est introduite dans laquelle l'appel de fonction est placé dans l'instruction print plutôt
que de l'attribuer à une nouvelle variable.
index ; float
x,y,sqr(),glsqr();
pour (index = 0;index <= 7;index++){
x = indice ; /* convertir int en float */ y = sqr(x); /* carré x à une
variable à virgule flottante */ printf("Le carré de %d est %10.4f\n",index,y);
float sqr(inval) /* place un float, renvoie un float float inval; { carré flottant ; */
retour(z*z);
}
Cela commence par définir une variable globale à virgule flottante que nous utiliserons plus tard. Ensuite, dans la
partie "principale" du programme, un entier est défini, suivi de deux variables à virgule flottante, puis de deux
définitions étranges. Les expressions "sqr()" et "glsqr()" ressemblent à des appels de fonction et elles le sont. C'est
la manière appropriée en C de définir qu'une fonction renverra une valeur qui n'est pas du type "int", mais d'un
autre type, dans ce cas "float". Cela indique au compilateur que lorsqu'une valeur est renvoyée par l'une ou l'autre
de ces deux fonctions, elle sera de type "float".
Référezvous maintenant à la fonction « sqr » près du centre de la liste et vous verrez que le nom de la fonction
est précédé du nom « float ». Ceci indique au compilateur que cette fonction renverra une valeur de type "float" à
tout programme qui l'appelle. La fonction est désormais compatible avec son appel. La ligne qui suit le nom de la
fonction contient "float inval;", qui indique au compilateur que la variable passée à cette fonction depuis le
programme appelant sera de type "float".
La fonction suivante, à savoir "glsqlr", renverra également une variable de type "float", mais elle utilise une variable
globale pour la saisie. Il effectue également la mise au carré dans l'instruction return et n'a donc pas besoin de
définir une variable distincte pour stocker le produit.
La structure globale de ce programme ne devrait poser aucun problème et ne sera pas discutée plus en détail.
Comme c'est l'habitude avec tous les exemples de programmes, compilez et exécutez ce programme.
{ registre int index ; /* Cette variable est disponible uniquement dans main */
tête1();
tête2();
tête3();
/* boucle "for" principale de ce programme */ for (index = 8;index >
0;index) {
des trucs int; /* Cette variable n'est disponible qu'entre ces accolades */ for (stuff = 0;stuff <= 6;stuff++)
printf("%d",stuff); printf(" l'index est maintenant %d\n",
index);
}
}
head2()
{ nombre int ; /* Cette variable est disponible uniquement dans head2 */ /* et elle déplace le
global du même nom */
compte = 53 ;
printf("La valeur de l'entête2 est %d\n",count); compteur = 77 ;
tête3() {
La première variable définie est une variable globale "count" qui est disponible pour n'importe quelle fonction du
programme puisqu'elle est définie avant n'importe laquelle des fonctions. De plus, il est toujours disponible car
il ne va pas et ne vient pas au fur et à mesure de l'exécution du programme. (Cela aura du sens sous peu.) Plus
bas dans le programme, une autre variable globale nommée "compteur" est définie qui est également globale
mais n'est pas disponible pour le programme principal puisqu'elle est définie après le programme principal. Une
variable globale est toute variable définie en dehors de toute fonction. Notez que ces deux variables sont parfois
appelées variables externes car elles sont externes à toute fonction.
Revenez au programme principal et vous verrez la variable "index" définie comme un entier. Ignorez le mot
« s'inscrire » pour le moment. Cette variable n'est disponible que dans le programme principal car c'est là qu'elle
est définie. De plus, il s'agit d'une variable "automatique", ce qui signifie qu'elle n'existe que lorsque la fonction
dans laquelle elle est contenue est invoquée, et cesse d'exister lorsque la fonction est terminée. Cela ne veut
vraiment rien dire ici car le programme principal est toujours en fonctionnement, même lorsqu'il donne le contrôle
à une autre fonction. Un autre entier est défini
Observez la fonction nommée "head1". Il contient une variable nommée "index", qui n'a rien à voir avec "l'index" du
programme principal, sauf que les deux sont des variables automatiques. Lorsque le programme n'exécute pas
réellement d'instructions dans cette fonction, cette variable nommée "index" n'existe même pas. Lorsque "head1" est
appelé, les variables sont générées et lorsque "head1" termine sa tâche, la variable "index" est complètement éliminée
de l'existence. Gardez cependant à l’esprit que cela n’affecte pas la variable du même nom dans le programme
principal, puisqu’il s’agit d’une entité complètement distincte.
Les variables automatiques sont donc automatiquement générées et supprimées en cas de besoin. La chose importante
à retenir est que d'un appel à une fonction au prochain appel, la valeur d'une variable automatique n'est pas conservée
et doit donc être réinitialisée.
Un type de variable supplémentaire doit être mentionné à ce stade, la variable « statique ». En plaçant le mot réservé
"static" devant une déclaration de variable dans une fonction, la ou les variables de la déclaration sont des variables
statiques et resteront en existence d'un appel à l'autre de la fonction particulière.
En plaçant le même mot réservé devant une variable externe, en dehors de toute fonction, cela rend la variable privée
et non accessible pour une utilisation dans un autre fichier. Cela implique qu'il est possible de faire référence à des
variables externes dans d'autres fichiers compilés séparément, et c'est vrai. Des exemples de cette utilisation seront
donnés au chapitre 14 de ce tutoriel.
Les variables de registre sont autorisées dans HiTech C, avec jusqu'à quatre variables de registre sans pointeur (dans 68
000 registres D4 à D7) et jusqu'à trois variables de registre avec pointeur (dans A3 à A5). Cette utilisation n'est pas en
conflit avec l'utilisation 1616/OS de D0 à D2 et A0 à A2). Comme les compilateurs MSDOS n'autorisent généralement
que deux variables de registre, de nombreux programmeurs n'utilisent pas beaucoup les variables de registre, vous pouvez
donc souvent gagner un peu plus de vitesse lors de la conversion de programmes en utilisant plus largement les variables
de registre (le compilateur ignorera la demande de variable de registre si aucune variable de registre n'est utilisée). des
registres sont disponibles et traitent la variable comme une variable ordinaire).
De plus, la plupart des compilateurs disposent de fonctions supplémentaires prédéfinies qui ne sont pas standards mais
permettent au programmeur de tirer le meilleur parti de son ordinateur particulier. Dans le cas des IBMPC et compatibles,
la plupart de ces fonctions permettent au programmeur d'utiliser les services BIOS disponibles dans le système
d'exploitation, ou d'écrire directement sur le moniteur vidéo ou à n'importe quel endroit de la mémoire.
Les équivalents Applix sont inclus dans les fichiers d'entête mentionnés ailleurs. Ceuxci ne seront pas abordés en détail
car vous pourrez étudier par vousmême les aspects uniques de votre compilateur. Beaucoup de ces types de fonctions
sont utilisés dans les versions IBM des exemples de programmes du chapitre 14.
La récursion est une autre de ces techniques de programmation qui semble très intimidante la première fois que vous la
rencontrez, mais si vous chargez et affichez l'exemple de programme nommé recurson.c, nous enlèverons tout le mystère.
C'est probablement le programme récursif le plus simple qu'il soit possible d'écrire et c'est donc un programme stupide
dans la pratique, mais à des fins d'illustration, il est excellent.
main( )
{ int index ;
indice = 8 ;
count_dn(index);
}
count_dn(count) int
compte ; {
compter;
printf("La valeur du compte est %d\n",count); si (compte > 0)
La récursivité n'est rien de plus qu'une fonction qui s'appelle ellemême. Il est donc dans une boucle qui doit avoir un
moyen de se terminer. Dans le programme sur votre moniteur, la variable "index" est définie sur 8 et est utilisée comme
argument de la fonction "count_dn". La fonction décrémente simplement la variable, l'imprime dans un message, et si
la variable n'est pas nulle, elle s'appelle ellemême, où elle la décrémente à nouveau, l'imprime, etc. etc. etc. Enfin, la
variable atteindra zéro, et la fonction ne s'appellera plus. Au lieu de cela, il reviendra à l'heure précédente où il s'est
appelé, et reviendra à nouveau, jusqu'à ce qu'il revienne finalement au programme principal et revienne au DOS.
Pour des raisons de compréhension, vous pouvez le considérer comme ayant 8 copies de la fonction "count_dn"
disponibles et il les a simplement toutes appelées une à la fois, en gardant une trace de la copie dans laquelle elle se
trouvait à un moment donné. Ce n’est pas ce qui s’est réellement passé, mais c’est une illustration raisonnable pour
vous permettre de commencer à comprendre ce qu’il faisait réellement.
En utilisant la récursion, vous souhaiterez peutêtre écrire un programme avec une récursion indirecte par opposition à
la récursion directe décrite cidessus. La récursivité indirecte se produirait lorsqu'une fonction "A" appelle la fonction
"B", qui à son tour appelle "A", etc. Ceci est tout à fait permis, le système se chargera de mettre les éléments
nécessaires sur la pile et de les récupérer en cas de besoin. encore. Il n'y a aucune raison pour que trois fonctions ne
s'appellent pas en cercle, ou quatre, ou cinq, etc. Le compilateur C s'occupera de tous les détails pour vous.
Ce que vous devez retenir à propos de la récursivité, c'est qu'à un moment donné, quelque chose doit aller à zéro ou
atteindre un point prédéfini pour terminer la boucle. Sinon, vous aurez une boucle infinie et la pile se remplira et
débordera, vous donnant une erreur et arrêtant le programme assez brusquement.
main( )
} forward_and_backwards(line_of_char,index) char
line_of_char[]; indice int ; {
if (line_of_char[index])
{ printf("%c",line_of_char[index]); indice++;
forward_and_backwards(line_of_char,index);
} printf("%c",line_of_char[index]);
}
Chaque appel successif à la fonction nommée "forward_and_backward" provoque l'impression d'un caractère
du message. De plus, chaque fois que la fonction se termine, l'un des caractères est à nouveau imprimé,
cette fois à l'envers à mesure que la chaîne d'appels de fonction récursifs est retracée.
Ne vous inquiétez pas du tableau de caractères défini à la ligne 3 ou des autres nouveautés présentées ici.
Après avoir terminé le chapitre 7 de ce didacticiel, ce programme prendra tout son sens. Il a été estimé qu'il
était important d'introduire un deuxième exemple de récursivité, c'est pourquoi ce fichier est inclus ici.
Une fonctionnalité supplémentaire est intégrée à ce programme dans la version IBM PC. Si vous observez
les deux appels à la fonction et la fonction ellemême, vous verrez que le nom de la fonction est orthographié
de trois manières différentes dans les derniers caractères de la version IBM d'origine. Le compilateur IBM ne
se soucie pas de la façon dont ils sont orthographiés car il n'utilise que les 8 premiers caractères du nom de
la fonction. En ce qui le concerne, la fonction est nommée "forward_". Les caractères restants sont simplement
ignorés. Si votre compilateur utilise plus de 8 caractères comme étant significatifs, comme le fait HiTech,
vous devrez modifier deux des noms afin que les trois noms soient identiques, comme nous l'avons fait.
Compilez et exécutez ce programme et observez les résultats.
6.1 Les définitions et les macros sont des aides à la programmation claire
Chargez et affichez le fichier nommé définir.c pour un premier aperçu de certaines définitions et macros.
#define START 0 /* Point de départ de la boucle */ #define ENDING 9 /* Point de fin de la
boucle */ #define MAX(A,B) ((A)>(B)?(A):(B)) / * Définition de macro maximale */ #define
MIN(A,B) ((A)>(B)?(B):(A)) /* Définition de macro minimale */
main( ) { int
Notez les quatre premières lignes du programme commençant chacune par le mot "#define". C’est ainsi que toutes les
définitions et macros sont définies. Avant le début de la compilation proprement dite, le compilateur passe par une
passe de préprocesseur pour résoudre toutes les définitions. Dans le cas présent, il trouvera chaque endroit du
programme où se trouve la combinaison "START" et il la remplacera simplement par le 0 puisque telle est la définition.
Le compilateur luimême ne verra jamais le mot "START", donc en ce qui concerne le compilateur, les zéros étaient
toujours là. Il devrait être clair pour vous maintenant que mettre le mot "START" dans votre programme au lieu du
chiffre 0 n'est qu'une commodité pour vous et agit en fait comme un commentaire puisque le mot "START" vous aide
à comprendre à quoi sert le zéro. pour.
Dans le cas d'un très petit programme, comme celui que vous avez devant vous, peu importe ce que vous utilisez. Si
toutefois vous aviez devant vous un programme de 2000 lignes avec 27 références au START, ce serait une tout autre
affaire. Si vous vouliez changer tous les STARTS du programme par un nouveau numéro, il serait simple de changer
celui #define, mais difficile, et peutêtre désastreux si vous manquiez une ou deux des références.
De la même manière, le préprocesseur trouvera toutes les occurrences du mot "ENDING" et les changera en 9, puis
le compilateur fonctionnera sur le fichier modifié sans savoir que "ENDING" a jamais existé.
C'est une pratique assez courante en programmation C d'utiliser toutes les lettres majuscules pour une constante
symbolique telle que "START" et "ENDING" et d'utiliser toutes les lettres minuscules pour les noms de variables. Vous
pouvez utiliser n’importe quelle méthode, car c’est avant tout une question de goût personnel.
Une macro n'est rien de plus qu'une autre définition, mais comme elle est capable au moins de sembler effectuer
certaines décisions logiques ou certaines fonctions mathématiques, elle a un nom unique. Considérez la troisième
ligne du programme sur votre écran pour un exemple de macro. Dans ce cas, chaque fois que le préprocesseur
trouve le mot « MAX » suivi d'un groupe entre parenthèses, il s'attend à trouver deux termes entre parenthèses
et effectuera un remplacement des termes dans la deuxième définition. Ainsi, le premier terme remplacera
chaque « A » dans la deuxième définition et le deuxième terme remplacera chaque « B » dans la deuxième
définition. Lorsque la ligne 12 du programme est atteinte, "index" sera remplacé par chaque "A" et "count" sera
remplacé par chaque "B". Se souvenir de la construction énigmatique que nous avons étudiée il y a quelques
chapitres révélera que "mx" recevra la valeur maximale de "index" ou "count". De la même manière, la macro
"MIN" aura pour conséquence que "mn" reçoive la valeur minimale de "index" ou de "count". Les résultats sont
ensuite imprimés. Il y en a apparemment beaucoup en plus, ils sont essentiels. Nous discuterons des parenthèses
supplémentaires dans notre prochain programme.
{ int i,offset;
décalage = 5 ; pour
(i = DÉBUT;i <= STOP;i++) {
printf("Le carré de %3d est %4d et son cube est %6d\n", i+offset,SQUR(i+offset),CUBE(i+offset));
printf("Le mauvais de %3d est %6d\n",i+offset,WRONG(i+offset));
}
}
La première ligne définit une macro nommée "WRONG" qui semble obtenir le cube de "A", et c'est effectivement
le cas dans certains cas, mais elle échoue lamentablement dans d'autres. La deuxième macro nommée "CUBE"
récupère le cube dans tous les cas.
Considérez le programme luimême où le CUBE de i+offset est calculé. Si i vaut 1, ce que c'est la première fois,
alors nous chercherons le cube de 1+5 = 6, ce qui donnera 216.
Lorsque nous utilisons "CUBE", nous regroupons les valeurs comme ceci, (1+5)*(1+5)*(1+5) = 6*6*6 = 216.
Cependant, lorsque nous utilisons FAUX, nous les regroupons comme 1+5*1+5*1+5 = 1+5+5+5 = 16, ce qui est
une mauvaise réponse. Les parenthèses sont donc nécessaires pour regrouper correctement les variables.
Il devrait être clair pour vous que "CUBE" ou "WRONG" aboutiraient à une réponse correcte pour un remplacement
d'un seul terme, comme nous l'avons fait dans le programme précédent. Les valeurs correctes du cube et du
carré des nombres sont imprimées ainsi que les valeurs erronées pour votre inspection.
Le reste du programme est simple et sera laissé à votre inspection et à votre compréhension.
Lorsque C va utiliser une chaîne de données d'une manière ou d'une autre, soit pour la comparer avec une autre, la sortir, la
copier dans une autre chaîne, ou autre, les fonctions sont configurées pour faire ce pour quoi elles sont appelées jusqu'à un
NULL, qui est un zéro, est détecté.
La meilleure façon de voir ces principes est d'utiliser un exemple, alors chargez le programme chrstrg.c et affichezle sur
votre moniteur. main( ) { nom du
caractère[5];
La première chose nouvelle est la ligne qui définit une entité de données de type "char". Les crochets définissent un indice
de tableau en C, et dans le cas de l'instruction de définition de données, le 5 entre parenthèses définit 5 champs de données
de type "char", tous définis comme la variable "nom". En langage C, tous les indices commencent à 0 et augmentent de 1 à
chaque pas jusqu'au maximum qui dans ce cas est 4.
Nous avons donc 5 variables de type "char" nommées, "name[0]", "name[1]", "name[2]", "name[3]", et "name[4]". Vous devez
garder à l’esprit qu’en C, les indices vont en réalité de 0 à un de moins que le nombre défini dans l’énoncé de définition.
Le "printf" suivant montre que nous pouvons afficher n'importe quel caractère de la chaîne en utilisant le "%c" et
en nommant le caractère particulier de "name" que nous voulons en incluant l'indice. Le dernier "printf" illustre
comment nous pouvons afficher une partie de la chaîne en indiquant le point de départ à l'aide d'un indice. Le &
spécifie l'adresse de "name[1]". Nous étudierons cela dans le prochain chapitre, mais j'ai pensé que vous
bénéficieriez d'un petit aperçu à venir.
Cet exemple peut vous donner l'impression que les chaînes sont plutôt lourdes à utiliser puisqu'il faut configurer
chaque caractère un par un. C’est une conclusion incorrecte car les chaînes sont très faciles à utiliser, comme
nous le verrons dans le prochain exemple de programme.
Chargez l'exemple de programme strings.c pour un exemple de quelques façons d'utiliser les chaînes.
main( )
strcpy(mixte,nom2); printf("Le
plus grand nom alphabétiquement est %s\n",mixte); strcpy(mixte,nom1); strcat(mixte,"
"); strcat(mixte,nom2);
printf("Les deux noms sont
%s\n",mixte);
Nous définissons d’abord quatre chaînes. Nous arrivons ensuite à une nouvelle fonction qui vous sera très utile, la
fonction "strcpy", ou copie de chaîne. Il copie d'une chaîne à une autre jusqu'à ce qu'il arrive au caractère NULL. Il
est facile de se rappeler lequel reçoit des copies et auquel vous les considérez comme une déclaration de mission.
Ainsi, si vous dites, par exemple, "x = 23;", les données sont copiées de l'entité de droite vers celle de gauche.
Dans la fonction "strcpy", les données sont également copiées de l'entité de droite vers la gauche, de sorte
qu'après l'exécution de la première instruction, name1 contiendra la chaîne "Rosalinda", mais sans les guillemets
doubles, c'est le moyen pour le compilateur de sachant que vous définissez une chaîne.
Les cordes ne sont pas difficiles et sont extrêmement utiles. Vous devriez prendre le temps de vous familiariser
avec eux avant de passer au sujet suivant.
Compilez et exécutez ce programme et observez les résultats pour vérifier leur conformité à cette définition.
Notez que le tableau est défini à peu près de la même manière que nous avons défini un tableau de caractères
afin d'effectuer les manipulations de chaînes dans la dernière section. Nous avons 12 variables entières à
travailler, sans compter celle nommée "index". Les noms des variables sont "values[0]", "values[1]", ... et
"values[11]". Ensuite, nous avons une boucle pour attribuer des données absurdes, mais bien définies, à chacune
des 12 variables, puis imprimer les 12. Vous ne devriez avoir aucune difficulté à suivre ce programme, mais
assurezvous de le comprendre. Compilezle et exécutezle pour voir s’il fait ce que vous attendez de lui.
Je
peux toujours voir que c'était prévu dans le tutoriel. */ char name1[ ] =
bizarre[12]; */
int bizarre[12]; static
char name2[ ] = "Deuxième titre du
programme" ; pour
(index = 0;index < 12;index++) {
trucs[index] = index + 10 ;
/* bizarre[index] = 12.0 * (index + 7); */ bizarre[index] = 12 * (index + 7);
} printf("%s\n",nom1);
printf("%s\n\n",nom2); for (index =
0;index< 12;index++) { printf("%5d %5d
%5d\n",index,stuff[index],bizarre[index]); /* printf("%5d %5d %10.3f\n",index,stuff[index],bizarre[index]);
*/ }
Ce programme a une fonctionnalité supplémentaire pour illustrer comment les chaînes peuvent être
initialisées. La première ligne du programme vous illustre comment initialiser une chaîne de caractères.
Notez que les crochets sont vides, laissant au compilateur le soin de compter les caractères et d'allouer
suffisamment d'espace pour notre chaîne. Une autre chaîne est initialisée dans le corps du programme
mais elle doit être déclarée ici "statique". Cela l'empêche d'être allouée en tant que variable "automatique"
et lui permet de conserver la chaîne une fois le programme démarré. Il n'y a rien d'autre de nouveau ici, les
variables se voient attribuer des données absurdes et les résultats de toutes les absurdités sont imprimés
avec un entête. Ce programme devrait également être facile à suivre pour vous, alors étudiezle jusqu'à ce
que vous soyez sûr de ce qu'il fait avant de passer au sujet suivant.
{ int index ;
matrice int[20] ;
pour (index = 0;index < 20;index++) matrice[index] = /* générer des données */
index + 1;
pour (index = 0;index < 5;index++) /* imprimer les données originales */
printf("Démarrer la matrice[%d] = %d\n",index,matrix[index]);
(i = 0;i <
5;i++) printf("Avant marrix[%d] =
%d\n",i,list[i]);
pour (i = 0;i < 20;i++) liste[i] +=10; /* ajoute 10 à toutes les valeurs */
Dans ce programme, nous définissons un tableau de 20 variables nommé "matrice", puis attribuons des données
absurdes aux variables et imprimons les cinq premières. Ensuite, nous appelons la fonction "dosome" en emportant
l'ensemble du tableau en mettant le nom du tableau entre parenthèses.
La fonction "dosome" a également un nom entre parenthèses mais elle préfère appeler le tableau "list".
La fonction doit être informée qu'elle reçoit réellement un tableau qui lui est transmis et que le tableau est de type
"int". La ligne suivante, avant le crochet qui démarre le programme, fait cela en définissant "liste" comme une variable
de type entier et en incluant les crochets pour indiquer un tableau.
Il n'est pas nécessaire d'indiquer à la fonction combien d'éléments se trouvent dans le tableau, mais vous pouvez le
faire si vous le souhaitez. Généralement, une fonction fonctionne avec un tableau jusqu'à ce qu'un marqueur de fin
de données soit trouvé, comme un NULL pour une chaîne, ou d'autres données ou modèles préalablement définis.
Plusieurs fois, une autre donnée est transmise à la fonction avec le nombre d'éléments avec lesquels travailler.
Sur notre illustration actuelle, nous utiliserons un nombre fixe d’éléments pour rester simple.
Jusqu’à présent, rien n’est différent des fonctions précédentes que nous avons appelées, sauf que cette fois, nous
avons transmis plus de points de données à la fonction que jamais auparavant, après avoir transmis 20 valeurs
entières. Nous imprimons à nouveau les 5 premiers pour voir s'ils ont effectivement été transmis ici. Ensuite, nous
ajoutons dix à chacun des éléments et imprimons les nouvelles valeurs. Enfin, nous revenons au programme
principal et imprimons les mêmes 5 points de données. Nous constatons que nous avons effectivement modifié les
données dans la fonction, et lorsque nous sommes revenus au programme principal, nous avons ramené les
modifications. Compilez et exécutez ce programme pour vérifier cette conclusion.
7.11 Les tableaux transmettent les données dans les deux sens
Nous avons déclaré lors de notre étude des fonctions que lorsque nous transmettions des données à une fonction,
le système en faisait une copie à utiliser dans la fonction qui était jetée à notre retour. Ce n'est pas le cas des
tableaux. Le tableau réel est transmis à la fonction et celleci peut le modifier comme elle le souhaite. Le résultat des
modifications sera disponible dans le programme appelant. Cela peut vous sembler étrange que les tableaux soient
traités différemment des données à point unique, mais c'est le cas.
Cela a vraiment du sens, mais vous devrez attendre que nous ayons des indications pour le comprendre.
Une autre façon de récupérer les données d'une fonction vers le programme appelant consiste à utiliser des pointeurs
que nous aborderons dans le chapitre suivant. Une fois sur place, nous constaterons qu’un tableau est en réalité un
pointeur vers une liste de valeurs. Ne vous laissez pas inquiéter maintenant, cela aura du sens lorsque nous y
arriverons. En attendant, concentrezvous sur les tableaux et comprenezen les bases, car lorsque nous arriverons
à l'étude des structures, nous pourrons définir des tableaux assez élaborés.
La variable "big" est un tableau de 8 x 8 qui contient 8 fois 8 ou 64 éléments au total. Le premier élément est "big[0]
[0]" et le dernier est "big[7][7]". Un autre tableau nommé « énorme » est également défini, qui n'est pas carré pour
illustrer que le tableau n'a pas besoin d'être carré. Les deux sont remplis de données, l’une représentant une table
de multiplication et l’autre étant transformée en table d’addition.
Pour illustrer que des éléments individuels peuvent être modifiés à volonté, l'un des éléments de « big » se voit
attribuer la valeur de l'un des éléments de « huge » après avoir été multiplié par 22. Ensuite, « big[2][2] » est attribué
la valeur arbitraire de 5, et cette valeur est utilisée pour les indices de la prochaine instruction d'affectation. La
troisième instruction d'affectation est en réalité "big[5][5] = 177" car chacun des indices contient la valeur 5. Ceci est
uniquement fait pour illustrer que toute expression valide peut être utilisée pour un indice. Il ne doit remplir que deux
conditions : il doit s'agir d'un nombre entier (même si un "char" fonctionnera tout aussi bien) et il doit être compris
dans la plage de l'indice pour lequel il est utilisé.
La variable matricielle entière "grande" est imprimée sous forme carrée afin que vous puissiez vérifier les valeurs
pour voir si elles ont été définies comme vous l'espériez.
2. Définissez deux tableaux d'entiers, chacun long de 10 éléments, appelés "array1" et "array2". À l'aide d'une
boucle, mettez une sorte de données absurdes dans chacune et ajoutezles terme à terme dans un autre tableau de
10 éléments nommé "tableaux".
Enfin, imprimez tous les résultats dans un tableau avec un numéro
d'index. 1 2 + 10
= 12 2 4 + 20 =
24 3 6 + 30 = 36 etc.
En termes simples, un pointeur est une adresse. Au lieu d'être une variable, c'est un pointeur vers une variable stockée quelque
part dans l'espace d'adressage du programme. Il est toujours préférable d'utiliser un exemple, alors chargez le fichier nommé
pointer.c et affichezle sur votre moniteur pour un exemple de programme contenant des pointeurs.
{ int index,*pt1,*pt2;
indice = 39 ; pt1 /* n'importe quelle valeur numérique */ /*
= &index; point2 = l'adresse de l'index */
point1 ;
printf("La valeur est %d %d %d\n",index,*pt1,*pt2); *pt1 = 13 ; /* cela change
la valeur de l'index */ printf("La valeur est %d %d %d\n",index,*pt1,*pt2);
Pour le moment, ignorez l'instruction de déclaration où l'on définit "index" et deux autres champs commençant par une étoile. On
l'appelle à proprement parler un astérisque, mais pour les raisons que nous verrons plus tard, convenons de l'appeler une étoile.
Si vous observez la première affirmation, il devrait être clair que nous attribuons la valeur 39 à la variable « index ». Ce n’est pas
une surprise, nous le faisons depuis plusieurs programmes maintenant. Cependant, l'instruction suivante dit d'attribuer à "pt1"
une valeur étrange, à savoir la variable "index" avec une esperluette devant elle. Dans cet exemple, pt1 et pt2 sont des pointeurs
et la variable « index » est une variable simple. Maintenant, nous avons un problème. Nous devons apprendre à utiliser les
pointeurs dans un programme, mais pour ce faire, nous devons d'abord définir les moyens d'utiliser les pointeurs dans le
programme.
Les deux règles suivantes vous paraîtront quelque peu déroutantes au début, mais nous devons énoncer les définitions avant de
pouvoir les utiliser. Prenez votre temps et tout s’éclaircira très vite.
1. Un nom de variable précédé d'une esperluette définit l'adresse de la variable et pointe donc vers la variable. Vous pouvez
donc lire la ligne six comme "pt1 se voit attribuer la valeur de l'adresse de "index".
2. Un pointeur précédé d'une « étoile » fait référence à la valeur de la variable pointée par le pointeur.
La ligne neuf du programme peut être lue comme "La valeur stockée (étoilée) à laquelle pointe le pointeur "pt1" se voit attribuer
la valeur 13". Vous pouvez maintenant comprendre pourquoi il est pratique de considérer l’astérisque comme une étoile, cela
ressemble en quelque sorte au mot magasin.
8.3 Aidemémoire
1. Considérez & comme une adresse.
2. Considérez * comme une étoile faisant référence au stockage.
La ligne neuf modifie la valeur en utilisant le pointeur. Puisque le pointeur "pt1" pointe vers la variable "index", alors
mettre une étoile devant le nom du pointeur fait référence à l'emplacement mémoire vers lequel il pointe. La ligne
neuf attribue donc à "index" la valeur 13. Partout dans le programme où il est permis d'utiliser le nom de variable
"index", il est également permis d'utiliser le nom "*pt1" puisqu'ils ont une signification identique jusqu'à ce que le
nom de variable "index" soit utilisé. le pointeur est réaffecté à une autre variable.
Il s'agit certes d'un concept très difficile, mais comme il est largement utilisé dans tous les programmes C, sauf les
plus triviaux, cela vaut la peine de s'attarder sur ce matériel jusqu'à ce que vous le compreniez parfaitement.
Un pointeur doit être défini pour pointer vers un certain type de variable. Suite à une définition appropriée, il ne peut
pas être utilisé pour pointer vers un autre type de variable, sinon cela entraînera une erreur « d'incompatibilité de
type ». De la même manière qu'une variable de type "float" ne peut pas être ajoutée à une variable de type "int", un
pointeur vers une variable "float" ne peut pas être utilisé pour pointer vers une variable entière.
Compilez et exécutez ce programme et observez qu'il n'y a qu'une seule variable et que l'instruction unique de la
ligne 9 modifie la variable qui est affichée trois fois.
Dans ce programme, nous avons défini plusieurs variables et deux pointeurs. Le premier pointeur nommé "there" est un
pointeur vers une variable de type "char" et le second nommé "pt" pointe vers une variable de type "int". Notez également
que nous avons défini deux variables tableau nommées "strg" et "list". Nous les utiliserons pour montrer la correspondance
entre les pointeurs et les noms de tableaux.
À toutes fins pratiques, « strg » est un pointeur. Il comporte cependant une restriction qu’un véritable pointeur n’a pas.
Elle ne peut pas être modifiée comme une variable, mais doit toujours contenir la valeur initiale et pointe donc toujours
vers sa chaîne. Cela pourrait être considéré comme une constante de pointeur et, dans certaines applications, vous
souhaiterez peutêtre un pointeur qui ne puisse en aucun cas être corrompu. Même s'il ne peut pas être modifié, il peut
être utilisé pour faire référence à d'autres valeurs que celle pour laquelle il est défini, comme nous le verrons dans la
prochaine section du programme.
En passant à la ligne 12, la variable "un" se voit attribuer la valeur de la neuvième variable (puisque l'indexation commence
à zéro) et "deux" se voit attribuer la même valeur car nous sommes autorisés à indexer un pointeur pour accéder à des
valeurs plus avancées. dans la chaîne. Les deux variables contiennent désormais le caractère "a".
Puisque "there" est déjà un pointeur, la valeur du onzième élément de "strg" peut lui être attribuée par l'instruction
de la ligne 16 du programme. N'oubliez pas que puisque "il" existe un vrai pointeur, n'importe quelle valeur peut
lui être attribuée tant que cette valeur représente une adresse de type "char". Il doit être clair que les pointeurs
doivent être « tapés » afin de permettre que l'arithmétique des pointeurs décrite dans le dernier paragraphe soit
effectuée correctement. Les troisième et quatrième sorties seront les mêmes, à savoir la lettre « c ».
Toutes les formes d’arithmétique ne sont pas autorisées sur un pointeur. Uniquement les choses qui ont du sens,
étant donné qu’un pointeur est une adresse quelque part dans l’ordinateur. Il serait logique d'ajouter une
constante à une adresse, la faisant ainsi avancer dans la mémoire d'autant de places. De même, la soustraction
est autorisée, en la reculant d'un certain nombre d'emplacements. L’ajout de deux pointeurs n’aurait aucun sens
car les adresses mémoire absolues ne sont pas additives. La multiplication du pointeur n'est pas non plus
autorisée, car ce serait un nombre amusant. Si vous réfléchissez à ce que vous faites réellement, vous
comprendrez ce qui est autorisé et ce qui ne l’est pas.
Vous vous souviendrez peutêtre que dans la leçon sur les fonctions, nous avons mentionné qu'il existait deux
manières de récupérer des données variables à partir d'une fonction. L’une des solutions consiste à utiliser le
tableau, et vous devriez être sur le point de deviner l’autre solution. Si vous devinez grâce à l’utilisation d’un
pointeur, vous avez raison. Chargez et affichez le programme nommé twoway.c pour un exemple.
main( ) { int
Dans twoway.c, il y a deux variables définies dans le programme principal "noix de pécan" et "pommes".
Notez qu’aucun de ces éléments n’est défini comme un pointeur. Nous attribuons des valeurs à ces deux éléments
et les imprimons, puis appelons la fonction "fixup" en emportant avec nous ces deux valeurs. La variable "noix de
pécan" est simplement envoyée à la fonction, mais l'adresse de la variable "pommes" est envoyée à la fonction.
Maintenant, nous avons un problème. Les deux arguments ne sont pas identiques, le second est un pointeur vers
une variable. Il faut en quelque sorte alerter la fonction du fait qu'elle est censée recevoir une variable entière et un
pointeur vers une variable entière. Cela s'avère très simple. Notez que les définitions des paramètres dans la
fonction définissent « noix » comme un entier et « fruit » comme un pointeur vers un entier. L'appel dans le
programme principal est donc désormais en accord avec l'entête de la fonction et l'interface du programme
fonctionnera parfaitement.
Dans le corps de la fonction, nous imprimons les deux valeurs envoyées à la fonction, puis les modifions et
imprimons les nouvelles valeurs. Cela devrait être parfaitement clair pour vous maintenant. La surprise se produit
lorsque nous revenons au programme principal et réimprimons les deux valeurs. Nous constaterons que la valeur
des noix de pécan sera restaurée à sa valeur avant l'appel de la fonction car le langage C fait une copie de l'élément
en question et amène la copie dans la fonction appelée, laissant l'original intact. Dans le cas de la variable
"pommes", nous avons fait une copie d'un pointeur vers la variable et pris la copie du pointeur vers la fonction.
Puisque nous avions un pointeur vers la variable d'origine, même si le pointeur était une copie, nous avions accès
à la variable d'origine et pouvions la modifier dans la fonction. Lorsque nous sommes revenus au programme
principal, nous avons trouvé une valeur modifiée dans "pommes" lorsque nous l'avons imprimé.
En utilisant un pointeur dans un appel de fonction, nous pouvons accéder aux données de la fonction et les modifier
de telle manière que lorsque nous revenons au programme appelant, nous avons une valeur de données modifiée.
Il faut cependant souligner que si vous modifiez la valeur du pointeur luimême dans la fonction, vous aurez restauré
le pointeur à votre retour car le pointeur que vous utilisez dans la fonction est une copie de l'original. Dans cet
exemple, il n'y avait pas de pointeur dans le programme principal car nous avons simplement envoyé l'adresse à la
fonction, mais dans de nombreux programmes, vous utiliserez des pointeurs dans les appels de fonction. L’un des
endroits où vous aurez besoin de pointeurs dans les appels de fonction sera lorsque vous demanderez la saisie de
données à l’aide de routines d’entrée/sortie standard. Ceuxci seront abordés dans les deux prochains chapitres.
{ char c;
printf("Entrez n'importe quel caractère, X = arrêt du programme.\n");
faire
{ c = getchar(); /* Récupère un caractère du ko */
putchar(c); } while /* Afficher le caractère sur le moniteur */
(c != 'X'); /* Jusqu'à ce que X soit atteint */
printf("\nFin du programme.\n");
}
La première chose que vous remarquez est la première ligne du fichier, la ligne #include "stdio.h". Cela ressemble
beaucoup au #define que nous avons déjà étudié, sauf qu'au lieu d'une simple substitution, un fichier entier est lu
à ce stade. Le système trouvera le fichier nommé "stdio.h" et lira l'intégralité de son contenu, remplaçant cette
instruction. Évidemment, le fichier nommé "stdio.h" doit contenir des instructions source C valides qui peuvent être
compilées dans le cadre d'un programme. Ce fichier particulier est composé de plusieurs #defines standard pour
définir certaines des opérations d'E/S standard. Le fichier est appelé fichier d'entête et vous trouverez plusieurs
fichiers d'entête différents sur les disques sources fournis avec votre compilateur. Chacun des fichiers d'entête a
un objectif spécifique et tout ou partie d'entre eux peuvent être inclus dans n'importe quel programme.
La plupart des compilateurs C utilisent les guillemets doubles pour indiquer que le fichier "include" se trouvera
dans le répertoire courant. Quelquesuns utilisent les signes « inférieur à » et « supérieur à » pour indiquer que le
fichier se trouvera dans un fichier d'entête standard. Presque tous les compilateurs MSDOS C utilisent les
guillemets doubles et la plupart exigent que le fichier « include » se trouve dans le répertoire par défaut. Tous les
programmes de ce didacticiel ont des guillemets doubles dans les instructions "include". Si votre compilateur utilise
l'autre notation, vous devrez les modifier avant de compiler.
En fait, le standard industriel de la définition du langage C est devenu le livre écrit par Kernigan et Ritchie, et ils ont
inclus ces fonctions dans leur définition. Vous trouverez souvent, en lisant de la littérature sur C, une référence à
K & R. Cela fait référence au livre écrit par Kernigan et Ritchie. Il vous serait conseillé d’en acheter une copie pour
référence.
Lorsque vous commencerez à écrire des programmes plus volumineux et à les diviser en parties compilées séparément,
vous aurez l'occasion d'utiliser des instructions communes à chacune des parties. Il serait à votre avantage de créer un
fichier séparé contenant les instructions et d'utiliser le #include pour l'insérer dans chacun des fichiers. Si vous souhaitez
modifier un fichier, vous serez assuré d'avoir toutes les déclarations courantes en accord. Nous sommes un peu en
avance, mais vous avez maintenant une idée de la manière dont la directive #include peut être utilisée.
La fonction "getchar()" lit un seul caractère du périphérique d'entrée standard, le clavier étant supposé car il s'agit du
périphérique d'entrée standard, et l'affecte à la variable "c". La fonction suivante "putchar(c)", utilise le périphérique de
sortie standard, le moniteur vidéo, et génère le caractère contenu dans la variable "c". Le caractère est affiché à
l'emplacement actuel du curseur et le curseur avance d'un espace pour le caractère suivant. Le système prend donc en
charge une grande partie des frais généraux pour nous. La boucle continue de lire et d'afficher des caractères jusqu'à ce
que nous tapions un X majuscule qui termine la boucle.
À ce stade, nous devons mentionner que 1616/OS, Unix et MSDOS font parfois des choses différentes, même dans des
domaines apparemment simples comme celuici. Dans certaines versions de HiTech C pour le 1616/OS, les caractères
sont répétés au fur et à mesure que vous les tapez, mais ne sont pas répétés lorsque vous appuyez sur la touche retour.
Cela est dû au fait que la fonction "getchar()" a été redéfinie comme la fonction 1616/OS "getch()", qui n'est pas exactement
la même.
Compilez et exécutez ce programme sur une machine MSDOS pour quelques surprises. Lorsque vous tapez sur le
clavier, vous remarquerez que ce que vous tapez s'affiche fidèlement à l'écran, et lorsque vous appuyez sur la touche
retour, la ligne entière est répétée. En fait, nous ne lui avons dit d'afficher chaque caractère qu'une seule fois, mais il
semble sauvegarder les caractères et les réafficher. Une brève explication s’impose.
Dans Applix 1616, la fonction stdio.h getchar() est définie dans stdio.h comme getch(), qui est fournie par 1616/OS, mais
n'est pas exactement identique aux résultats attendus de "getchar()". Le tampon du clavier est lu par getch() et les
caractères sont transmis directement au programme.
Dans le programme, les caractères sont renvoyés à l'écran par putchar() et le 1616/OS n'a qu'à changer la position du
curseur. Si vous omettez le #include "stdi.h", alors le compilateur HiTech C utilisera sa fonction par défaut "getchar()", qui
est correcte, et fonctionnera de la même manière que MSDOS.
Compilez et exécutez "simpleio.c. Après avoir exécuté le programme plusieurs fois et après avoir été sûrs que vous
comprenez l'explication cidessus, nous passerons à un autre programme.
Ne vous laissez pas décourager par le comportement apparemment étrange du système d'E/S cidessus. C'est étrange,
mais il existe d'autres moyens d'introduire des données dans l'ordinateur. En fait, vous trouverez la méthode cidessus
utile pour de nombreuses applications, et vous trouverez probablement également certaines des méthodes suivantes utiles.
{ char c;
printf("Entrez n'importe quel caractère, terminez le programme avec X\n");
faire
{c = getch(); /* Obtient un personnage */
putchar(c); } while /* Afficher la touche d'activation */
(c != 'X'); printf("\nFin du
programme.\n");
}
Le "getch()" est une nouvelle fonction qui est une fonction "obtenir un caractère". Il diffère de "getchar()" en ce sens qu'il
n'est pas bloqué sous DOS. Il lit le caractère sans écho et le place directement dans le programme où il est immédiatement
utilisé. Cette fonction lit ensuite un caractère, l'affiche immédiatement à l'écran et continue l'opération jusqu'à ce qu'un X
majuscule soit tapé.
Vous reconnaîtrez que l'utilisation accidentelle de cette fonction "getch()" dans le #include "stdio.h" a causé des problèmes
dans le programme précédent.
Lorsque vous compilez et exécutez ce programme, vous constaterez qu'il n'y a pas de répétition des lignes lorsque vous
appuyez sur un retour chariot, et lorsque vous appuyez sur le X majuscule, le programme se termine immédiatement.
Aucun retour chariot n'est nécessaire pour qu'il accepte la ligne contenant le X. Nous avons un autre problème ici, il n'y a
pas de saut de ligne avec le retour chariot.
Cela ne vous apparaît pas clairement dans la plupart des programmes d'application, mais lorsque vous appuyez sur
la touche Entrée, le programme fournit un saut de ligne pour accompagner le retour chariot. Vous devez revenir sur
le côté gauche du moniteur et vous devez également dérouler une ligne. Le saut de ligne n'est pas automatique.
Nous devons également améliorer notre programme pour ce faire. Si vous chargez et affichez le programme
nommé betterin.c, vous trouverez un changement pour intégrer cette fonctionnalité.
# inclure "stdio.h" # définir CR
13 # définir LF 10 /* cela définit CR comme étant 13 */ /* cela définit
LF comme étant 10 */
main( )
{ char c;
printf("saisissez n'importe quel caractère, appuyez sur X pour arrêter.\n");
faire
{c = getch(); /* récupère un caractère */ /* affiche
putchar(c); si (c la touche hit */ /* s'il s'agit d'un retour
== CR) putchar(LF); chariot, émet également un saut de ligne */
} while (c != 'X');
printf("\nFin du programme.\n");
}
Dans betterin.c, nous avons deux instructions supplémentaires au début qui définiront les codes de caractères pour
le saut de ligne (LF) et le retour chariot (CR). Si vous regardez n'importe quel tableau ASCII, vous constaterez que
les codes 10 et 13 sont exactement tels que définis ici. Dans le programme principal, après avoir sorti le caractère,
nous produisons également un saut de ligne qui est le LF. Nous aurions tout aussi bien pu laisser de côté les deux
instructions #define et utiliser "if (c == 13) putchar(10);" mais cela ne serait pas très descriptif de ce que nous faisons
ici. La méthode utilisée dans le programme représente une meilleure pratique de programmation.
Compilez et exécutez betterin.c pour voir s'il fait ce que nous lui avons dit. Il doit afficher exactement ce que vous
tapez, y compris un saut de ligne à chaque retour chariot, et doit s'arrêter immédiatement lorsque vous tapez un X
majuscule.
Si vous utilisez un compilateur non standard, il se peut qu'il ne trouve pas de « CR » car votre système renvoie un
caractère « LF » pour indiquer la fin de ligne. Ce sera à vous de déterminer quelle méthode utilise votre compilateur.
Le moyen le plus rapide consiste à ajouter une instruction "printf" qui imprime le caractère saisi au format décimal.
Nous avons examiné deux méthodes de lecture de caractères dans un programme C et sommes confrontés au
choix de celle que nous devons utiliser. Cela dépend vraiment de l’application car chaque méthode présente des
avantages et des inconvénients. Jetons un coup d'œil à chacun.
Lorsque vous utilisez la première méthode, le 1616/OS fait tout le travail à notre place, en stockant les caractères
dans un tampon d'entrée et en nous signalant lorsqu'une ligne complète a été saisie. Nous pourrions écrire un
programme qui, par exemple, ferait beaucoup de calculs, puis irait chercher des informations. Pendant que nous
faisions les calculs, 1616/OS accumulait une ligne de caractères pour nous, et ils seraient là lorsque nous serions
prêts à les recevoir. Cependant, nous ne pouvions pas lire en une seule frappe car le 1616/OS ne nous rapportait
pas de tampon de caractères tant qu'il n'avait pas reconnu un retour chariot.
Je dois mentionner à ce stade qu'il existe également une fonction "ungetch" qui fonctionne avec la fonction "getch". Si vous
« récupérez » un caractère et constatez que vous êtes allé trop loin, vous pouvez le « récupérer » sur le périphérique d'entrée. Cela
simplifie certains programmes car vous ne savez pas que vous ne voulez pas du personnage tant que vous ne l'avez pas obtenu.
Vous ne pouvez "extraire" qu'un seul caractère sur le périphérique d'entrée, mais cela est suffisant pour accomplir la tâche pour
laquelle cette fonction a été conçue. Il est difficile de démontrer cette fonction dans un programme simple donc son utilisation sera à
vous d'étudier quand vous en aurez besoin.
La discussion ainsi faite dans ce chapitre devrait être une bonne indication que, même si le langage de programmation C est très
flexible, il vous impose une grande responsabilité en tant que programmeur de garder de nombreux détails à l'esprit.
#include "/sys/stdio.h"
main( )
{ int valin;
printf("Saisissez un nombre de 0 à 2 milliards, arrêtez avec 100.\n");
faire
{ scanf("%d",&valin); printf("La /* lit une seule valeur entière dans */
valeur est %d\n",valin); } while (valin != 100);
printf("Fin du programme\n");
}
Au lieu de lire un caractère à la fois, comme nous l'avons fait dans les trois derniers fichiers, nous lisons une valeur entière entière
avec un seul appel en utilisant la fonction nommée "scanf". Cette fonction est très similaire à "printf" que vous utilisez depuis un
certain temps maintenant, sauf qu'elle est utilisée pour l'entrée au lieu de la sortie. Examinez la ligne avec le "scanf" et vous
remarquerez qu'elle ne demande pas directement la variable "valin", mais donne l'adresse de la variable puisqu'elle s'attend à ce
qu'une valeur soit renvoyée pour la fonction. Rappelons qu'une fonction doit avoir l'adresse d'une variable pour pouvoir renvoyer la
valeur au programme appelant. Ne pas fournir de pointeur dans la fonction "scanf" est probablement le problème le plus courant
rencontré lors de l'utilisation de cette fonction.
La fonction "scanf" scanne la ligne d'entrée jusqu'à ce qu'elle trouve le premier champ de données. Il ignore les espaces de début et
dans ce cas, il lit les caractères entiers jusqu'à ce qu'il trouve un espace ou un caractère décimal invalide, moment auquel il arrête la
lecture et renvoie la valeur.
En vous souvenant de notre discussion cidessus sur la façon dont fonctionne le tampon d'entrée du 1616/OS, il devrait être clair que
rien n'est réellement effectué jusqu'à ce qu'une ligne complète soit entrée et se termine par un retour chariot. À ce momentlà, la
lecture du tampon est entrée et notre programme recherchera sur la ligne en lisant toutes les valeurs entières qu'il peut trouver
jusqu'à ce que la ligne soit complètement analysée. C'est parce que nous sommes dans une boucle et nous lui disons de trouver une
valeur, de l'imprimer, d'en trouver une autre, de l'imprimer, etc. Si vous entrez
Le paragraphe cidessus est vrai pour le compilateur HiTech C pour Applix, qui utilise des entiers 32 bits.
Les compilateurs MSDOS utilisent généralement une valeur entière de 16 bits et n'acceptent donc que des valeurs
d'entrée allant jusqu'à 32 767. Si votre compilateur est différent, les mêmes principes seront vrais mais à des limites
différentes de celles indiquées cidessus.
Compilez et exécutez ce programme, en entrant plusieurs nombres sur une ligne pour voir les résultats, et avec un
nombre variable d'espaces entre les nombres. Essayez de saisir des nombres trop grands pour voir ce qui se passe, et
enfin saisissez des caractères non valides pour voir ce que le système fait avec les caractères non décimaux.
#include "/sys/stdio.h"
main( )
{ char gros[25];
printf("Saisissez une chaîne de caractères, jusqu'à 25 caractères.\n"); printf("Un X dans la
colonne 1 provoque l'arrêt du programme.\n");
faire
{ scanf("%s",grand);
printf("La chaîne est > %s\n",big); } while (big[0] !='X');
printf("Fin du programme.\n");
}
La variable dans le "scanf" n'a pas besoin d'un & car "big" est une variable tableau et par définition c'est déjà un pointeur.
Ce programme ne devrait nécessiter aucune explication supplémentaire. Compilezle et exécutezle pour voir s'il
fonctionne comme vous le souhaitez.
Vous avez probablement eu une surprise lorsque vous l'avez exécuté car il séparait votre phrase en mots séparés.
Lorsqu'il est utilisé en mode de saisie chaîne, "scanf" lit les caractères dans la chaîne jusqu'à ce qu'elle arrive à la fin
d'une ligne ou à un caractère vide. Par conséquent, il lit un mot, trouve le blanc qui le suit et affiche le résultat. Puisque
nous sommes dans une boucle, ce programme continue de lire les mots jusqu'à épuiser le tampon d'entrée DOS. Nous
avons écrit ce programme pour qu'il s'arrête chaque fois qu'il trouve un X majuscule dans la colonne 1, mais comme la
phrase est divisée en mots individuels, il s'arrêtera chaque fois qu'un mot commence par un X majuscule. Essayez
d'entrer une phrase de 5 mots avec un X majuscule comme le premier caractère du troisième mot. Vous devriez voir les
trois premiers mots affichés et les deux derniers simplement ignorés lorsque le programme s'arrête.
Essayez de saisir plus de 24 caractères pour voir ce que fait le programme. Cela devrait générer une erreur, mais cela
dépendra fortement du système que vous utilisez. Dans un programme réel, il est de votre responsabilité de compter les
caractères et de vous arrêter lorsque le tampon d'entrée est plein. Vous avez peutêtre le sentiment qu'une grande
responsabilité vous incombe lorsque vous écrivez en C. C'est vrai, mais vous bénéficiez également d'une grande flexibilité
dans le marché.
Ne laissez pas cela vous inquiéter. Au fur et à mesure que vous gagnerez en expérience avec le C, vous gérerez facilement
vos besoins en E/S.
Un dernier point doit être fait à propos de ces fonctions d'E/S. Il est parfaitement permis de mélanger les fonctions "scanf" et
"getchar" lors des opérations de lecture. De la même manière, il est également possible de mélanger les fonctions de sortie
"printf" et "putchar".
nombre[0] = 74 ;
nombre[1] = 18 ;
nombre[2] = 33 ;
nombre[3] = 30 ;
nombre[4] = 97 ;
sprintf(ligne,%d %d %d %d %d\n",nombres[0],nombres[1],
nombres[2],nombres[3],nombres[4]);
printf("%s",ligne);
sscanf(ligne,"%d %d %d %d %d",&result[4],&result[3], (résultat+2),
(résultat+1),résultat);
pour (index = 0;index < 5;index++)
Dans inmem.c, nous définissons quelques variables, puis attribuons des valeurs à celles nommées "nombres" à des fins
d'illustration, puis utilisons une fonction "sprintf" sauf qu'au lieu d'imprimer la ligne de sortie sur un périphérique, elle imprime
la ligne de la sortie formatée en une chaîne de caractères en mémoire. Dans ce cas, la chaîne va à la variable chaîne "line",
car c'est le nom de chaîne que nous avons inséré comme premier argument dans la fonction "sprintf". Les espaces après le
2ème %d ont été placés là pour illustrer que la fonction suivante recherchera correctement sur toute la ligne. Nous imprimons
la chaîne résultante et constatons que la sortie est identique à ce qu'elle aurait été en utilisant un "printf" au lieu du "sprintf" en
premier lieu. Vous le verrez lorsque vous compilerez et exécuterez le programme sous peu.
Puisque la chaîne générée est toujours en mémoire, on peut désormais la lire avec la fonction "sscanf". Nous disons à la
fonction dans son premier argument que "line" est la chaîne à utiliser pour son entrée, et les parties restantes de la ligne sont
exactement ce que nous utiliserions si nous devions utiliser la fonction "scanf" et lire des données de l'extérieur. l'ordinateur.
Notez qu'il est essentiel que nous utilisions des pointeurs vers les données car nous souhaitons renvoyer les données d'une
fonction. Juste pour illustrer qu'il existe de nombreuses façons de déclarer un pointeur, plusieurs méthodes sont utilisées, mais
toutes sont des pointeurs. Les deux premiers déclarent simplement
main( )
{ int index ;
pour (index = 0;index < 6;index++) {
printf("Cette ligne va vers la sortie standard.\n"); fprintf(stderr,"Cette ligne va au
périphérique d'erreur.\n");
}
sortie(4); /*
Cela peut être testé avec la commande MSDOS errorlevel dans un fichier batch.
Le numéro renvoyé est utilisé comme suit :
Le programme consiste en une boucle avec deux messages émis, l'un vers le périphérique de sortie standard et l'autre vers
le périphérique d'erreur standard. Le message adressé à la norme inclut le nom du périphérique « stderr » comme premier
argument. A part ces deux petits changements, c'est la même chose que notre fonction standard "printf". (Vous verrez
davantage la fonction "fprintf" dans le prochain chapitre, mais son fonctionnement s'intègre mieux dans le cadre de ce
chapitre.) Ignorez la ligne avec "exit" pour le moment, nous y reviendrons.
Compilez et exécutez ce programme, et vous trouverez 12 lignes de sortie sur le moniteur. Pour voir la différence, exécutez
à nouveau le programme avec la sortie redirigée vers un fichier nommé « STUFF » en entrant la ligne suivante à l'invite 1616/
OS ; F0/ spécial >trucs
La plupart des compilateurs qui fonctionnent en plusieurs passes renvoient un 1 avec ce mécanisme pour indiquer qu'une erreur
fatale s'est produite et que ce serait une perte de temps de passer à une autre passe, ce qui entraînerait
encore plus d'erreurs.
Il est donc judicieux d'utiliser un fichier batch pour compiler les programmes et tester la valeur renvoyée pour
les erreurs.
de char[25];
indice int ;
Nous commençons comme avant par l'instruction "include" pour "stdio.h", puis définissons quelques variables à utiliser
dans l'exemple, y compris un nouveau type plutôt étrange.
Le type "FILE" est utilisé pour une variable de fichier et est défini dans le fichier "stdio.h". Il est utilisé pour définir un pointeur
de fichier à utiliser dans les opérations sur les fichiers. La définition de C contient l'exigence d'un pointeur vers un "FILE",
et comme d'habitude, le nom peut être n'importe quel nom de variable valide.
LECTURE ("r")
Le deuxième paramètre est l'attribut du fichier et peut être l'une des trois lettres suivantes : "r", "w" ou "a", et doit être en
minuscules. Lorsqu'un "r" est utilisé, le fichier est ouvert en lecture, un "w" est utilisé pour indiquer un fichier à utiliser pour
l'écriture et un "a" indique que vous souhaitez ajouter des données supplémentaires aux données déjà présentes dans un
fichier. fichier existant. L'ouverture d'un fichier en lecture nécessite que le fichier existe déjà.
S'il n'existe pas, le pointeur de fichier sera défini sur NULL et pourra être vérifié par le programme.
ÉCRITURE ("w")
Lorsqu'un fichier est ouvert en écriture, il sera créé s'il n'existe pas déjà et il sera réinitialisé s'il existe, entraînant la
suppression de toutes les données déjà présentes.
ANNEXE ("a")
Vous pouvez ouvrir un fichier en écriture, le fermer et le rouvrir en lecture, puis le fermer et l'ouvrir à nouveau pour l'ajouter,
etc. Chaque fois que vous l'ouvrez, vous pouvez utiliser le même pointeur de fichier ou un autre. un. Le pointeur de fichier
est simplement un outil que vous utilisez pour pointer vers un fichier et vous décidez vers quel fichier il pointera.
Compilez et exécutez ce programme. Lorsque vous l'exécutez, vous n'obtiendrez aucune sortie sur le moniteur car il n'en
génère aucune. Après l'avoir exécuté, recherchez dans votre répertoire un fichier nommé TENLINES.TXT et "tapezle".
C'est là que sera votre sortie. Comparez la sortie avec celle spécifiée dans le programme. Il faudrait être d'accord.
N'effacez pas encore le fichier nommé TENLINES.TXT. Nous l'utiliserons dans certains des autres exemples de ce chapitre.
} fclose(point);
}
Le programme est en fait constitué de deux boucles "for" imbriquées. La boucle externe consiste simplement à compter jusqu’à
dix, de sorte que nous parcourrons la boucle interne dix fois. La boucle interne appelle la fonction « putc » à plusieurs reprises
jusqu'à ce qu'un caractère dans « autres » soit détecté comme étant un zéro.
Lorsque la ligne de texte « autres » est épuisée, une nouvelle ligne est nécessaire car une nouvelle ligne n'a pas été incluse dans
la définition cidessus. Un seul "putc" est ensuite exécuté qui génère le caractère "\n" pour renvoyer le chariot et effectuer un saut
de ligne.
Lorsque la boucle externe a été exécutée dix fois, le programme ferme le fichier et se termine.
Compilez et exécutez ce programme, mais encore une fois, il n'y aura aucune sortie sur le moniteur.
Suite à l'exécution du programme, "tapez" le fichier nommé TENLINES.TXT et vous verrez que les 10 nouvelles lignes ont été
ajoutées à la fin des 10 qui existaient déjà. Si vous l'exécutez à nouveau, 10 lignes supplémentaires seront ajoutées. Encore une
fois, n’effacez pas ce fichier car nous n’en avons toujours pas fini.
#include "/sys/stdio.h"
principal( )
{
FICHIER *drôle ;
int c;
drôle = fopen("TENLINES.TXT","r"); if (drôle == NULL)
printf("Le fichier n'existe pas\n"); else { do { c = getc (drôle); /* récupère un
caractère
du
fichier /* l'affiche sur le moniteur */ putchar(c); } while (c != EOF); /* répéter jusqu'à EOF */
(fin du fichier)
*/
} fclose(drôle);
}
Ce programme commence par le familier "include", quelques définitions de données et l'instruction d'ouverture de fichier qui ne
devrait nécessiter aucune explication, sauf le fait qu'un "r" est utilisé ici parce que nous voulons le lire. Dans ce programme, nous
vérifions que le fichier existe, et si c'est le cas, nous exécutons le corps principal du programme. Si ce n'est pas le cas, nous
imprimons un message et quittons. Si le fichier n'existe pas, le système définira le pointeur égal à NULL que nous pourrons tester.
À ce stade, nous sommes potentiellement confrontés à l'un des problèmes de programmation les plus courants et les plus
déroutants en C. La variable renvoyée par la fonction "getc" est un caractère, nous pourrions donc utiliser une variable
"char" à cette fin. Il y a cependant un problème avec cela, car sur certaines implémentations de C, sinon la plupart, l'EOF
renvoie un moins qu'une variable de type "char" n'est pas capable de contenir. Une variable de type "char" ne peut avoir
que des valeurs comprises entre zéro et 255, elle renverra donc un 255 pour un moins un sur les compilateurs qui utilisent
un moins un pour EOF. Il s’agit d’un problème très frustrant à rechercher car aucun diagnostic n’est donné. Le programme
ne pourra tout simplement jamais trouver l'EOF et ne terminera donc jamais la boucle. Ceci est facile à éviter, utilisez
toujours une variable de type "int" pour renvoyer un EOF. Vous pouvez savoir ce que votre compilateur utilise pour EOF en
consultant le fichier "stdio.h" où EOF est défini. C'est l'endroit habituel pour définir de telles valeurs.
Il y a un autre problème avec ce programme, mais nous nous en soucierons lorsque nous arriverons au prochain programme
et le résoudrons avec celui qui suit.
Une fois que vous avez compilé et exécuté ce programme et que vous êtes satisfait des résultats, ce serait un bon exercice
de changer le nom de "TENLINES.TXT" et d'exécuter à nouveau le programme pour voir que le test NULL fonctionne
réellement comme indiqué. Assurezvous de modifier à nouveau le nom car nous n'en avons toujours pas fini avec
"TENLINES.TXT".
fp1 = fopen("TENLINES.TXT","r");
faire
{ c = fscanf(fp1,"%s",un mot); /* a obtenu un mot du fichier */ printf("%s\n",oneword); /* l'afficher sur
le moniteur */ } while (c != EOF); /* répéter jusqu'à EOF */
fclose(fp1);
}
Ce programme est presque identique au précédent sauf qu'il utilise la fonction "fscanf" pour lire une chaîne à la fois. Étant
donné que la fonction "fscanf" arrête la lecture lorsqu'elle trouve un espace ou un caractère de nouvelle ligne, elle lira un
mot à la fois et affichera les résultats mot par ligne.
Vous le verrez lorsque vous le compilerez et l’exécuterez, mais nous devons d’abord examiner un problème de
programmation.
L'inspection du programme révélera que lorsque nous lisons des données et détectons l'EOF, nous imprimons quelque
chose avant de vérifier l'EOF, ce qui entraîne une ligne d'impression supplémentaire. Ce que nous imprimons habituellement
est la même chose imprimée lors du passage précédent dans la boucle, car elle est toujours
Compilez et exécutez le programme original que nous avons étudié, readtext.c et observez le résultat. Si vous n'avez pas
modifié TENLINES.TXT, vous vous retrouverez avec "Supplémentaire" et "lignes". sur deux lignes distinctes avec des
"lignes" supplémentaires. affiché à cause du "printf" avant de vérifier EOF.
Compilez et exécutez readgood.c et observez que les "lignes" supplémentaires. ne s'affiche pas en raison de la vérification
supplémentaire de l'EOF au milieu de la boucle. C'est également le problème évoqué lorsque nous avons examiné
readchar.c, mais j'ai choisi de ne pas l'exposer ici car l'erreur dans la sortie n'était pas si évidente.
#include "/sys/stdio.h"
principal( )
{
FICHIER *fp1 ;
char un mot[100]; char *c;
fp1 = fopen("TENLINES.TXT","r");
faire
{ c = fgets (un mot, 100, fp1); /* récupère une ligne du fichier */ if (c != NULL); printf("%s",un mot); /*
l'afficher sur le moniteur
*/ /* répéter jusqu'à NULL
} while (c != NULL); */
fclose(fp1);
}
Nous utilisons "fgets" qui lit une ligne entière, y compris le caractère de nouvelle ligne, dans un tampon.
Le tampon à lire est le premier argument de l'appel de fonction, et le nombre maximum de caractères à lire est le deuxième
argument, suivi du pointeur de fichier. Cette fonction lira les caractères dans le tampon d'entrée jusqu'à ce qu'elle trouve un
caractère de nouvelle ligne ou qu'elle lise le nombre maximum de caractères autorisés moins un. Il laisse un caractère pour
la fin de la chaîne, le caractère NULL. De plus, s’il trouve un EOF, il renverra une valeur NULL. Dans notre exemple, lorsque
l'EOF est trouvé, le pointeur "c" se verra attribuer la valeur NULL. NULL est défini comme zéro dans votre fichier "stdio.h".
Lorsque nous constatons que "c" a reçu la valeur NULL, nous pouvons arrêter le traitement des données, mais nous devons
vérifier avant d'imprimer, comme dans le programme précédent.
#include "stdio.h"
fclose(fp1);
}
Compilez et exécutez ce programme. Lorsqu'il demande un nom de fichier, entrez le nom et l'extension de n'importe quel
fichier texte disponible, même l'un des exemples de programmes C.
Chargez le dernier fichier d'exemple de ce chapitre, celui nommé printdat.c pour un exemple de comment imprimer. Ce
programme ne devrait pas vous surprendre, nous allons donc le parcourir très rapidement.
#include "/sys/stdio.h"
principal( )
{
FICHIER *drôle,*imprimante ; int c;
Encore une fois, nous ouvrons TENLINES.TXT pour la lecture et nous ouvrons PRN pour l'écriture. L'impression est
identique à l'écriture de données sur un fichier disque, sauf que nous utilisons un nom standard pour le nom de fichier. Il
n'existe pas de normes définies en ce qui concerne le ou les noms à utiliser pour l'imprimante, mais les noms du 1616/OS
sont « CENT : », « SA : » et « SB : ». Vérifiez votre documentation pour votre implémentation particulière.
Certains des compilateurs MSDOS les plus récents utilisent un pointeur de fichier prédéfini tel que « stdprn » pour le fichier
d'impression. Encore une fois, vérifiez votre documentation.
Le programme est simplement une boucle dans laquelle un caractère est lu, et si ce n'est pas l'EOF, il est affiché et imprimé.
Lorsque l'EOF est trouvé, le fichier d'entrée et les fichiers de sortie de l'imprimante sont tous deux fermés.
Vous pouvez maintenant effacer TENLINES.TXT de votre disque. Nous ne l’utiliserons dans aucun des chapitres suivants.
2. Demander un nom de fichier à lire. Lisez le fichier ligne par ligne et affichezle sur le moniteur
avec les numéros de lignes.
Une structure est un type de données défini par l'utilisateur. Vous avez la possibilité de définir un nouveau type de
données considérablement plus complexe que les types que nous utilisons. Une structure est une combinaison de
plusieurs types de données différents préalablement définis, y compris d'autres structures que nous avons définies.
Une définition facile à comprendre est qu'une structure est un regroupement de données liées d'une manière
pratique pour le programmeur ou l'utilisateur du programme. La meilleure façon de comprendre une structure est
de regarder un exemple, donc si vous chargez et affichez struct1.c, c'est exactement ce que nous ferons.
principal( )
{
struct { char
initial ; /* initiale du nom de famille */ int âge; */ note int ; /* note
de l'enfant à l'école */ } garçon,
/* âge de
fille;
l'enfant
garçon.initial = 'R';
garçon.âge = 15 ;
garçon.grade = 75 ;
fille.âge = garçon.âge 1 ; fille.grade /* elle a un an de moins */
= 82 ; fille.initial = 'H';
Le programme commence par une définition de la structure. Le mot clé « struct » est suivi de quelques variables
simples entre les accolades, qui sont les composants de la structure. Après l'accolade fermante, vous trouverez
deux variables répertoriées, à savoir « garçon » et « fille ». Selon la définition d'une structure, « garçon » est
désormais une variable composée de trois éléments, « initiale », « âge » et « grade ».
Chacun des trois champs est associé à "boy", et chacun peut stocker une variable de son type respectif. La variable
"fille" est également une variable contenant trois champs portant les mêmes noms que ceux de "garçon" mais qui
sont en réalité des variables différentes. Nous avons donc défini 6 variables simples.
Les structures sont une méthode très utile pour regrouper des données afin de rendre un programme plus facile
à écrire et à comprendre. Ce premier exemple est trop simple pour vous donner ne seraitce qu'une idée de
l'intérêt de l'utilisation des structures, mais continuez à travers ces leçons et vous finirez par voir l'intérêt de
l'utilisation des structures.
{ struct
{ char initial ; âge
entier ; note
internationale ; }
enfants[12] ;
indice int ; pour
(index = 0;index < 12;index++) {
enfants[index].initial = 'A' + index; enfants[index].age
= 16 ; enfants[index].grade = 84 ;
} enfants[3].age = enfants[5].age = 17 ;
enfants[2].grade = enfants[6].grade = 92;
enfants[4].grade = 57 ; for
(index = 0;index < 12;index++) printf("%c a %d ans
et a obtenu une note de %d\n", kids[index].initial,kids[index].age, kids[index ].grade);
Ce programme contient la même définition de structure que précédemment mais cette fois nous définissons un
tableau de 12 variables nommées "kids". Ce programme contient donc 12 fois 3 = 36 variables simples, chacune
pouvant stocker une donnée à condition qu'elle soit du bon type. Nous définissons également une variable simple
nommée "index" à utiliser dans les boucles for.
Dans les prochaines instructions du programme, nous attribuons de nouvelles valeurs à certains champs pour illustrer
la méthode utilisée pour y parvenir. Il doit être explicite, aucun commentaire supplémentaire ne sera donc fourni.
{ struct { char
initial ; âge entier ;
note
internationale ; }
enfants[12],*point;
indice int ;
pour (index = 0;index < 12;index++) { point = enfants +
index; point>initial = 'A' + index ;
point>âge = 16 ; point > note = 84 ;
enfants[3].age = enfants[5].age = 17 ;
enfants[2].grade = enfants[6].grade = 92; enfants[4].grade
= 57 ; pour (index = 0;index <
12;index++) { point = enfants + index; printf("%c a %d ans
et a obtenu une note de %d\n",
(*point).initial,kids[index].age, point>grade);
}
}
La première différence apparaît dans la définition des variables suite à la définition de la structure.
Dans ce programme, nous définissons un pointeur nommé "point" qui est défini comme un pointeur pointant vers
La différence suivante réside dans la boucle for où nous utilisons le pointeur pour accéder aux champs de données.
Puisque « kids » est une variable pointeur qui pointe vers la structure, nous pouvons définir « point » en termes de
« kids ». La variable "kids" est une constante, sa valeur ne peut donc pas être modifiée, mais "point" est une variable
de pointeur et peut se voir attribuer n'importe quelle valeur compatible avec sa nécessité de pointer vers la structure.
Si nous attribuons la valeur de « kids » à « point », alors il devrait être clair qu'il pointera vers le premier élément du
tableau, une structure contenant trois champs.
L'ajout de 1 à "point" le fera désormais pointer vers le deuxième champ du tableau en raison de la façon dont les
pointeurs sont gérés en C. Le système sait que la structure contient trois variables et il sait combien d'éléments de
mémoire sont nécessaires pour stocker le structure complète. Par conséquent, si nous lui disons d’en ajouter un au
pointeur, il ajoutera en fait le nombre d’éléments de mémoire requis pour accéder à l’élément suivant du tableau. Si,
par exemple, nous devions ajouter 4 au pointeur, cela avancerait la valeur du pointeur de 4 fois la taille de la
structure, ce qui le ferait pointer 4 éléments plus loin le long du tableau. C'est la raison pour laquelle un pointeur ne
peut pas être utilisé pour pointer vers un type de données autre que celui pour lequel il a été défini.
Revenons maintenant au programme affiché sur votre moniteur. Il ressort clairement de la discussion précédente
qu’au fur et à mesure que nous parcourons la boucle, le pointeur pointe à chaque fois vers le début de l’un des
éléments du tableau. On peut donc utiliser le pointeur pour référencer les différents éléments de la structure. Faire
référence aux éléments d'une structure avec un pointeur se produit si souvent en C qu'une méthode spéciale pour
le faire a été conçue. Utiliser "point>initial" revient à utiliser "(*point).initial", ce qui est vraiment la façon dont nous
l'avons fait dans les deux derniers programmes. N'oubliez pas que *point correspond aux données vers lesquelles
pointe le pointeur et que la construction doit être claire. Le ">" est composé du signe moins et du signe supérieur.
Puisque le pointeur pointe vers la structure, il faut encore une fois définir à quel élément on souhaite faire référence
à chaque fois que l'on utilise l'un des éléments de la structure. Il existe, comme nous l'avons vu, plusieurs méthodes
différentes pour faire référence aux membres de la structure, et dans la boucle for utilisée pour la sortie à la fin du
programme, nous utilisons trois méthodes différentes. Cela serait considéré comme une très mauvaise pratique de
programmation, mais nous le faisons ici pour vous illustrer qu'ils conduisent tous au même résultat. Ce programme
nécessitera probablement une certaine étude de votre part pour bien le comprendre, mais cela vaudra la peine de
consacrer votre temps et vos efforts à comprendre ces principes.
Chargez et affichez le fichier nommé nested.c pour un exemple de structure imbriquée. Les structures que nous
avons vues jusqu’à présent sont très simples, bien qu’utiles. Il est possible de définir des structures contenant des
dizaines, voire des centaines ou des milliers d'éléments mais il serait avantageux pour les programmeurs de ne
pas définir tous les éléments d'un seul coup mais plutôt d'utiliser une structure de définition hiérarchique. Ceci sera
illustré avec le programme sur votre moniteur. main( ) { struct personne { nom de caractère[25]; âge
entier ;
statut de caractère ; } ;
/* M = marié, S = célibataire */
étudiant[7][descrip]âge = 14 ; étudiant[12]
[grade] = 87 ;
}
La première structure contient trois éléments mais n'est suivie d'aucun nom de variable. Nous n'avons donc défini aucune
variable seulement une structure, mais comme nous avons inclus un nom au début de la structure, la structure est nommée
"personne". Le nom « personne » peut être utilisé pour faire référence à la structure mais pas à une quelconque variable
de ce type de structure. C'est donc un nouveau type que nous avons défini, et nous pouvons utiliser le nouveau type à peu
près de la même manière que nous utilisons "int", "char", ou tout autre type existant en C. La seule restriction est que ce
nouveau nom doit toujours être associé au mot réservé "struct".
La définition de structure suivante contient trois champs, le champ du milieu étant la structure définie précédemment que
nous avons nommée « personne ». La variable qui a le type "personne" est nommée "descrip". Ainsi, la nouvelle structure
contient deux variables simples, "grade" et une chaîne nommée "lunch[25]", et la structure nommée "descrip". Puisque
« descrip » contient trois variables, la nouvelle structure contient en réalité 5 variables. Cette structure reçoit également un
nom « alldat », qui est une autre définition de type. Enfin nous définissons un tableau de 53 variables chacune avec la
structure définie par "alldat", et chacune avec le nom "student". Si cela est clair, vous verrez que nous avons défini un total
de 53 fois 5 variables, chacune étant capable de stocker une valeur.
Puisque nous avons une nouvelle définition de type, nous pouvons l'utiliser pour définir deux variables supplémentaires.
Les variables "teacher" et "sub" sont définies dans l'instruction suivante comme étant des variables du type "alldat", de
sorte que chacune de ces deux variables contient 5 champs pouvant stocker des données.
Dans les cinq prochaines lignes du programme, nous attribuerons des valeurs à chacun des champs de « professeur ».
Le premier champ est le champ « grade » et est traité comme les autres structures que nous avons étudiées car il ne fait
pas partie de la structure imbriquée. Nous souhaitons ensuite attribuer une valeur à son âge qui fait partie de la structure
imbriquée. Pour aborder ce champ, nous commençons par le nom de la variable "enseignant" auquel nous ajoutons le
nom du groupe "descrip", puis nous devons définir quel champ de
Notez que les noms de variables dans la fonction "strcpy" sont toujours des noms de variables même s'ils sont constitués chacun
de plusieurs parties.
La variable "sub" se voit attribuer des valeurs absurdes de la même manière, mais dans un ordre différent puisqu'elles ne doivent
pas nécessairement apparaître dans un ordre requis. Enfin, quelquesunes des variables « étudiants » se voient attribuer des
valeurs à des fins d'illustration et le programme se termine. Aucune des valeurs n'est imprimée à titre d'illustration puisque plusieurs
ont été imprimées dans les derniers exemples.
Compilez et exécutez ce programme, mais lorsque vous l'exécutez, vous risquez d'obtenir une erreur de « débordement de pile ».
C utilise sa propre pile interne pour stocker les variables automatiques, mais la plupart des compilateurs C n'utilisent qu'une petite
pile (généralement 2048 octets) par défaut. Ce programme a plus que cela dans les structures définies il vous faudra donc
augmenter la taille de la pile. La méthode permettant d'effectuer cette opération pour certains compilateurs MSDOS est indiquée
dans le fichier COMPILER.DOC qui accompagne ce didacticiel. Consultez la documentation de votre compilateur pour plus de
détails sur votre compilateur, mais cela ne semble pas être présent dans le compilateur HiTech. Utilisez le programme Applix
chmem pour ajuster la taille de la pile du code exécutable après la compilation.
Il existe une autre façon de contourner ce problème : déplacer les définitions de structure en dehors du programme où elles seront
des variables externes et donc statiques. Résultat : ils ne seront pas conservés sur la pile interne et la pile ne débordera donc pas.
Ce serait bien que vous essayiez les deux méthodes pour résoudre ce problème.
Les structures peuvent contenir des tableaux d'autres structures qui à leur tour peuvent contenir des tableaux de types simples ou
d'autres structures. Cela peut continuer encore et encore jusqu'à ce que vous perdiez toute raison de continuer. J'essaie seulement
de vous illustrer que les structures sont très précieuses et que vous les trouverez d'une grande aide pour la programmation si
vous les utilisez à bon escient. Soyez conservateur au début et devenez plus audacieux à mesure que vous gagnez en expérience.
Les structures plus complexes ne seront pas illustrées ici, mais vous trouverez des exemples de structures supplémentaires dans
les programmes exemples inclus dans le dernier chapitre de ce tutoriel. Par exemple, consultez le fichier "#include" "STRUCT.H".
{ valeur int
courte ; /* Ceci est la première partie de l'union */
/* Ça doit être un court avec le HITECH */
/* Compilateur C car l'int est de 32 bits */
struct { char
en premier ; /* Ces deux valeurs sont les secondes */
}
}
Dans cet exemple, nous avons deux éléments dans l'union, la première partie étant l'entier nommé "valeur", qui est
stocké sous forme de variable de deux octets quelque part dans la mémoire de l'ordinateur. Le deuxième élément
est constitué de deux variables de caractères nommées « premier » et « seconde ». Ces deux variables sont
stockées dans les mêmes emplacements de stockage que la "valeur", car c'est ce que fait une union. Une union
permet de stocker différents types de données dans les mêmes emplacements de stockage physiques. Dans ce
cas, vous pouvez mettre un nombre entier dans "valeur", puis le récupérer dans ses deux moitiés en obtenant
chaque moitié en utilisant les deux noms "premier" et "seconde". Cette technique est souvent utilisée pour regrouper
des octets de données lorsque vous combinez, par exemple, des octets à utiliser dans les registres du
microprocesseur.
L'accès aux champs de l'union est très similaire à l'accès aux champs d'une structure et vous sera laissé à
déterminer en étudiant l'exemple.
Une note supplémentaire doit être donnée ici concernant le programme. Lorsqu'il est exécuté avec la plupart des
compilateurs, les données seront affichées avec deux f en tête en raison de la sortie hexadécimale promouvant les
variables de type char en int et étendant le bit de signe vers la gauche. La conversion des champs de données de
type char en champs de type int avant l'affichage devrait supprimer les premiers f de votre affichage. Cela impliquera
de définir deux nouvelles variables de type int et de leur attribuer les variables de type char.
Cela vous sera laissé en exercice. Notez que le même problème se produira également dans quelquesuns des
fichiers ultérieurs.
Lors de l'utilisation du compilateur HiTech C, les données seront affichées avec plus de deux « f » en tête, en raison
de l'utilisation de la valeur int courte.
Compilez et exécutez ce programme et observez que les données sont lues sous la forme d'une variable "int" et de
deux variables "char". Les variables "char" sont inversées dans l'ordre en raison de la façon dont une variable "int"
est stockée en interne dans votre ordinateur. Ne vous inquiétez pas pour ça. Ce n’est pas un problème mais cela
peut être un domaine d’étude très intéressant si vous le souhaitez.
Les variables 'char' sont dans leur position correcte lorsqu'elles sont compilées avec HiTech C.
Supposons que vous souhaitiez créer une grande base de données comprenant des informations sur de nombreux types de véhicules.
Il serait idiot d’inclure le nombre d’hélices sur une voiture ou le nombre de pneus sur un bateau.
Cependant, afin de conserver toutes les données pertinentes, vous aurez besoin de ces points de données pour
leurs types de véhicules appropriés. Afin de créer une base de données efficace, vous auriez besoin de plusieurs
types de données différents pour chaque véhicule, dont certaines seraient communes et d’autres différentes.
C'est exactement ce que nous faisons dans l'exemple de programme sur votre moniteur.
#define AUTO 1
#define BATEAU 2
#define AVION 3
#define NAVIRE 4
ailes intérieures;
portes intérieures;
};
typedef struct { /* structure pour un bateau ou un navire */
déplacement int ;
longueur du caractère ;
} BATEAUDEF;
structure {
véhicule de char ; /* quel type de véhicule */
poids entier ; /* /* poids brut du véhicule */
union { /* données dépendantes du type */
partie 1 de la voiture automobile union struct ; */ */
Bateau BOATDEF ; /* partie 2 du syndicat
structure {
moteurs de charbon de bois ;
envergure int;
} avion ; /* partie 3 du syndicat /* partie 4 du */
Navire syndicat */
BOATDEF ; } Type de véhicule;
du propriétaire */ /* valeur du véhicule en dollars valeur int ; /* le nom */
propriétaire de char[32]; }
gué, sun_fish, piper_cub; /* trois structures variables */
/* Définir quelques champs à titre d'illustration */
ford.véhicule = AUTO ;
gué.poids = 2742 ; /* avec un réservoir d'essence plein */
ford.vehicle_type.car.tires = 5 ; /* y compris les pièces de rechange */
ford.vehicle_type.car.doors = 2;
sun_fish.value = 3742 ; /* bandeannonce non incluse */
sun_fish.vehicle_type.boat.length = 20 ;
piper_cub.vehicle = AVION ;
piper_cub.vehicle_type.airplane.wingspan = 27 ;
if (ford.vehicle == AUTO) /* ce dont il s'agit dans ce cas */
printf("?La Ford a %d pneus./n",ford.vechicle_type.car.tires);
if (piper_cub.vehicle == AUTO) /* ce qui n'est pas le cas dans ce cas */
printf("L'avion a %d pneus.\n",piper_cub.vechicle_type.
pneus de voiture);
}
Dans ce programme, nous définirons une structure complète, puis déciderons lequel des différents types peut
entrezy. Nous commencerons par le haut et descendrons. Tout d'abord, nous définissons quelques constantes
avec le #defines, et démarrez le programme luimême. On définit une structure nommée "automobile"
contenant plusieurs champs que vous ne devriez avoir aucun mal à reconnaître, mais nous ne définissons aucun
variables à ce moment.
J'espère qu'il est évident pour vous que les quatre auraient pu être définies de l'une des trois manières présentées, mais
les trois méthodes différentes ont été utilisées pour vous montrer que n'importe laquelle pouvait être utilisée. En pratique,
la définition la plus claire aurait probablement été obtenue en utilisant le "typedef" pour chacune des parties.
Le syndicat n'est pas utilisé trop fréquemment, et presque jamais par les programmeurs débutants. Vous le rencontrerez
de temps en temps, cela vaut donc la peine de savoir au moins de quoi il s’agit. Vous n’avez pas besoin d’en connaître
les détails pour le moment, alors ne passez pas trop de temps à l’étudier. Lorsque vous avez besoin d’une structure
variable, d’un syndicat, vous pouvez l’apprendre à ce momentlà. Cependant, pour votre propre bénéfice, ne négligez
pas la structure. Vous devriez utiliser la structure souvent.
Un être humain a 2 jambes et 2 bras. Un chien a 4 pattes et 0 bras. Un téléviseur a 4 jambes et 0 bras. Une chaise a 4
pieds et 2 bras. etc.
animal1 = animal3; /* pet1 pointe maintenant vers la même structure que pet3 pointe /*
cela libère une */
gratuit(animal3); structure /* cela libère une structure */
gratuit(animal2); supplémentaire */
/* gratuit(animal1); cela ne peut pas être fait, voir l'explication dans le texte } */
Nous commençons par définir une structure nommée « animal » avec quelques champs relatifs aux chiens. Nous ne
définissons aucune variable de ce type, seulement trois pointeurs. Si vous effectuez une recherche dans le reste du
programme, vous ne trouverez aucune variable définie, nous n'avons donc rien dans lequel stocker les données. Tout
ce avec quoi nous devons travailler, ce sont trois pointeurs, chacun pointant vers la structure définie. Pour faire quoi
que ce soit, nous avons besoin de certaines variables, nous allons donc en créer dynamiquement.
Une limitation imposée aux utilisateurs par de nombreux compilateurs pour IBMPC et compatibles est une limite de 64 Ko
pour le code exécutable. En effet, l'IBMPC utilise un microprocesseur avec une taille de segment de 64 Ko et nécessite
des appels spéciaux pour utiliser des données en dehors d'un seul segment. Afin de garder le programme petit et efficace,
ces appels ne sont pas utilisés et leur taille est limitée mais reste adéquate pour la plupart des programmes. Cette limitation
ne s'applique pas aux utilisateurs du système d'exploitation 1616/OS, car le Motorola 68000 dispose d'un espace mémoire
plat de 16 Mo.
Un tas est une zone à laquelle le programme peut accéder pour stocker des données et des variables. Les données et les
variables sont placées sur le « tas » par le système au fur et à mesure que les appels à « malloc » sont effectués. Le
système garde une trace de l'endroit où les données sont stockées. Les données et les variables peuvent être libérées à
volonté, ce qui entraîne des trous dans le tas. Le système sait où se trouvent les trous et les utilisera pour un stockage de
données supplémentaire à mesure que d'autres appels "malloc" seront effectués. La structure du tas est donc une entité
très dynamique, en constante évolution. Reportezvous au manuel du programmeur 1616/OS pour plus de détails sur
l'allocation de mémoire dans le 1616.
Certains des compilateurs les plus chers offrent à l'utilisateur un choix de modèles de mémoire à utiliser. Les exemples
sont Lattice et Microsoft, qui permettent au programmeur de choisir d'utiliser un modèle avec une limitation de 64 Ko sur la
taille du programme mais une exécution plus efficace, ou d'utiliser un modèle avec une limitation de 640 Ko et nécessitant
des appels d'adresse plus longs conduisant à un adressage moins efficace. L'utilisation d'un espace d'adressage plus
grand nécessite un adressage intersegments, ce qui entraîne un temps d'exécution légèrement plus lent. Le temps est
probablement insignifiant dans la plupart des programmes, mais il y a d'autres considérations à prendre en compte.
Si un programme MSDOS n'utilise pas plus de 64 Ko pour le total de son code et de sa mémoire et s'il n'utilise pas de pile,
il peut être transformé en fichier .com. Puisqu'un fichier .com est déjà au format image mémoire, il peut être chargé très
rapidement alors qu'un fichier au format .exe doit voir ses adresses déplacées au fur et à mesure de son chargement. Par
conséquent, un petit modèle de mémoire peut générer un programme qui se charge plus rapidement qu'un programme
généré avec un modèle de mémoire plus grand. Ne vous laissez pas inquiéter, c'est un point délicat qui préoccupe peu de
programmeurs.
La nécessité de rester dans le modèle de petite mémoire est encore plus importante que la nécessité de rester dans
le cadre de l'ordinateur. Si vous aviez un programme qui utilisait plusieurs grandes zones de stockage de données,
mais pas en même temps, vous pouvez charger un bloc en le stockant dynamiquement, puis vous en débarrasser et
réutiliser l'espace pour le prochain grand bloc de données. Le stockage dynamique de chaque bloc de données
successivement et l'utilisation du même stockage pour chaque bloc peuvent vous permettre d'exécuter l'intégralité de
votre programme sur l'ordinateur sans le diviser en programmes plus petits.
Nous avons toujours une construction amusante au début de l'appel de fonction "malloc". C'est ce qu'on appelle un
"casting". La fonction "malloc" renvoie un bloc avec le pointeur pointant vers lui étant un pointeur de type "char" par
défaut. Très souvent, sinon la plupart du temps, vous ne souhaitez pas un pointeur vers une variable de type "char",
mais vers un autre type. Vous pouvez définir le type de pointeur avec la construction donnée sur la ligne d'exemple.
Dans ce cas, nous voulons que le pointeur pointe vers une structure de type "animal", nous le disons donc au
compilateur avec cette construction étrange. Même si vous omettez le cast, la plupart des compilateurs renverront un
pointeur correctement, vous donneront un avertissement et continueront à produire un programme fonctionnel. Il est
préférable de fournir au compilateur le cast pour éviter de recevoir le message d'avertissement.
Dans l’instruction suivante, nous attribuons également la valeur de « pet1 » à « pet2 ». Cela ne crée aucune nouvelle
donnée, nous avons simplement deux pointeurs vers le même objet. Puisque "pet2" pointe vers la structure que nous
avons créée cidessus, "pet1" peut être réutilisé pour obtenir une autre structure allouée dynamiquement, ce que
nous faisons ensuite. Gardez à l'esprit que "pet2" aurait tout aussi bien pu être utilisé pour la nouvelle allocation. La
nouvelle structure est remplie de données idiotes à titre d'illustration.
Enfin, nous allouons un autre bloc sur le tas à l'aide du pointeur "pet3", et remplissons son bloc de données illustratives.
L'impression des données ne devrait vous poser aucun problème puisqu'il n'y a rien de nouveau dans les trois
instructions d'impression. Il vous reste à étudier.
Afin d'illustrer un autre aspect de l'allocation et de la désallocation dynamiques des données, une étape supplémentaire
est incluse dans le programme sur votre moniteur. Le pointeur "pet1" reçoit la valeur "pet3". En faisant cela, le bloc vers
lequel "pet1" pointait est effectivement perdu puisqu'aucun pointeur ne pointe maintenant vers ce bloc. Il ne pourra donc
plus jamais être consulté, modifié ou éliminé. Cette mémoire, qui constitue un bloc sur le tas, est désormais gaspillée. Ce
n’est pas quelque chose que vous feriez délibérément dans un programme. Ceci n’est fait ici qu’à titre d’illustration.
Le premier appel de fonction « gratuit » supprime le bloc de données vers lequel « pet1 » et « pet3 » pointaient, et le
deuxième appel « gratuit » supprime le bloc de données vers lequel « pet2 » pointait. Nous avons donc perdu l’accès à
toutes nos données générées précédemment. Il y a toujours un bloc de données sur le tas, mais il n'y a aucun pointeur
vers celuici puisque nous en avons perdu l'adresse. Essayer de "libérer" les données pointées par "pet1" entraînerait une
erreur car elles ont déjà été "libérées" par l'utilisation de "pet3". Il n'y a pas lieu de s'inquiéter, lorsque nous reviendrons au
système d'exploitation, l'intégralité du tas sera éliminé sans égard à ce que nous y avons mis. Il faut souligner que la perte
d'un pointeur vers un bloc du tas supprime définitivement ce bloc de stockage de données de notre programme et nous
pourrions avoir besoin de ce stockage plus tard.
Compilez et exécutez le programme pour voir s'il fait ce que vous pensez qu'il devrait faire sur la base de cette discussion.
Il a fallu près de quatre pages pour parcourir la discussion du dernier programme, mais ce fut du temps bien dépensé.
Cela devrait être quelque peu passionnant pour vous de savoir qu'il n'y a rien d'autre à apprendre sur l'allocation
dynamique, les quatre dernières pages ont tout couvert. Bien sûr, il y a beaucoup à apprendre sur la technique d'utilisation
de l'allocation dynamique, et pour cette raison, il y a deux autres fichiers à étudier.
Mais il n’en demeure pas moins qu’il n’y a rien de plus à apprendre sur l’allocation dynamique que ce qui a été exposé
jusqu’à présent dans ce chapitre.
Le "*pet[12]" est nouveau pour vous donc quelques mots s'imposent. Ce que nous avons défini est un
tableau de 12 pointeurs, le premier étant "pet[0]" et le dernier "pet[11]". En fait, puisqu'un tableau est
luimême un pointeur, le nom « animal de compagnie » en luimême est un pointeur vers un pointeur. Ceci est valable en C, et en fait
vous pouvez aller plus loin si nécessaire mais vous serez vite confus. Je ne connais aucune limite quant à la façon dont
de nombreux niveaux de pointage sont possibles, donc une définition telle que "int ****pt" est légale comme pointeur vers
un pointeur vers un pointeur vers un pointeur vers une variable de type entier, si j'ai bien compté. Une telle utilisation est
découragé jusqu'à ce que vous acquériez une expérience considérable.
Maintenant que nous disposons de 12 pointeurs qui peuvent être utilisés comme n’importe quel autre pointeur, il est simple de
écrire une boucle pour allouer dynamiquement un bloc de données à chacun et remplir les champs respectifs avec
toutes les données souhaitables. Dans ce cas, les champs sont remplis de données simples à titre indicatif,
mais nous pourrions lire dans une base de données, des lectures provenant d'un équipement de test ou de toute autre source
de données.
Quelques champs sont choisis au hasard pour recevoir d'autres données afin d'illustrer que des affectations simples peuvent
être utilisé et les données sont imprimées sur le moniteur. Le pointeur "point" est utilisé dans l'impression
boucle uniquement pour servir d'illustration, les données auraient pu être facilement imprimées en utilisant le "pet[n]"
moyen de définition. Enfin, les 12 blocs de données sont libérés avant de terminer le programme.
Compilez et exécutez ce programme pour vous aider à comprendre cette technique. Comme indiqué précédemment, il y a
Il n'y avait rien de nouveau ici en matière d'allocation dynamique, seulement en ce qui concerne un tableau de pointeurs.
principal( )
{
structurer animal {
nom du personnage[25]; /* Le nom de l'animal */
race d'omble /* Le type d'animal */
chevalier[25] ; /* Les animaux vieillissent */
âge entier ; struct animal /* un pointeur vers un autre enregistrement de ce type */
*suivant ; } *point, *début, *avant; indice /* ceci définit 3 pointeurs, aucune variable */
int ;
/* le premier enregistrement est toujours un cas particulier */
Afin de vous rassurer, pensez à la liste chaînée que vous avez utilisée lorsque vous étiez enfant. Ton
ma sœur t'a offert ton cadeau d'anniversaire, et quand tu l'as ouvert, tu as trouvé une note qui disait : "Regarde
dans le placard du couloir. » Vous êtes allé dans le placard du couloir et avez trouvé une autre note qui disait : « Regardez derrière
le téléviseur. » Derrière le téléviseur, vous avez trouvé une autre note qui disait : « Regardez sous la cafetière. » Vous
avez continué cette recherche, et finalement vous avez trouvé votre paire de chaussettes sous la gamelle du chien.
En réalité, vous avez exécuté une liste chaînée, le point de départ étant le cadeau emballé.
et le point final étant sous la gamelle du chien. La liste s'est terminée au plat à manger pour chiens
puisqu'il n'y avait plus de notes.
Dans le programme dynlink.c, nous ferons la même chose que votre sœur vous a forcé à faire.
Nous le ferons cependant beaucoup plus rapidement et nous laisserons un petit tas de données à chacun des étapes intermédiaires.
points en cours de route. Nous aurons également la possibilité de revenir au début et de revenir en arrière
la liste entière encore et encore si nous le désirons.
Nous définissons trois pointeurs vers cette structure à utiliser dans le programme, et un entier à utiliser comme
un comptoir, et nous sommes prêts à commencer à utiliser la structure définie dans le but que nous souhaitons.
Dans ce cas, nous générerons à nouveau des données absurdes à des fins d'illustration.
A l'aide de la fonction "malloc", nous demandons un bloc de stockage sur le "heap" et le remplissons de données.
Le champ supplémentaire dans cet exemple, le pointeur, se voit attribuer la valeur NULL, qui est uniquement utilisée
pour indiquer qu'il s'agit de la fin de la liste. Nous laisserons le pointeur "start" sur cette structure, afin qu'il pointe
toujours vers la première structure de la liste. Nous attribuons également à "prior" la valeur de "start" pour des
raisons que nous verrons bientôt. Gardez à l’esprit que les points finaux d’une liste chaînée devront toujours être
traités différemment de ceux situés au milieu d’une liste. Nous n'avons maintenant qu'un seul élément de notre liste
et il est rempli de données représentatives.
Lorsque nous aurons parcouru 6 fois la boucle "for", nous aurons une liste de 7 structures dont celle que nous avons
générée avant la boucle. La liste aura les caractéristiques suivantes. "start" pointe vers la première structure
1. de la liste.
2. Chaque structure contient un pointeur vers la structure suivante.
3. La dernière structure a un pointeur qui pointe vers NULL et peut être utilisée pour détecter la fin comme
indiqué cidessous.
Il devrait être clair pour vous, si vous comprenez la structure cidessus, qu'il n'est pas possible de simplement sauter
au milieu de la structure et de modifier quelques valeurs. La seule façon d'accéder à la troisième structure est de
commencer par le début et de parcourir la structure, un enregistrement à la fois. Bien que cela puisse sembler un
prix élevé à payer pour la commodité de placer autant de données en dehors de la zone du programme, il s'agit en
réalité d'un très bon moyen de stocker certains types de données.
Un traitement de texte serait une bonne application pour ce type de structure de données car vous n'auriez jamais
besoin d'un accès aléatoire aux données. Dans la pratique, il s'agit du type de stockage de base utilisé pour le texte
dans un traitement de texte avec une ligne de texte par enregistrement. En fait, un programme quel que soit son
degré de sophistication utiliserait une liste doublement chaînée. Il s'agirait d'une liste avec deux pointeurs par
enregistrement, l'un pointant vers l'enregistrement suivant et l'autre pointant vers l'enregistrement juste avant celui
en question. L'utilisation de ce type de structure d'enregistrement permettrait de parcourir les données dans les deux
sens.
Des livres entiers sont écrits décrivant différents types de listes chaînées et comment les utiliser, donc aucun détail
supplémentaire ne sera donné. La quantité de détails fournis devrait être suffisante pour une première compréhension
du C et de ses capacités.
mix_up_the_chars(ligne);
}
} while (c != NULL);
fclose(fp);
}
autre {
if (islower(line[index])) /* 1 si minuscule line[index] = toupper(line[index]); */
}
}
printf("%s",ligne);
}
\0 NULL (zéro)
En faisant précéder chacun des caractères cidessus du caractère barre oblique inverse, le caractère peut être
inclus dans une ligne de texte pour affichage ou impression. De la même manière qu'il est parfaitement normal de
utiliser la lettre « n » dans une ligne de texte comme partie du nom de quelqu'un et comme fin de ligne, l'autre
les caractères peuvent être utilisés comme parties de texte ou pour leurs fonctions particulières.
Le programme sur votre écran utilise les fonctions qui peuvent déterminer la classe d'un personnage, et
compte les personnages de chaque classe. Le numéro de chaque classe est affiché avec la ligne
luimême. Les trois fonctions sont les suivantes :
#include "/sys/stdio.h"
#include "/sys/ctype.h" /*notez que votre compilateur n'en a peutêtre pas besoin */
principal( )
{
FICHIER *fp;
ligne de caractères [80], nom de fichier [24] ;
char *c;
fclose(fp);
}
count_the_data(ligne)
ligne de caractères[];
{
int blancs, caractères, chiffres ;
indice int ;
printf("%3d%3d%3d %s",blancs,caractères,chiffres,ligne);
}
{ masque de
caractères ; numéro de
caractère.6, ; char et,ou,xor,inv,index;
numéro[0] = 0X00 ; numéro[1]
= 0X11 ; numéro[2] = 0X22 ;
numéro[3] = 0X44 ; numéro[4]
= 0X88 ; numéro[5] = 0Xff ;
masque,et,ou,xor,inv);
}
printf("\n"); masque
= 0X22 ; pour (index
= 0;index <= 5;index++) { et = masque & nombre[index]; ou
= masque l numéro[index]; xor = masque ^
numéro[index]; inv = ~numéro[index];
printf("%5x %5x %5x %5x %5x
%5x\n",numéro[index],
masque,et,ou,xor,inv);
}
}
Les fonctions de ce groupe de fonctions sont utilisées pour effectuer des opérations au niveau des bits, ce qui
signifie que les opérations sont effectuées sur les bits comme s'il s'agissait de bits individuels. Aucun report de bit à
bit n'est effectué comme cela serait le cas avec une addition binaire. Même si les opérations sont effectuées sur un
seul bit, un octet entier ou une variable entière peut être utilisé dans une seule instruction. Les opérateurs et les
opérations qu'ils effectuent sont indiqués dans le tableau suivant ; ET logique, si les deux bits sont 1, le résultat
& est 1.
L'exemple de programme utilise plusieurs champs qui sont combinés de chacune des manières indiquées cidessus.
Les données sont au format hexadécimal. On supposera que vous connaissez déjà le format hexadécimal si vous
devez utiliser ces opérations. Si ce n’est pas le cas, vous devrez l’étudier vousmême. L'enseignement du format
hexadécimal des nombres dépasse le cadre de ce didacticiel.
Les deux dernières opérations abordées dans ce chapitre sont les instructions de décalage à gauche et de
décalage à droite. Chargez l'exemple de programme shifter.c pour un exemple en utilisant ces deux instructions.
Les deux opérations utilisent les opérateurs suivants : Décalage à gauche
<< n de n places.
>> n Décalage à droite de n places.
main( )
printf("\n"); compte
= 2 ; petit = 1 ;
grand =
0x4000 ; for(index =
0;index < 9;index++) { printf("%8d %8x
%8x\n",small,small,big); petit = petit << nombre ; grand = grand
>> compte ;
}
}
Là encore les opérations sont effectuées et affichées au format hexadécimal. Le programme doit être simple à
comprendre par vousmême, il n'y a pas de code compliqué.
DOSEX était destiné à illustrer comment effectuer des appels système MSDOS et vous apprendra, grâce à l'autoapprentissage,
comment le système répond au clavier. DOSEX a été modifié par Tim Ward pour utiliser les appels système Applix 1616 au lieu
des appels MSDOS.
WHATNEXT lit les commandes saisies sur la ligne de commande et vous aidera à configurer un fichier de commandes variable,
un fichier qui demande une entrée d'opérateur et répond à l'entrée en se connectant à une partie différente du fichier de
commandes.
LIST est le code source du programme que vous avez utilisé pour imprimer les fichiers sources C lorsque vous avez commencé
à étudier le C à l'aide de ce didacticiel. Enfin, nous arrivons à VC, la Calculatrice Visuelle, que vous devriez trouver comme un
programme utile même si vous n'étudiez pas son code source. VC utilise la plupart des techniques de programmation que nous
avons étudiées dans ce cours et quelquesunes que nous n'avons même jamais mentionnées, comme les sousprogrammes
compilés séparément.
Nous examinerons les exemples de programmes un par un, mais sans explication complète d'aucun d'entre eux, car vous étudiez
le C depuis un certain temps maintenant et devriez être capable de lire et de comprendre la plupart de ces programmes par vous
même. Une autre chose doit être mentionnée : ces programmes utilisent de nombreuses constructions non standard et vous
devrez probablement en modifier certaines pour les faire compiler avec votre compilateur particulier. Cela vous sera laissé en
exercice. dosexibm.c L'exemple de programme MSDOS La copie de MSDOS que vous avez reçue avec votre IBMPC ou
vous pouvez utiliser en tant que programmeur pour contrôler vos périphériques et lire des informations ou des états. d'eux.
Certains des manuels IBM DOS antérieurs, MSDOS 2.0 et versions antérieures, répertorient ces appels à la fin du manuel ainsi
que la manière de les utiliser. La plupart des manuels fournis avec les ordinateurs compatibles ne font aucune mention de ces
appels même s'ils sont extrêmement utiles. Ces appels sont accessibles à partir de presque tous les langages de programmation,
mais ils nécessitent une étude initiale pour apprendre à les utiliser. Ce programme est destiné à vous aider dans cette étude.
Affichez le programme sur votre moniteur ou imprimezle pour référence. Il s'agit simplement d'une boucle guettant une saisie au
clavier ou un changement de temps. Si l’un ou l’autre se produit, il réagit en conséquence. À la ligne 23, la fonction "kbhit()"
renvoie une valeur de 1 si une touche a été appuyée mais pas encore lue dans le tampon d'entrée par le programme. Il s'agit
d'une fonction non standard et peut nécessiter un changement de nom pour votre compilateur particulier. Il y aura probablement
plusieurs appels similaires qui devront être modifiés pour votre compilateur afin de compiler et exécuter les programmes du
chapitre 14.
Regardez la fonction nommée "get_time" pour un exemple d'appel MSDOS. Une interruption 21 (hex) est appelée après avoir
réglé le registre AH sur 2C (hex) = 44 (décimal). L'heure est renvoyée dans les registres CH, CL et DH. Reportezvous aux
définitions d'appel MSDOS dans votre copie de MSDOS (si disponible). Si les définitions n'y figurent pas, le livre de Peter Norton,
"Programmers Guide
Une autre fonction utile est la fonction "pos_cursor()" qui positionne le curseur n'importe où sur le
moniteur que vous désirez en utilisant une interruption MSDOS. Cet appel a été modifié dans l'Applix
Version 1616, pour utiliser un appel système équivalent. Dans le cas MSDOS, l'interruption utilisée est 10(hex)
qui est l'interruption générale du moniteur. Ce service particulier est le numéro 2 sur environ 10 différents
services de surveillance disponibles. Cette fonction particulière n'est peutêtre pas nécessaire à votre compilateur car
certains compilateurs ont une fonction de positionnement du curseur prédéfinie pour votre usage. Cette fonction est
inclus ici comme un autre exemple pour vous.
La fonction suivante, le service numéro 6 de l'interruption 10 (hex) est le service de défilement de fenêtre. Cela devrait
être explicite. Le service de défilement de fenêtre a été supprimé de la version Applix. Complet
les détails de la prise en charge des fenêtres de Conal Walsh sont fournis dans les didacticiels commençant dans
µProblème périphérique 10.
Dans ce programme, le curseur est positionné et certaines données sont sorties sur le moniteur, puis le curseur
est "caché" en le déplaçant vers la ligne 26 qui n'est pas affichée. Après avoir compilé et exécuté le
programme, vous remarquerez que le curseur n’est pas visible sur le moniteur. Ceci est possible dans n'importe quel
programme, mais assurezvous de mettre le curseur en vue avant de revenir au DOS car le DOS ne le fait pas.
j'aime avoir un curseur "caché" et peut faire des choses étranges.
Un peu de temps passé à étudier ce programme vous sera précieux car il vous dévoilera comment le clavier
les données sont entrées dans l’ordinateur. Il est particulièrement important de savoir comment les touches spéciales telles que la fonction
les touches, les flèches, etc. sont gérées.
/* ************************************************* **********************/
/* Ceci est un exemple de programme pour illustrer comment ; */
/* 1. Obtenez l'heure et la date d'APPLIX 1616/OS à l'aide d'un appel système */
/* 2. Placez le curseur sur n'importe quelle position sur l'écran à l'aide d'une vidéo 1616/OS*/
/* code d'échappement du pilote */
/* 3. Lire les caractères du clavier et afficher leurs codes */
/* 4. Comment faire défiler une fenêtre vers le haut sur le moniteur */
/* 5. Formater un programme pour faciliter la lecture et la compréhension */
/* ************************************************* **********************/
#include "/sys/stdio.h"
#include "/sys/syscalls.h"
#include "/sys/ctype.h"
principal( )
{
caractère int, x ;
x=2;
draw_box(); /* dessine les cases autour des champs */
pos_cursor(33,19);
printf("Tapez Q pour quitter");
faire {
if (kbhit()) { /* faire défiler /* une touche atelle été appuyée */
si (x <= 16) la sortie dans la fenêtre vers le bas a */
{ pos_cursor(9,x+1); /* doubler. ++x; */
}
sinon si
(x <= 31){
pos_cursor(50,x14); /* retourne en haut de la fenêtre à droite */
++x; /* côté. */
}
autre {
x=2; /* retour en haut de la fenêtre à gauche */
pos_curseur(9,x+1); /* côté. */
}
caractère = getch(); /* je le lis */
printf("%s",ligne);
ligne[8] = 201 ; pour /* dessine la ligne supérieure de la boîte */
(index = 9;index < 70;index++)
ligne[index] = 205 ;
ligne[70] = 187 ;
printf("%s",ligne);
de boîte[8] = 186; /* dessine les côtés d'une grande ligne */
pour (index = 9;index < 70;index++)
ligne[index] = ligne[70] ' ';
= 186;
pour (index = 0;index <15;index++)
printf("%s",ligne);
ligne[8] = 204 ; pour /* trace une ligne entre les cases */
(index = 9;index < 70;index++)
ligne[index] = 205 ;
ligne[70] = 185 ;
printf("%s",ligne);
ligne[8] = 186 ; pour /* côtés de la boîte heure/date */
(index = 9;index < 70;index++)
ligne[index] = ligne[70] ' ';
= 186;
printf("%s",ligne);
ligne[8] = 200 ; pour /* ligne du bas de la boîte */
(index = 9;index < 70;index++)
ligne[index] = 205 ;
ligne[70] = 188 ;
printf("%s",ligne);
pour (index = 0;index < 80;index++) ligne[index] = pour /* trois lignes vides */
(index = 0;index < ' ';
3;index++)
printf("%s",ligne);
}
Tout d'abord, la question sur la ligne de commande, composée d'un certain nombre de mots, est affichée sur l'écran.
moniteur et le programme attend que l'opérateur appuie sur une touche. Si la touche frappée est l'une de celles du
dernier "mot" du groupe de mots sur la ligne de commande, le numéro du caractère dans le
Le groupe est renvoyé au programme où il peut être testé avec la commande "errorlevel" dans le
fichier batch. Vous pouvez utiliser cette technique pour créer un fichier de variable AUTOEXEC.BAT ou tout autre
Le fichier batch peut l'utiliser pour une branche à plusieurs voies. Compilez et exécutez ce fichier avec TEST.BAT pour
un exemple de la façon dont cela fonctionne dans la pratique. Vous trouverez peutêtre cette technique utile dans l'un de vos lots
fichiers et vous aurez presque certainement besoin de lire les paramètres de ligne de commande un jour. Depuis
/* affiche tout sauf le dernier sur le moniteur. Le dernier est une série de */
/* Caractères utilisés comme comparaisons d'entrée. Un caractère est */
/* lecture depuis le clavier. S'il s'agit d'un des caractères du */
/* liste de comparaison, son numéro est renvoyé au DOS comme niveau d'erreur */
/* commande. Si le caractère n'existe pas dans la liste, un zéro est
*/
/* revenu. L'exemple suit : /* /* WHATNEXT Quel */
*/
modèle souhaitezvous ? ALR%3T /* /* /* /* /* /* /* /* /* La question doit */
Si la touche a ou A est enfoncée, le niveau d'erreur 1 est renvoyé. */
Si la touche l ou L est enfoncée, le niveau d'erreur 2 est renvoyé. */
Si la touche r ou R est enfoncée, le niveau d'erreur 3 est renvoyé. */
Si la clé % est atteinte, le niveau d'erreur 4 est renvoyé. */
Si la touche 3 est activée, le niveau d'erreur 5 est renvoyé. */
Si la touche t ou T est enfoncée, le niveau d'erreur 6 est renvoyé. */
Si une autre touche est atteinte, le niveau d'erreur 0 est renvoyé. */
*/
être sur une seule ligne. */
/* Jusqu'à neuf clés différentes peuvent être utilisées. */
/* Le niveau d'erreur peut être interprété dans un fichier batch. */
/* ************************************************* ************** */
#include "/sys/stdio.h"
#include "/sys/ctype.h"
principal(numéro,nom)
numéro entier ; /* nombre total de mots sur la ligne de commande */
char *nom[];
{
indice int ; int c; /* un compteur et une variable incrémentale */
code /* le caractère lu pour comparaison */
entier ; char /* le niveau d'erreur résultant est renvoyé à */
next_char; char *point; /* utilisé pour la boucle de comparaison */
/* un pointeur factice utilisé pour plus de commodité */
} while (*(point + index)); /* renvoie le code /* jusqu'à ce que le terminateur NULL soit trouvé */
quitter(code); d'erreur au système */
}
/* ************************************************* ******************* */
/* Ce programme lira n'importe quel fichier et le listera sur le moniteur avec */
/* les numéros de ligne et les numéros de page. */
/* ************************************************* ******************* */
#include "/sys/stdio.h" /* fichier d'entête d'E/S standard */
#define MAXCHARS 255 /* taille maximale d'une ligne */
FICHIER *point_fichier ; /* pointe vers le fichier à lire */
FICHIER *print_file_point; externe /* pointeur vers le pronter */
top_of_page();
char oneline[256]; /* zone tampon de chaîne d'entrée */
principal(numéro,nom)
numéro entier ; /* nombre d'arguments sur la ligne de commande */
char *nom[]; { /* arguments sur la ligne de commande */
faire {
if (file_point == NULL) { printf("Entrez le /* pas encore de nom de fichier */
nom du fichier > ");
scanf("%s",nom de fichier);
file_point = fopen(nom de fichier,"r"); /* NULL si le /* fichier ouvert */
if (file_point == NULL) printf("Le nom fichier n'existe pas */
de fichier n'existe pas, réessayez.\n");
}
} while (file_point == NULL); /* continue jusqu'à ce que le nom de fichier soit correct */
}
entête();
printf("%5d %s",numéro_ligne,oneline);
/* Ceci imprime une ligne de moins de 72 caractères */
if (strlen(oneline) < 72)
fprintf(print_file_point,"%5d %s",line_number,oneline);
/* Ceci imprime une ligne de 72 à 143 caractères */ else if (strlen(oneline) <
144) {
fprintf(print_file_point,"%5d ",line_number); pour (index = 0;index < 72;index+
+)
fprintf(print_file_point,"%c",oneline[index]); fprintf(print_file_point,"<\n "); pour
(index = 72;index < strlen(oneline);index++)
fprintf(print_file_point,"%c",oneline[index]); lignes_this_page++;
}
/* Ceci imprime une ligne de 144 à 235 caractères */
sinon si (strlen(oneline) < 235) {
fprintf(print_file_point,"%5d ",line_number); pour (index = 0;index < 72;index+
+)
fprintf(print_file_point,"%c",oneline[index]); fprintf(print_file_point,"<\n "); pour
(index = 72;index < 144;index++)
fprintf(print_file_point,"%c",oneline[index]);
fprintf(print_file_point,"<\n "); for (index = 144;index < strlen(oneline);index+
+) fprintf(print_file_point,"%c",oneline[index]); lignes_this_page
+= 2;
}
/* la ligne suivante génère une nouvelle ligne s'il n'y en a pas à la fin de la dernière ligne */ if
(oneline[strlen(oneline)1] != '\n')
fprintf(print_file_point,"%c",'\ n'); numéro_ligne++; lignes_this_page+
+;
/* ************************************************* ********* entête */ /* Cette routine vérifie si un entête doit être imprimé. Elle
vérifie également */ /* la fin d'une page et espace le papier. */ /* ********************************************** ********************
*/ entête() { int index;
/* voir d'abord si nous sommes en bas de la page */ /* espacer le papier à partir du bas */
lines_this_page;index < 61;index++) if (lines_this_page > MAXLINES) { for (index =
fprintf(print_file_point,"\n "); lignes_this_page = 0 ; }
Il n'y a aucune raison pour que les variables n'aient pas pu être définies dans la partie listf.c du programme et déclarées
comme "externes" dans la partie list.c. Certaines variables auraient pu être définies dans l’un et d’autres dans l’autre. C'est
simplement une question de goût personnel. Poussé à l'extrême, l'ensemble des variables aurait pu être défini dans un
troisième fichier et nommé "extern" dans ces deux fichiers. Le troisième fichier serait alors compilé et inclus dans le
processus de liaison.
Il serait à votre avantage de compiler, lier et exécuter ce programme pour vous préparer au prochain programme composé
de 5 fichiers distincts qui doivent tous fonctionner ensemble.
La première chose à faire est de parcourir le didacticiel pour VC inclus dans le fichier VC.DOC.
Vous devez exécuter plusieurs dizaines d'étapes, chaque étape illustrant un aspect de la calculatrice visuelle. Vous aurez
une bonne idée de ce qu'il est capable de faire et rendrez votre étude du code source très rentable. De plus, vous trouverez
probablement de nombreuses façons d'utiliser la calculatrice visuelle pour résoudre des problèmes impliquant des calculs
pour lesquels la simplicité du problème ne justifie pas l'écriture d'un programme.
Notez que les définitions de structure, utilisées dans toutes les parties distinctes du programme, sont définies dans le
fichier STRUCT.DEF (renommé STRUCT.H dans la version Applix 1616). Vous devez inclure STRUCT.H avec les autres
fichiers d'entête Applix 1616. Lors du développement du programme, lorsqu'il devenait nécessaire de modifier légèrement
une des structures, il n'était pas nécessaire de la changer dans tous les fichiers, un seul fichier nécessitait une modification
qui était alors "inclus" dans les fichiers sources.
Notez que les données de transcription sont stockées dans une liste doublement chaînée, les données ellesmêmes étant
stockées dans une chaîne de caractères distincte allouée dynamiquement. Cette ligne est pointée par le pointeur "lineloc".
Pour faciliter le développement, les fonctions similaires ont été regroupées et compilées séparément.
Ainsi, toutes les fonctions impliquant le moniteur ont été incluses dans le fichier nommé video.c, et toutes les fonctions
impliquant le stockage de données ont été regroupées dans la collection file.c.
Diviser votre programme d'une manière similaire à celleci devrait simplifier le débogage et les modifications futures.
La fonction "monitor()" est particulièrement intéressante. Dans la version Applix 1616, cette fonction est supprimée, car le
1616 prend en charge les moniteurs couleur et monochromes sans changement. Dans le monde MSDOS, cette fonction
examine le mode vidéo à l'aide d'une commande DOS et s'il s'agit d'un 7, elle suppose qu'il s'agit d'un moniteur
monochrome, sinon elle suppose un moniteur couleur. Les couleurs des différents champs sont établies à ce moment et
utilisées tout au long du programme. La plupart des données sont écrites directement dans la mémoire vidéo, mais
certaines sont écrites via les routines BIOS MSDOS standard.
N'hésitez pas, après avoir compris ce code, à le modifier comme vous le souhaitez pour votre propre usage.
Pour compiler et lier ceci, copiez les cinq fichiers VC (data.c, file.c, vc.c,
video.c, struct.h) dans un répertoire et incluez ce répertoire dans votre XPath. Taper:
relcc v W1 lf vc.c data.c vidéo.c fichier.c
(Notez que l'option lf doit être incluse pour que le compilateur utilise la bibliothèque float, tandis que l'option lf doit être incluse pour que le compilateur utilise la bibliothèque float, tandis que l'option lf doit être incluse pour que le compilateur utilise la bibliothèque float, tandis que l'option
d'autres sont facultatifs.) Ou utilisez le makefile inclus sur le disque de distribution, si vous disposez du
rendre utilitaire.
#include "/sys/ctype.h"
#include "/sys/stdio.h"
#include "/f0/struct.h"
#include "/sys/syscalls.h"
struct vars allvars[12]; int varinuse = 0; /* c'est le stockage principal des variables */
char en ligne[60]; col int ; /* quelle variable est utilisée actuellement */
code d'erreur int ; int /* zone de la ligne d'entrée */
couleur; int /* utilisé pour rechercher dans l'entrée */
printit = 1; int /* numéro du code d'erreur /* */
ignorer; externe colonne où l'erreur s'est produite /* 1 = imprimer */
char strngout[]; lignes de une transcription /* 1 = ignorer les calculs */
structure *top, pour la ligne /* zone de message de sortie */
*bot, *q, *p, *arrow, *trnsend ; */
principal()
{
top = bot = q = p = flèche = trnsend = NULL ;
initfk(); /* configure les touches de fonction */
initdata(&sévérité[0]); bkgndvid(); /* initialise toutes les données */
valusvid(); /* afficher l'arrièreplan vidéo lignes doubles */
/* affiche les valeurs de départ de toutes les variables */
strtrans("Bienvenue dans la Calculatrice Visuelle Applix 1616 Version
1.00",0);transout();
faire {
cours(22,7);
printf(" entrée >
");
printf(" do ");
/* répéter jusqu'à ce qu'il n'y ait plus */
{ readline(); errdis(" d'erreur /* obtenir une ligne */
parse(); if de saisie "); /* effacer le message d'erreur */
(errcode) /* analyser la ligne */
casser;
cas 4 : if (index < 61) { if (inline[index] == 0); /* flèche droite */
inline[index] = index = index + 1; /* zéro trouvé */
' '; /* vide sur 0 */
/* curseur en avant */
}
casser;
cas 5 : movarrow(1); casser; /* flèche vers le haut */
inline[temp] = inline[temp+1];
putchar(inline[temp++]);
}
putchar(' '); casser; /* vide à la dernière place */
casser;
cas 60 : aide(); transout(); /* F2 Système d'aide */
casser;
cas 61 :
casser; /* F3 Rechange */
cas 62 : /* F4 Marquer la transcription */
flèche>marqué = (flèche>marqué?0:1);
transout();
casser;
cas 63 : fileout(); casser; /* F5 Transcription du magasin */
}
par défaut : poscurs(15,5);
printf(" S%3d",spéc);
}
poscurs(ligne,col+index); /* met réellement le curseur en position */
}
putchar(c);
suivant = en ligne[index];
en ligne[index++] = c;
cours = indice ;
while((next != 0) && (curr <=60)) { /* déplacer le reste */
temp = suivant; /* ligne à droite */
suivant = inline[curr];
inline[curr++] = temp;
putchar(temp);
}
}
autre {
si ((c == 8) && index){ /* retour arrière */
varinus = 1 ;
code d'erreur = 0 ;
col = 0 ;
ignorer = 1 ; /* ignorer cette ligne */
if (inline[0] == '#') { getnames(); /* récupère la liste des noms de variables */
retour;
}
while (inline[col] == ' ') col++; if (inline[col] == '$') return; /* ignore les espaces de début */
if (inline[col] == 0) return; ignorer = 0 ; /* ignore une ligne de commentaire */
/* ignore une ligne vide */
/* n'ignore pas cette ligne */
nom[0] = inline[col++]; indice = 1 ; /* trouver le nom de la variable */
faire {
if (inline[col] == '(') parcol++;
if (inline[col++] == ')') parcol;
si (parcol < 0) errchk(1); /* le nombre de parents est devenu négatif */
}
while (inline[col]);
si (parcol)
errchk(2); col = /* reste une parenthèse */
indice ;
} /* ********************************************** **************** errout */ /* C'est la fonction qui affiche les messages d'erreur
clignotants sur le moniteur */ /*. Notez les erreurs supplémentaires pour l’expansion du tableau. */ erreur() {
} poscurs(22,12+colerr);
/* Cette fonction est utilisée par APPLIX 1616 pour initialiser les touches de fonction */ /* pour la Calculatrice Visuelle */
initfk() {
DEF_FK(0, fkey1);
DEF_FK( 1, fkey2);
DEF_FK(2, fkey3);
DEF_FK(3, fkey4);
DEF_FK(4, fkey5);
DEF_FK(5, fkey6);
DEF_FK(6, fkey7);
DEF_FK(7, fkey8);
DEF_FK(8, fkey9);
DEF_FK(9, fkey10);
}
/* ************************************************* ****************/
La calculatrice visuelle a été écrite pour être utilisée pour des calculs rapides de la variété qui serait
se fait généralement avec une calculatrice portative. Il n'y a aucune allocation pour les boucles de programmation,
ou des variables indirectes, ou toute autre fonctionnalité d'un langage de programmation moderne. Là
Il n'y a pas non plus de complications, et ce programme ne devrait pas nécessiter plus de quelques minutes pour
l'utilisateur expérimenté d'un ordinateur pour apprendre à l'utiliser, et seulement un peu plus longtemps pour la personne
inexpérimentée en informatique.
Il est suggéré de commencer par parcourir lentement le didacticiel, en effectuant les opérations suggérées,
puis lisez les commentaires suivants pour une description de la calculatrice visuelle. Ce programme est
destiné à être beaucoup plus complet que les petites calculatrices à l'écran qui sont devenues
populaire, mais il n'est pas résident en mémoire. En raison de la popularité future attendue de ces programmes
Comme "Windows", ce programme peut être aussi pratique que les programmes résidents actuels en mémoire.
4. Tapez B = SQRT(A) <retour> Vous trouverez la racine carrée de A affichée dans les deux
places à côté de la variable B. Vous avez peutêtre remarqué que le système ne se soucie pas de savoir si
vous utilisez des majuscules ou des minuscules, cela le force à être en majuscules. Vous avez maintenant défini certains
valeurs pour les variables A et B.
6. Appuyez sur la touche F6 puis <return> Le F6 demande qu'un fichier soit lu et si vous ne le faites pas
spécifiez un nom de fichier, litil dans le fichier nommé "HELP". Ce serait un bon endroit
pour stocker une liste de vos autres fichiers de la même manière que ce fichier.
7. Appuyez sur la touche F6 puis tapez AMORT <return> Ceci lit dans le fichier nommé "AMORT"
et calcule chaque ligne au fur et à mesure de sa lecture. Notez qu'il a également modifié les noms des
variables qu'il utilise pour les rendre plus significatives pour vous.
8. Tapez PRINC = 30000 <retour> Cela modifie le montant du prêt. Nous voudrions
pour recalculer le paiement que nous ferons dans les prochaines étapes.
9. Déplacez la flèche vers le haut jusqu'à la ligne commençant par "PMNT=..." en utilisant les flèches haut et bas.
clés. Lorsque la flèche pointe vers la ligne en question,...
dix. Appuyez sur la touche F9. Cela déplace la ligne pointée, par la petite flèche, dans la zone de saisie
où il peut être modifié ou réutilisé tel quel.
14. Tapez PAYMENT <return> Ce fichier sera lu et vous donnera les résultats de
votre prêt hypothécaire après le premier versement. Les résultats seront également imprimés.
15. Appuyez à nouveau sur la touche F6 et <retour> Le dernier fichier lu sera réutilisé à nouveau et le
le résultat du deuxième paiement sera affiché sur le moniteur et sur l’imprimante.
16. Répétez l'étape 15 trois ou quatre fois.
17. Appuyez sur la touche F1. Un écran d'aide apparaîtra décrivant les différentes fonctions mathématiques
disponible. Ils peuvent être imbriqués au niveau souhaité.
18. Appuyez sur la touche F2. Un écran d'aide apparaîtra avec une très brève description du système
fonctions disponibles.
19. Appuyez sur la touche "Accueil". Vous serez immédiatement transporté tout en haut du
transcription où le message de bienvenue a été initialement vu. Le Pgup, Pgdn, Accueil,
et Fin vous permettront de parcourir la fenêtre de transcription très rapidement.
20. Déplacez la petite flèche vers la ligne qui commence "# APRINC" et appuyez une fois sur la touche F4.
Vous verrez que l'astérisque apparaît devant la ligne. Cela « marquera » la ligne.
Continuer à appuyer sur la touche F4 activera et désactivera l'astérisque.
21. Déplacez la flèche vers la ligne qui commence "# EEQUITY" et marquez également cette ligne.
22. Appuyez sur la touche F5. Le système vous demande maintenant un nom de fichier vers lequel sortir.
23. Tapez STUFF <return> C'est simplement un nom de fichier. N'importe quel nom de fichier valide peut être utilisé.
Toutes les lignes de la zone de transcription qui sont « marquées » seront sorties dans le fichier « STUFF ».
24. Appuyez sur la touche F6 et tapez STUFF <return> Toutes les lignes qui viennent d'être sorties seront
être lu et tous les calculs seront effectués.
36. Tapez M = 0XFFFF <retour> La variable M est lue au format hexadécimal et affichée dans les trois formats dans
la zone supérieure, mais sous forme décimale dans la zone de transcription. L'affichage par défaut des entiers est
décimal.
37. Type I = SQRT(48) <retour> La racine carrée est calculée à l'aide de 15 chiffres significatifs et le résultat est
tronqué à la valeur immédiatement inférieure. Tous les calculs sont effectués de cette façon et le résultat est
tronqué à la valeur entière avant affichage.
38. Type A = FACT(170)/FACT(169) 170 <retour> Le très petit résultat vous indiquera une mesure de l'exactitude
des calculs. Il ne vous semble peutêtre pas évident que nous utilisons une fonction factorielle. Calculez la valeur
de FACT(170) pour avoir une idée de la plage dynamique disponible avec ce système.
VC.XREL Le fichier exécutable de la calculatrice visuelle (la version IBM est .EXE).
AIDE L'index des fichiers des utilisateurs.
Toutes les équations sont saisies dans la zone de saisie dans une expression mathématique normale. Seules les expressions à
valeur unique peuvent être évaluées, aucune équation simultanée ne peut être résolue avec ce système.
Pour élever « A » à la puissance « B », utilisez ; C = EXP(B*LOG(A)) $ toutes les variables peuvent être utilisées. Un
signe dollar n'importe où sur une ligne affiche le reste de cette ligne sous forme de commentaire uniquement.
L'imbrication est autorisée à n'importe quelle profondeur, mais l'expression entière doit tenir dans la fenêtre de saisie. Les
expressions plus longues doivent être décomposées en instructions plus petites.
Les variables « I » à « J » peuvent être mélangées avec les variables « A » à « F » de n'importe quelle manière.
Les variables « I » sont tronquées après évaluation et ne peuvent donc être utilisées que pour stocker des valeurs entières, mais
cela serait acceptable dans de nombreux cas, comme la valeur initiale du prêt dans l'exemple cidessus.
Afin de faciliter la lecture des équations, les noms des variables "A" à "F" peuvent être modifiés par n'importe quel nom de votre
choix comportant jusqu'à 6 caractères. Le premier doit être alphabétique et le reste peut être alphabétique ou numérique. Pour
modifier les noms, utilisez le signe # dans la première colonne de l'instruction et n'importe quel ordre de groupes de noms de
variables. Un groupe de variables est composé d'une variable
En mélange avec ce qui précède, ou placé sur leur propre ligne d'entrée, vous pouvez mettre autant de groupes "de base"
que vous le souhaitez pour les variables "I" à "N". Un groupe de base se compose du nom de la variable, d'un signe moins
et de l'une des lettres "D", "O", "H" ou "X".
Si, après avoir nommé les variables, vous souhaitez les renommer autrement, les noms d'origine sont utilisés pour les
nouveaux changements de nom. Ainsi, si "A" était nommé "PLACE" et que vous souhaitiez le renommer "WHERE", la
méthode appropriée serait d'utiliser "# AWHERE".
4. Limites
Cette version de la calculatrice visuelle a une limite quant au nombre de lignes dans la zone de transcription.
Il devrait y en avoir suffisamment pour la plupart des applications. Si vous avez besoin de plus, je vous suggère de changer
de programme.
La limite des nombres est d’environ dix puissances plus ou moins 308. Bien entendu, les nombres positifs et négatifs
peuvent être utilisés partout. La limite pour les variables « I » est d'environ 16 millions et ne peut être que nulle ou positive.
Le nombre exact en 2 à la puissance 24 moins 1. C'est le nombre affiché dans la variable "N" lorsque vous chargez le
système.
La plus grande limitation du système est la limite de votre propre créativité. C'est à vous de l'utiliser de manière productive
ou simplement de lui permettre de prendre la poussière comme tant de vos autres programmes.
Je pourrais ajouter que je possède également de nombreux dépoussiéreurs que je n’ai pas encore appris à utiliser.
Je n'ai aucune possibilité d'assistance téléphonique pour ce tutoriel et je n'ai pas l'intention d'en mettre en place.
Si vous rencontrez des problèmes ou si vous avez des suggestions, écrivezmoi à l'adresse cidessous.
Gordon Dodrill 30 juin 1986 Copyright
Ceci est un résumé des notes fournies par Andrew Morton d'Applix Pty Ltd, décrivant les modifications apportées au compilateur
HiTech C. La plupart des modifications apportées à la bibliothèque visent à garantir que le code de sortie coopère avec le
gestionnaire de mémoire.
18.1 Relcc.c
Il s'agit du cc HiTech original modifié par Colin McCormack et Andrew Morton afin qu'il produise directement des fichiers .xrel
relocalisables, au lieu de fichiers .exec à position fixe. L'indicateur r produira un fichier .exec. Cet indicateur doit être placé au
début de la ligne de commande relcc, car il y a une petite erreur dans le codage de l'indicateur.
Les fichiers temporaires du compilateur sont placés dans le répertoire /temp. Vous devez attribuer /temp ou un autre endroit sûr
chemins d'inclusion /hitech et /hitech/ avant d'utiliser relcc. Le compilateur a /rd ou assign /temp . a été modifié pour définir les
include vers le préprocesseur. L'identifiant applix1616 est défini pour le préprocesseur, plutôt que pour applix, ce qui a posé des
problèmes lors du remplacement de #include <applix>.
Relcc déclenche les passes du compilateur à l'aide de l' appel système exec , plutôt que de les rechercher.
Les passes du compilateur doivent résider quelque part dans votre chemin de recherche normal (voir la commande xpath dans
votre manuel d'utilisation.)
Relcc a un aperçu de la situation. Tout C indicateur et se termine par un code de sortie de 1 si un est détecté.
Tout Cpour l'attraper; l'
Vous devrez peutêtre vous appuyer Tout C appel système exec efface le drapeau au début
sur chaque passe.
Chaque passe du compilateur ferme les descripteurs de fichiers de sortie, d'entrée et d'erreur standard à la sortie, de sorte qu'ils
ne sont pas disponibles lorsque la passe suivante est invoquée. Relcc modifie l' appel système close pour garder ces fichiers
ouverts.
Le répertoire standard pour les fichiers d'inclusion est /hitech/include. Attribuezle à l'endroit où vous conservez réellement les
fichiers d'inclusion avant d'utiliser le compilateur. Spécifiez le drapeau I/hitech/include pour relcc (en fait le préprocesseur)
pour tout trouver.
Le répertoire standard pour les bibliothèques et le code de démarrage du runtime (crtapp, etc.) est /hitech.
Attribuezle comme assign /hitech /f0/hitech ou là où se trouvent réellement les bibliothèques.
Relcc.xrel est un code déplaçable, il reste donc en haut de la mémoire lorsque les passes du compilateur sont exécutées. Il est
possible que vous ayez également make.xrel quelque part audessus. Si le compilateur plante (probablement pendant la passe
d'assemblage), vous devrez réduire le disque RAM et l'espace de pile à environ 100 000150 000 au total. Désolé pour ça. Les
détails sur la façon d'utiliser buildmrd pour modifier le fichier mrdrivers sur votre disque de démarrage sont donnés dans le
manuel de référence technique et dans le nouveau manuel des programmes utilisateur.
NETTOYAGE.C
Modifié pour que le code compilé prenne en charge la redirection d'E/S OK.
MRD_CRTAPP.AS Il
s'agit du code de démarrage d'exécution normal piraté afin qu'il n'efface pas le BBS lors de l'entrée dans le code.
Utilisezle uniquement pour les pilotes résidents en mémoire (MRD) écrits en C. Si le code de démarrage normal
est utilisé, le stockage global du MRD sera supprimé à chaque fois que le MRD est appelé par l' appel système
callmrd . Spécifiez jmrd_crtapp.obj sur la ligne de commande pour résoudre ce problème. Ne l'utilisez jamais, sauf
lorsque vous effectuez un MRD.
NEW_CRTAPP.AS Le
code d'exécution modifié pour les programmes C normaux. Il laissait le pointeur d'environnement comme un
pointeur nul. Mais getenv() s'attend à ce qu'il pointe vers un pointeur nul s'il n'y a pas de table d'environnement.
SBRK.AS
La fonction sbrk() tente d'allouer plus de stockage audelà de la fin du segment BBS du programme. Sous le
système de mémoire du 1616, cela empiètera sur la mémoire réservée, provoquant des crashs époustouflants. La
version modifiée demande ici la mémoire au système avant de l'utiliser. Si le code est un fichier .xrel, alors la
mémoire sera forcément indisponible. Si vous ne pouvez pas retravailler un programme pour utiliser malloc() à la
place de sbrk(), le code ne s'exécutera que sous forme de fichier .exec à 4 000 $, à partir duquel il pourra augmenter
sa mémoire.
SYSCALLS.H Il
s'agit d'un fichier d'entête qui $définit chaque appel système 1616/OS en majuscules, avec le même ordre
d'utilisation et d'argument que dans le manuel du programmeur qu'Andrew a également écrit. Notez que les appels
printf(), sprintf() et fprintf() utilisent une astuce qui leur permet de contourner le mécanisme normal des appels
système, afin de pouvoir être utilisés avec n'importe quel nombre d'arguments. Ils fonctionnent également avec la
redirection d'E/S. Vous devez utiliser STDERR et STDOUT avec fprintf(), pas stderr et stdout.
De plus, toute E/S de fichier est un peu compliquée ; ne mélangez pas les descripteurs de fichiers, les lectures, les écritures, etc. du
C avec ceux natifs du système.
TOUPPER.AS
TOLOWER.AS
Évidemment, c'était une mauvaise journée lorsque ceuxci ont été écrits. Les comparaisons étaient terriblement
fausses. Je les ai réparés. Méfiezvous des #define'd toupper() et tolower() dans ctype.h. Ils ne vérifient pas que le
caractère est à portée avant d'effectuer l'addition ou la soustraction. Ils sont une nuisance.
19.3 C avancé
Techniques et applications
Gerald E. Sobelman et
David E Krekkelberg
Que Corp, Indianapolis, Indiana
ISBN0880221623
19.4
Indice Tutoriel C je
dumbconv.c,
Machine Translated 36
by Google K&R, 91
allocation dynamique, 121
variables dynamiques, 122 fonctions de bibliothèque,
dynlink.c, 125 57 saut de
dynlist.c, 121 ligne, 94 liste
chaînée, 125 list.c,
modifier, 141, 146 variables
11 sinon, locales, 56 comparaisons
33 fin du marqueur, 75 logiques, 45 évaluation
Problème EOF, 104 logique, 47 fonctions
logiques, 13 3
false, 46 long int, 41 boucle utilisant
fgets(), 105 while, 31 boucles, limites
entréessorties de fichiers, d'imbrication, 32
101 float, lottypes.c, 43 minuscules, 131
42 tableaux à virgule flottante,
74 fonctions à virgule flottante, macro, 62
54 floatsq.c, macro.c, 62
54 touches de main, 21
fonction, 9 9 malloc(), 122, 123
fopen(), 103 allocation de mémoire, 122
for loop, 32 types de données de
forloop.c, 32 style de mélange, 42
formatage, 24, 36 modulo, 41
formout.c, 101 mortotypes.c, 42 tableaux
fprintf(), 98 multidimensionnels, 76 multiary.c, 76
free(), 12 4
fscanf(), 104 structures nommées, 114
fonction, 21 Fonctions et variables, 51 nested.c, 114
boucles imbriquées,
variables globales, 55 32 structures imbriquées,
goodform.c, 24 114 nouvelle ligne
goto, 34 \n, 22
gotoex.c, 34 non, 46 caractère NULL, 71
Indice Tutoriel C ii
impression
Machine Translated byd'un fichier,
Google struct, 111
106 comparaisons de struct1.c, 111
problèmes, 48 contrôle de struct2.c, 112
programme, 31 promotion de struct3.c, 113
char en int, 42 putc(), 103 programmation structurée, 35
structures, 111
lire un fichier, 103 structures et unions, 111 style
lire une ligne, 105 de formatage, 2 4 tableaux
lire un mot, 104 indicés, 76 sumsqres.c,
readchar.c, 103 51 switch, 34
readline.c, 105 switch.c, 34
readtext.c, 104 variable de
récursion, 57 commutation, 34 constante
recurson.c , 57 symbolique, 61
enregistrer des variables,
56 relcc.xrel, tempconv.c, 35
11 renvoyer, fichiers temporaires,
53 renvoyer une 11 tenlines.txt, 102
valeur, 53 renvoyer des données dans des tableaux, 74 trivial.c, 21
true, 46
scanf(), 95 twoway.c, 84
scope.c, 55 typedef, 118
portée des variables, 55
segments, 122 laidform.c, 24
pointvirgule ;, 21 souligné, 12
instruction shift, 134 union1.c, 116
shifter.c, 134 union2.c, 117
short int, 41 unions, 116
simpleio.c, 91 uplow.c, 131
sortie de caractère unique, 102 majuscules et minuscules, 131
singleio.c, 93 fonctions définies par l'utilisateur,
chaînes de tri, 73 51 type défini par l'utilisateur, 111
special.c, 98
sprintf(), 97 transmission de
crochets [ ] tableau, 71 squares.c, valeurs, 52 caractéristiques des
53 pile, 58 variables, 43 nom de fichier
débordement variable, 105 sortie
de pile, 116 bibliothèques variable %, portée de
de fonctions standard, 57 entrée 23
sortie standard, 91 étoile * variables, 55 vc.c, 149
pointeur, 81 étoile calculatrice visuelle, 149, 151 calculatrice visuelle tutoriel, 151
barre oblique /* commentaires, 2
4 terminateur d'instruction ;, 21 whatnext.c, 144
variables statiques, 56 while.c, 31
fichier d'entête stdio.h, 91 while boucle, 31
store *, 81 wrtmore.c, 22
fonction strcat, 73 wrtsome.c, 21
fonction strcmp, 73
strcpy(), 103 XOR ^, 133
fonction strcpy , 72 xpath, 11
chaîne se termine par null,
71 chaîne variable comme pointeur, zéro, caractère nul, 71
83 stringin.c,
96 chaînes,
71 chaînes.c,
72 Chaînes et tableaux, 71
Tutoriel C je
5 Fonctions
Machine Translated et variables ....................................................... ........................ 51 5.1 Notre première
by Google
fonction définie par l'utilisateur .............. .................................................................. 51 5.2 Définir les
fonctions .................................................. ...................................... 52 5.3 Passer une valeur à une
fonction . .................................................................. .................. 52 5.4 En savoir plus sur la
transmission d'une valeur à une fonction ............... ...................... 52 5.5 Maintenant, confessons un
petit mensonge .............. .................................................................. ........ 53 5.6 Fonctions à virgule
flottante .............................. .................................................. 5 4 5.7 Portée des
variables ............................................ ...................................... 55 5.8 En savoir plus Variables
"automatiques" ..................................................... .................... 56 5.9 Que sont les variables
statiques ? .................................................................. ...................... 56 5.10 Utiliser à nouveau le
même nom .............. .................................................................. .... 56 5.11 Qu'estce qu'une variable
de registre ? .................................................................. ...................... 56 5.12 Où définir les
variables ? .................................................................. .................... 57 5.13 Bibliothèques de
fonctions standard ............................ .................................................. 57 5.14 Qu'estce que la
récursivité ? .................................................................. ................................... 57 5.15 Qu'atil
fait ? .................................................................. ........................................ 58 5.16 Un autre exemple
de récursion .................................................................. ............ 58 5.17 Exercices de
programmation ............................ .................................................................. .59
Tutoriel C ii
8.13by
Machine Translated Exercices
Google de programmation .................................................. ................................ 85
Tutoriel C iii
12 Allocation
Machine Translated dynamique .................................................. ...................... 121
by Google
12.1 Qu'estce que l'allocation dynamique ? .................................................................. ...................... 121
12.2 Création de variables dynamiques .................................................. ...................... 122
12.3 Qu'estce qu'un tas ? .................................................................. ...................................... 122
12.4 En savoir plus sur les segments ............................................ ...................................... 122
12.5 Retour à la fonction "Malloc" .......................................... ...................... 123
12.6 Qu'estce qu'un casting ? .................................................................. ........................................ 123
12.7 Utilisation du bloc de mémoire alloué dynamiquement .................................. 123
12.8 Suppression des données allouées dynamiquement 124 .......................................
12.9 Cela a fait l'objet de nombreuses discussions .............................................. ...................... 124
12.10 Un tableau de pointeurs .................................................. ...................................... 124
12.11 Une liste chaînée .................................................. .................................................. 12 5
12.12 Les définitions des données .................................................. ...................................... 126
12.13 Le premier champ .................................................. .................................................. 127
12.14 Remplissage de structures supplémentaires .............................................. ...................... 127
12.15 Impression des données .................................................. .................................. 127
12.16 En savoir plus sur l'allocation dynamique et les listes liées .............................. 128
12.17 Une autre nouvelle fonction Calloc ............................................ ...................... 128
12.18 Exercices de programmation .................................................. ................................ 128
Tutoriel C iv
Machine Translated by Google Tableau des figures
Tutoriel C dans
struct3.cby
Machine Translated ...............................................
Google .................................................................. ...................... 113
imbriqué.c ............................................... .................................................................. ...................... 115
syndicat1.c ................................................. . .................................................................. ...................... 117
union2.c ............................................................ .................................................................. ...................... 118
dynlist.c ............................................... .................................................................. ...................... 121
bigdyn1.c .................................................. .................................................................. ...................... 125
dynlink.c ............................................... .................................................................. ...................... 126
uplow.c ......................................................................................................................... 131
charclas.c ............................................... .................................................................. ...................... 132
bitops.c ............................................... .................................................................. ...................... 133
levier de vitesse.c ............................................... .................................................................. ...................... 134
dosex_1616.c .................................................. .................................................................. ............... 144
et après.c ............................................... .................................................................. ................... 146
liste.c ............................................................ .................................................................. ...................... 146
vc.c ............................................... .................................................................. .. ................................. 1415
Tutoriel C nous