
Un programme informatique est une séquence ou un ensemble d'instructions dans un langage de programmation qu'un ordinateur doit exécuter . Il s'agit d'un composant d' un logiciel , qui comprend également la documentation et d'autres composants intangibles.
Un programme informatique dans sa forme lisible par l'homme est appelé code source . Le code source a besoin d'un autre programme informatique pour s'exécuter car les ordinateurs ne peuvent exécuter que leurs instructions machine natives . Par conséquent, le code source peut être traduit en instructions machine à l'aide d'un compilateur écrit pour le langage. ( Les programmes en langage assembleur sont traduits à l'aide d'un assembleur .) Le fichier résultant est appelé un exécutable . Alternativement, le code source peut s'exécuter dans un interpréteur écrit pour le langage.
Si l'exécutable est demandé pour exécution, le système d'exploitation le charge en mémoire et démarre un processus . L' unité centrale de traitement passera bientôt à ce processus afin de pouvoir récupérer, décoder, puis exécuter chaque instruction machine.
Si le code source est demandé pour exécution, le système d'exploitation charge l'interpréteur correspondant en mémoire et démarre un processus. L'interpréteur charge ensuite le code source en mémoire pour traduire et exécuter chaque instruction . L'exécution du code source est plus lente que l'exécution d'un exécutable . De plus, l'interpréteur doit être installé sur l'ordinateur.
Exemple de programme informatique
Le programme "Hello, World!" est utilisé pour illustrer la syntaxe de base d'un langage . La syntaxe du langage BASIC (1964) a été volontairement limitée pour rendre le langage facile à apprendre. Par exemple, les variables ne sont pas déclarées avant d'être utilisées. De plus, les variables sont automatiquement initialisées à zéro. Voici un exemple de programme informatique, en Basic, pour faire la moyenne d'une liste de nombres :
10 INPUT "Combien de nombres faire la moyenne ?" , A 20 FOR I = 1 TO A 30 INPUT "Entrez le nombre :" , B 40 LET C = C + B 50 NEXT I 60 LET D = C / A 70 PRINT "La moyenne est" , D 80 END
Une fois les mécanismes de la programmation informatique de base appris, des langages plus sophistiqués et plus puissants sont disponibles pour construire de grands systèmes informatiques.
Histoire
Les progrès réalisés dans le développement des logiciels sont le résultat des progrès réalisés dans le matériel informatique . À chaque étape de l'histoire du matériel, la tâche de programmation informatique a radicalement changé.
Moteur d'analyse

En 1837, le métier à tisser de Jacquard a inspiré Charles Babbage pour tenter de construire la machine analytique . Les noms des composants de l'appareil de calcul ont été empruntés à l'industrie textile. Dans l'industrie textile, le fil était apporté du magasin pour être moulu. L'appareil avait une mémoire qui comprenait 1 000 nombres de 50 chiffres décimaux chacun. Les nombres du magasin étaient transférés à l' usine pour traitement. Le moteur était programmé à l'aide de deux jeux de cartes perforées. Un jeu dirigeait l'opération et l'autre jeu saisissait les variables. Cependant, les milliers de roues dentées et d'engrenages n'ont jamais fonctionné complètement ensemble.
Ada Lovelace a travaillé pour Charles Babbage pour créer une description de la machine analytique (1843). La description contenait la note G qui détaillait complètement une méthode de calcul des nombres de Bernoulli à l'aide de la machine analytique. Cette note est reconnue par certains historiens comme le premier programme informatique au monde .
Machine de Turing universelle

En 1936, Alan Turing a présenté la machine de Turing universelle , un dispositif théorique capable de modéliser tous les calculs. Il s'agit d'une machine à états finis dotée d'une bande de lecture/écriture infiniment longue. La machine peut déplacer la bande d'avant en arrière, modifiant son contenu au fur et à mesure qu'elle exécute un algorithme . La machine démarre dans l'état initial, passe par une séquence d'étapes et s'arrête lorsqu'elle rencontre l'état d'arrêt. Tous les ordinateurs actuels sont complets selon la norme Turing .
ENIAC

L' intégrateur numérique et ordinateur électronique (ENIAC) a été construit entre juillet 1943 et l'automne 1945. C'était un ordinateur polyvalent complet de Turing qui utilisait 17 468 tubes à vide pour créer les circuits . À la base, il s'agissait d'une série de pascalines câblées ensemble. Ses 40 unités pesaient 30 tonnes, occupaient 1 800 pieds carrés (167 m 2 ) et consommaient 650 $ par heure ( en monnaie des années 1940 ) en électricité lorsqu'elles étaient inactives. Il disposait de 20 accumulateurs de base 10. La programmation de l'ENIAC prenait jusqu'à deux mois. Trois tables de fonctions étaient sur roulettes et devaient être déplacées vers des panneaux de fonctions fixes. Les tables de fonctions étaient connectées aux panneaux de fonctions en branchant de lourds câbles noirs dans des tableaux de connexion . Chaque table de fonctions comportait 728 boutons rotatifs. La programmation de l'ENIAC impliquait également le réglage de certains des 3 000 commutateurs. Le débogage d'un programme prenait une semaine. Il a fonctionné de 1947 à 1955 au terrain d'essai d'Aberdeen , calculant les paramètres des bombes à hydrogène, prédisant les conditions météorologiques et produisant des tables de tir pour viser les canons d'artillerie.
Ordinateurs à programme enregistré
Au lieu de brancher des câbles et d'actionner des interrupteurs, un ordinateur à programme enregistré charge ses instructions dans la mémoire de la même manière qu'il charge ses données dans la mémoire. En conséquence, l'ordinateur pouvait être programmé rapidement et effectuer des calculs à des vitesses très élevées. Presper Eckert et John Mauchly ont construit l'ENIAC. Les deux ingénieurs ont introduit le concept de programme enregistré dans un mémo de trois pages daté de février 1944. Plus tard, en septembre 1944, John von Neumann a commencé à travailler sur le projet ENIAC. Le 30 juin 1945, von Neumann a publié la première ébauche d'un rapport sur l'EDVAC , qui assimilait les structures de l'ordinateur à celles du cerveau humain. La conception est devenue connue sous le nom d' architecture von Neumann . L'architecture a été déployée simultanément dans les constructions des ordinateurs EDVAC et EDSAC en 1949.
L' IBM System/360 (1964) était une famille d'ordinateurs, chacun ayant la même architecture de jeu d'instructions . Le modèle 20 était le plus petit et le moins cher. Les clients pouvaient mettre à niveau et conserver le même logiciel d'application . Le modèle 195 était le plus haut de gamme. Chaque modèle System/360 était doté de la multiprogrammation c'est-à-dire qu'il disposait de plusieurs processus en mémoire à la fois. Lorsqu'un processus attendait une entrée/sortie , un autre pouvait effectuer le calcul.
IBM avait prévu que chaque modèle soit programmé en utilisant PL/1 . Un comité a été formé, comprenant des programmeurs COBOL , Fortran et ALGOL . L'objectif était de développer un langage complet, facile à utiliser, extensible et qui remplacerait Cobol et Fortran. Le résultat a été un langage volumineux et complexe qui a pris beaucoup de temps à compiler .

Les ordinateurs fabriqués jusqu'aux années 1970 étaient équipés de commutateurs sur le panneau avant pour la programmation manuelle. Le programme informatique était écrit sur papier pour référence. Une instruction était représentée par une configuration de paramètres marche/arrêt. Après avoir défini la configuration, un bouton d'exécution était enfoncé. Ce processus était ensuite répété. Les programmes informatiques étaient également saisis automatiquement via une bande papier , des cartes perforées ou une bande magnétique . Une fois le support chargé, l'adresse de départ était définie via des commutateurs et le bouton d'exécution était enfoncé.
Intégration à très grande échelle

Une étape majeure dans le développement des logiciels a été l'invention du circuit d'intégration à très grande échelle (VLSI) (1964). Après la Seconde Guerre mondiale , la technologie à base de tubes a été remplacée par des transistors à contact ponctuel (1947) et des transistors à jonction bipolaire (fin des années 1950) montés sur une carte de circuit imprimé . [ Au cours des années 1960 , l' industrie aérospatiale a remplacé la carte de circuit imprimé par une puce de circuit intégré .
Robert Noyce , cofondateur de Fairchild Semiconductor (1957) et d'Intel (1968), a réalisé une amélioration technologique pour affiner la production de transistors à effet de champ (1963). L'objectif est de modifier la résistivité et la conductivité électriques d'une jonction semi-conductrice . Tout d'abord, les minéraux silicatés naturels sont convertis en tiges de polysilicium à l'aide du procédé Siemens . Le procédé Czochralski convertit ensuite les tiges en un silicium monocristallin , un cristal boule . Le cristal est ensuite finement tranché pour former un substrat de plaquette . Le processus planaire de photolithographie intègre ensuite des transistors unipolaires, des condensateurs , des diodes et des résistances sur la plaquette pour construire une matrice de transistors métal-oxyde-semiconducteur (MOS). Le transistor MOS est le composant principal des puces de circuits intégrés .
À l'origine, les puces de circuits intégrés avaient leur fonction définie lors de la fabrication. Au cours des années 1960, le contrôle du flux électrique a migré vers la programmation d'une matrice de mémoire morte (ROM). La matrice ressemblait à un réseau bidimensionnel de fusibles. Le processus d'intégration des instructions sur la matrice consistait à brûler les connexions inutiles. Il y avait tellement de connexions que les programmeurs de micrologiciels ont écrit un programme informatique sur une autre puce pour superviser la gravure. La technologie est devenue connue sous le nom de ROM programmable . En 1971, Intel a installé le programme informatique sur la puce et l'a nommée microprocesseur Intel 4004.

Les termes microprocesseur et unité centrale de traitement (CPU) sont désormais utilisés de manière interchangeable. Cependant, les CPU sont antérieurs aux microprocesseurs. Par exemple, l' IBM System/360 (1964) avait un CPU fabriqué à partir de cartes de circuits imprimés contenant des composants discrets sur des substrats en céramique .
État de Sac 8008

L'Intel 4004 (1971) était un microprocesseur 4 bits conçu pour exécuter la calculatrice Busicom . Cinq mois après sa sortie, Intel a sorti l' Intel 8008 , un microprocesseur 8 bits. Bill Pentz a dirigé une équipe à Sacramento State pour construire le premier micro-ordinateur utilisant l'Intel 8008 : le Sac State 8008 (1972). Son but était de stocker les dossiers médicaux des patients. L'ordinateur prenait en charge un système d'exploitation sur disque dur pour exécuter un disque dur Memorex de 3 mégaoctets . Il avait un écran couleur et un clavier regroupés dans une seule console. Le système d'exploitation sur disque était programmé à l'aide du langage d'assemblage de base (BAL) d'IBM . L'application des dossiers médicaux était programmée à l'aide d'un interpréteur BASIC . Cependant, l'ordinateur était une impasse évolutive car il était extrêmement coûteux. De plus, il a été construit dans un laboratoire d'une université publique dans un but précis. Néanmoins, le projet a contribué au développement du jeu d'instructions Intel 8080 (1974) .
Série x86

En 1978, l' environnement de développement logiciel moderne a commencé lorsque Intel a mis à niveau l' Intel 8080 vers l' Intel 8086. Intel a simplifié l'Intel 8086 pour fabriquer l' Intel 8088 moins cher . IBM a adopté l'Intel 8088 lorsqu'ils sont entrés sur le marché des ordinateurs personnels (1981). La demande des consommateurs pour les ordinateurs personnels a augmenté, tout comme le développement des microprocesseurs d'Intel. La succession de développement est connue sous le nom de série x86 . Le langage d'assemblage x86 est une famille d' instructions machine rétrocompatibles . Les instructions machine créées dans les microprocesseurs antérieurs ont été conservées tout au long des mises à niveau des microprocesseurs. Cela a permis aux consommateurs d'acheter de nouveaux ordinateurs sans avoir à acheter de nouveaux logiciels d'application . Les principales catégories d'instructions sont les suivantes :
- Instructions de mémoire pour définir et accéder aux nombres et aux chaînes dans la mémoire à accès aléatoire .
- Instructions d' unité arithmétique et logique (ALU) pour effectuer les opérations arithmétiques primaires sur les entiers .
- Instructions ALU à virgule flottante pour effectuer les opérations arithmétiques primaires sur les nombres réels .
- Appelez les instructions de la pile pour pousser et faire apparaître les mots nécessaires à l'allocation de mémoire et à l'interface avec les fonctions .
- Instructions d'instruction unique, données multiples (SIMD) pour augmenter la vitesse lorsque plusieurs processeurs sont disponibles pour exécuter le même algorithme sur un tableau de données .
Changement d'environnement de programmation

Les circuits VLSI ont permis à l' environnement de programmation de passer d'un terminal informatique (jusqu'aux années 1990) à un ordinateur à interface utilisateur graphique (GUI). Les terminaux informatiques limitaient les programmeurs à un seul shell exécuté dans un environnement de ligne de commande . Au cours des années 1970, l'édition de code source en plein écran est devenue possible grâce à une interface utilisateur textuelle . Quelle que soit la technologie disponible, l'objectif est de programmer dans un langage de programmation .
Paradigmes et langages de programmation
Les fonctionnalités du langage de programmation existent pour fournir des blocs de construction à combiner pour exprimer les idéaux de programmation. Idéalement, un langage de programmation devrait :
- exprimer des idées directement dans le code.
- exprimer des idées indépendantes de manière indépendante.
- exprimer les relations entre les idées directement dans le code.
- combiner librement les idées.
- combiner des idées uniquement là où les combinaisons ont du sens.
- exprimer des idées simples simplement.
Le style de programmation d'un langage de programmation pour fournir ces éléments de base peut être classé en paradigmes de programmation . Par exemple, différents paradigmes peuvent différencier :
- langages procéduraux , langages fonctionnels et langages logiques .
- différents niveaux d' abstraction des données .
- différents niveaux de hiérarchie de classes .
- différents niveaux de types de données d'entrée , comme dans les types de conteneurs et la programmation générique .
Chacun de ces styles de programmation a contribué à la synthèse de différents langages de programmation .
Un langage de programmation est un ensemble de mots-clés , de symboles , d'identifiants et de règles par lesquels les programmeurs peuvent communiquer des instructions à l'ordinateur. Ils suivent un ensemble de règles appelées syntaxe .
- Les mots-clés sont des mots réservés pour former des déclarations et des affirmations .
- Les symboles sont des caractères permettant de former des opérations , des affectations , des flux de contrôle et des délimiteurs .
- Les identificateurs sont des mots créés par les programmeurs pour former des constantes , des noms de variables , des noms de structures et des noms de fonctions .
- Les règles de syntaxe sont définies sous la forme Backus–Naur .
Les langages de programmation tirent leur base des langages formels . Le but de la définition d'une solution en termes de langage formel est de générer un algorithme pour résoudre le problème sous-jacent. Un algorithme est une séquence d'instructions simples qui résolvent un problème.
Générations de langage de programmation

L'évolution des langages de programmation a commencé lorsque l' EDSAC (1949) a utilisé le premier programme informatique stocké dans son architecture von Neumann . La programmation de l'EDSAC faisait partie de la première génération de langage de programmation .
- La première génération de langage de programmation est le langage machine . Le langage machine exige que le programmeur saisisse des instructions à l'aide de numéros d'instructions appelés code machine . Par exemple, l'opération ADD sur le PDP-11 porte le numéro d'instruction 24576.
- La deuxième génération de langage de programmation est le langage assembleur . Le langage assembleur permet au programmeur d'utiliser des instructions mnémoniques au lieu de mémoriser les numéros d'instructions. Un assembleur traduit chaque mnémonique du langage assembleur en son numéro de langage machine. Par exemple, sur le PDP-11, l'opération 24576 peut être référencée comme ADD dans le code source. Les quatre opérations arithmétiques de base ont des instructions d'assemblage comme ADD, SUB, MUL et DIV. Les ordinateurs ont également des instructions comme DW (Define Word ) pour réserver des cellules de mémoire . Ensuite, l'instruction MOV peut copier des entiers entre les registres et la mémoire.
- La structure de base d'une instruction en langage assembleur est une étiquette, une opération , un opérande et un commentaire.
- Les étiquettes permettent au programmeur de travailler avec des noms de variables . L'assembleur traduira ensuite les étiquettes en adresses de mémoire physique .
- Les opérations permettent au programmeur de travailler avec des mnémoniques. L'assembleur traduira ensuite les mnémoniques en numéros d'instructions.
- Les opérandes indiquent à l'assembleur quelles données l'opération traitera.
- Les commentaires permettent au programmeur d’articuler un récit car les instructions seules sont vagues.
- La caractéristique clé d'un programme en langage assembleur est qu'il forme une correspondance biunivoque avec sa cible en langage machine correspondante.
- La troisième génération de langages de programmation utilise des compilateurs et des interprètes pour exécuter des programmes informatiques. La caractéristique distinctive d'un langage de troisième génération est son indépendance par rapport à un matériel particulier. Les premiers langages incluent Fortran (1958), COBOL (1959), ALGOL (1960) et BASIC (1964). En 1973, le langage de programmation C est apparu comme un langage de haut niveau qui produisait des instructions de langage machine efficaces. Alors que les langages de troisième génération généraient historiquement de nombreuses instructions machine pour chaque instruction, C a des instructions qui peuvent générer une seule instruction machine. De plus, un compilateur optimisant peut prendre le pas sur le programmeur et produire moins d'instructions machine que d'instructions. Aujourd'hui, tout un paradigme de langages remplit le spectre impératif de troisième génération .
- La quatrième génération de langages de programmation met l'accent sur les résultats de sortie souhaités, plutôt que sur la manière dont les instructions de programmation doivent être construites. Les langages déclaratifs tentent de limiter les effets secondaires et permettent aux programmeurs d'écrire du code avec relativement peu d'erreurs. Un langage de quatrième génération populaire est appelé Structured Query Language (SQL). de bases de données n'ont plus besoin de traiter chaque enregistrement de base de données un par un. De plus, une simple instruction peut générer des enregistrements de sortie sans avoir à comprendre comment ils sont récupérés.
Langues impératives

Les langages impératifs spécifient un algorithme séquentiel à l'aide de déclarations , d'expressions et d'instructions :
- Une déclaration introduit un nom de variable dans le programme informatique et l’affecte à un type de données – par exemple :
var x: integer; - Une expression donne une valeur – par exemple :
2 + 2donne 4 - Une instruction peut affecter une expression à une variable ou utiliser la valeur d'une variable pour modifier le flux de contrôle du programme , par exemple :
x := 2 + 2; if x = 4 then do_something();
Fortran
FORTRAN (1958) a été présenté comme le « système de traduction de formules mathématiques d'IBM ». Il a été conçu pour les calculs scientifiques, sans fonctionnalités de manipulation de chaînes . Outre les déclarations , les expressions et les instructions , il prenait en charge :
- tableaux .
- sous-routines .
- Boucles « do » .
Cela a réussi parce que :
- les coûts de programmation et de débogage étaient inférieurs aux coûts de fonctionnement de l'ordinateur.
- il a été soutenu par IBM.
- les applications à l'époque étaient scientifiques.
Cependant, des fournisseurs non IBM ont également écrit des compilateurs Fortran, mais avec une syntaxe qui aurait probablement fait échouer le compilateur d'IBM. L' American National Standards Institute (ANSI) a développé la première norme Fortran en 1966. En 1978, Fortran 77 est devenu la norme jusqu'en 1991. Fortran 90 prend en charge :
- enregistrements .
- pointeurs vers des tableaux.
COBOL
COBOL (1959) signifie « Common Business Oriented Language ». Fortran manipulait des symboles. On s'est vite rendu compte que les symboles n'avaient pas besoin d'être des nombres, donc des chaînes de caractères ont été introduites. Le Département de la Défense américain a influencé le développement de COBOL, Grace Hopper étant l'un des principaux contributeurs. Les instructions étaient de type anglais et verbeuses. L'objectif était de concevoir un langage permettant aux gestionnaires de lire les programmes. Cependant, le manque d'instructions structurées a entravé cet objectif.
Le développement de COBOL a été étroitement contrôlé, de sorte que des dialectes n'ont pas émergé pour nécessiter des normes ANSI. En conséquence, il n'a pas été modifié pendant 15 ans jusqu'en 1974. La version des années 1990 a apporté des modifications conséquentes, comme la programmation orientée objet .
Algol
ALGOL (1960) signifie « ALGOrithmic Language ». Il a eu une profonde influence sur la conception des langages de programmation. Issu d'un comité d'experts européens et américains en langages de programmation, il utilisait une notation mathématique standard et avait une conception lisible et structurée. Algol a été le premier à définir sa syntaxe en utilisant la forme Backus–Naur . Cela a conduit à des compilateurs orientés syntaxe . Il a ajouté des fonctionnalités telles que :
- structure de bloc , où les variables étaient locales à leur bloc.
- tableaux avec des limites variables.
- Boucles « for » .
- fonctions .
- récursivité .
Les descendants directs d'Algol incluent Pascal , Modula-2 , Ada , Delphi et Oberon sur une branche. Sur une autre branche, les descendants incluent C , C++ et Java .
Basique
BASIC (1964) signifie « Code d'instruction symbolique polyvalent pour débutants ». Il a été développé au Dartmouth College pour que tous ses étudiants l'apprennent. Même si un étudiant ne passait pas à un langage plus puissant, il se souvenait toujours du Basic. Un interpréteur Basic a été installé dans les micro-ordinateurs fabriqués à la fin des années 1970. Au fur et à mesure que l'industrie des micro-ordinateurs s'est développée, le langage a également grandi.
Basic a été le pionnier de la session interactive . Il offrait des commandes de système d'exploitation au sein de son environnement :
- La commande « nouveau » a créé une page vide.
- Déclarations évaluées immédiatement.
- Les instructions pourraient être programmées en les précédant de numéros de ligne.
- La commande « liste » affiche le programme.
- La commande « run » a exécuté le programme.
Cependant, la syntaxe de base était trop simple pour les programmes volumineux. Les dialectes récents ont ajouté une structure et des extensions orientées objet. Visual Basic de Microsoft est toujours largement utilisé et produit une interface utilisateur graphique .
C
Le langage de programmation C (1973) doit son nom au remplacement du langage BCPL par le langage B , et AT&T Bell Labs a appelé la version suivante « C ». Son but était d'écrire le système d'exploitation UNIX . C est un langage relativement petit, ce qui facilite l'écriture de compilateurs. Sa croissance a reflété la croissance du matériel dans les années 1980. Sa croissance est également due au fait qu'il dispose des fonctionnalités du langage assembleur , mais utilise une syntaxe de haut niveau . Il a ajouté des fonctionnalités avancées telles que :
- assembleur en ligne .
- arithmétique sur les pointeurs.
- pointeurs vers des fonctions.
- opérations sur les bits.
- combinant librement des opérateurs complexes .

Le C permet au programmeur de contrôler la région de mémoire à stocker. Les variables globales et les variables statiques nécessitent le moins de cycles d'horloge pour être stockées. La pile est automatiquement utilisée pour les déclarations de variables standard . La mémoire de tas est renvoyée à une variable pointeur à partir de la malloc()fonction.
- La région des données globales et statiques se situe juste au-dessus de la région du programme . (La région du programme est techniquement appelée la région du texte . C'est là que sont stockées les instructions de la machine.)
- La région de données globale et statique est techniquement composée de deux régions. Une région est appelée segment de données initialisé , où sont stockées les variables déclarées avec des valeurs par défaut. L'autre région est appelée bloc démarré par segment , où sont stockées les variables déclarées sans valeurs par défaut.
- Les adresses des variables stockées dans la région de données globales et statiques sont définies au moment de la compilation. Elles conservent leurs valeurs tout au long de la vie du processus.
- La région globale et statique stocke les variables globales qui sont déclarées au-dessus (à l'extérieur) de la
main()fonction. Les variables globales sont visibles parmain()et toutes les autres fonctions dans le code source.
- La région globale et statique stocke les variables globales qui sont déclarées au-dessus (à l'extérieur) de la
- D'autre part, les déclarations de variables à l'intérieur de
main(), d'autres fonctions ou à l'intérieur{}de délimiteurs de blocs sont des variables locales . Les variables locales incluent également des variables de paramètres formelles . Les variables de paramètres sont placées entre les parenthèses d'une définition de fonction. Les paramètres fournissent une interface à la fonction.
- Les variables locales déclarées à l'aide du
staticpréfixe sont également stockées dans la région de données globales et statiques . Contrairement aux variables globales, les variables statiques ne sont visibles que dans la fonction ou le bloc. Les variables statiques conservent toujours leur valeur. Un exemple d'utilisation serait la fonctionint increment_counter(){static int counter = 0; counter++; return counter;}
- Les variables locales déclarées à l'aide du
- La région de pile est un bloc de mémoire contigu situé près de l'adresse mémoire supérieure. Les variables placées dans la pile sont renseignées de haut en bas. Un pointeur de pile est un registre à usage spécial qui garde la trace de la dernière adresse mémoire renseignée. Les variables sont placées dans la pile via l' instruction PUSH du langage assembleur . Par conséquent, les adresses de ces variables sont définies pendant l'exécution . La méthode permettant aux variables de pile de perdre leur portée est via l'instruction POP.
- Les variables locales déclarées sans
staticpréfixe, y compris les variables de paramètres formels, sont appelées variables automatiques et sont stockées dans la pile. Elles sont visibles à l'intérieur de la fonction ou du bloc et perdent leur portée à la sortie de la fonction ou du bloc.
- Les variables locales déclarées sans
- La zone de tas est située sous la pile. Elle est peuplée de bas en haut. Le système d'exploitation gère le tas à l'aide d'un pointeur de tas et d'une liste de blocs de mémoire alloués. Comme la pile, les adresses des variables de tas sont définies pendant l'exécution. Une erreur de manque de mémoire se produit lorsque le pointeur de tas et le pointeur de pile se rencontrent.
- C fournit la
malloc()fonction de bibliothèque permettant d' allouer la mémoire du tas. Le remplissage du tas avec des données est une fonction de copie supplémentaire. Les variables stockées dans le tas sont transmises de manière économique aux fonctions à l'aide de pointeurs. Sans pointeurs, l'intégralité du bloc de données devrait être transmise à la fonction via la pile.
- C fournit la
C++
Dans les années 1970, les ingénieurs logiciels avaient besoin d'un support linguistique pour décomposer les grands projets en modules . Une fonctionnalité évidente était de décomposer physiquement les grands projets en fichiers séparés . Une fonctionnalité moins évidente était de décomposer logiquement les grands projets en types de données abstraits . À l'époque, les langages prenaient en charge les types de données concrets (scalaires) comme les nombres entiers , les nombres à virgule flottante et les chaînes de caractères . Les types de données abstraits sont des structures de types de données concrets, avec un nouveau nom attribué. Par exemple, une liste d'entiers pourrait être appelée integer_list.
Dans le jargon orienté objet, les types de données abstraits sont appelés classes . Cependant, une classe n'est qu'une définition ; aucune mémoire n'est allouée. Lorsque de la mémoire est allouée à une classe et liée à un identifiant , elle est appelée un objet .
Les langages impératifs orientés objet se sont développés en combinant le besoin de classes et le besoin d' une programmation fonctionnelle sûre . Une fonction, dans un langage orienté objet, est assignée à une classe. Une fonction assignée est alors appelée méthode , fonction membre ou opération . La programmation orientée objet consiste à exécuter des opérations sur des objets .
Les langages orientés objet prennent en charge une syntaxe permettant de modéliser les relations sous-ensemble/sur-ensemble . Dans la théorie des ensembles , un élément d'un sous-ensemble hérite de tous les attributs contenus dans le sur-ensemble. Par exemple, un étudiant est une personne. Par conséquent, l'ensemble des étudiants est un sous-ensemble de l'ensemble des personnes. Par conséquent, les étudiants héritent de tous les attributs communs à toutes les personnes. De plus, les étudiants ont des attributs uniques que les autres personnes n'ont pas. Les langages orientés objet modélisent les relations sous-ensemble/sur-ensemble à l'aide de l'héritage . La programmation orientée objet est devenue le paradigme de langage dominant à la fin des années 1990.
C++ (1985) s'appelait à l'origine « C avec classes ». Il a été conçu pour étendre les capacités de C en ajoutant les fonctionnalités orientées objet du langage Simula .
Un module orienté objet est composé de deux fichiers. Le fichier de définitions est appelé fichier d'en-tête . Voici un fichier d'en-tête C++ pour la classe GRADE dans une application scolaire simple :
// note.h // ------- // Utilisé pour permettre à plusieurs fichiers sources d'inclure // ce fichier d'en-tête sans erreurs de duplication. // ---------------------------------------------- #ifndef GRADE_H #define GRADE_H classe GRADE { public : // Ceci est l'opération du constructeur. // ---------------------------------- GRADE ( const char letter ); // Ceci est une variable de classe. // ------------------------- char letter ; // Il s'agit d'une opération membre. // --------------------------- int grade_numeric ( const char letter ); // Ceci est une variable de classe. // ------------------------- int numeric ; }; #endif
Une opération de constructeur est une fonction portant le même nom que le nom de la classe. Elle est exécutée lorsque l'opération appelante exécute l' newinstruction.
L'autre fichier d'un module est le fichier source . Voici un fichier source C++ pour la classe GRADE dans une application scolaire simple :
// note.cpp // --------- #include "note.h" GRADE :: GRADE ( const char letter ) { // Référencez l'objet en utilisant le mot-clé 'this'. // ---------------------------------------------- this -> letter = letter ; // Ceci est la cohésion temporelle // ------------------------- this -> numeric = grade_numeric ( letter ); } int GRADE :: grade_numeric ( const char lettre ) { si ( ( lettre == 'A' || lettre == 'a' ) ) renvoie 4 ; sinon si ( ( lettre == 'B' || lettre == 'b' ) ) renvoie 3 ; sinon si ( ( lettre == 'C' || lettre == 'c' ) ) renvoie 2 ; sinon si ( ( lettre == 'D' || lettre == 'd' ) ) renvoie 1 ; sinon si ( ( lettre == 'F' || lettre == 'f' ) ) renvoie 0 ; sinon renvoie -1 ; }
Voici un fichier d'en-tête C++ pour la classe PERSON dans une application scolaire simple :
// personne.h // -------- #ifndef PERSON_H #define PERSON_H classe PERSONNE { public : PERSONNE ( const char * nom ); const char * nom ; }; #endif
Voici un fichier source C++ pour la classe PERSON dans une application scolaire simple :
// personne.cpp // ---------- #include "personne.h" PERSONNE :: PERSONNE ( const char * nom ) { ceci -> nom = nom ; }
Voici un fichier d'en-tête C++ pour la classe STUDENT dans une application scolaire simple :
// étudiant.h // --------- #ifndef ÉTUDIANT_H #define ÉTUDIANT_H #include "personne.h" #include "note.h" // Un ÉTUDIANT est un sous-ensemble de PERSONNE. // -------------------------------- class ÉTUDIANT : public PERSONNE { public : ÉTUDIANT ( const char * nom ); GRADE * note ; }; #endif
Voici un fichier source C++ pour la classe STUDENT dans une application scolaire simple :
// étudiant.cpp // ----------- #include "étudiant.h" #include "personne.h" STUDENT :: STUDENT ( const char * name ) : // Exécute le constructeur de la superclasse PERSON. // ------------------------------------------------- PERSON ( name ) { // Rien d'autre à faire. // ------------------- }
Voici un programme pilote pour démonstration :
// student_dvr.cpp // --------------- #include <iostream> #include "student.h" int main ( void ) { ÉTUDIANT * étudiant = nouvel ÉTUDIANT ( "L'étudiant" ); étudiant -> note = nouvelle GRADE ( 'a' ); std :: cout // Notez que l'étudiant hérite du nom de la PERSONNE << étudiant -> nom << ": Note numérique = " << étudiant -> note -> numérique << " " ; return 0 ; }
Voici un makefile pour tout compiler :
# makefile # -------- tous : student_dvr nettoyer : rm student_dvr *.o student_dvr : student_dvr . cpp grade . o étudiant . o personne . o c++ student_dvr.cpp grade.o étudiant.o personne.o -o student_dvr note.o : note.cpp note.h c ++ -c note.cpp étudiant.o : étudiant . cpp étudiant . h c++ -c étudiant.cpp personne.o : personne . cpp personne . h c++ -c personne.cpp
Langages déclaratifs
Les langages impératifs ont une critique majeure : l'affectation d'une expression à une variable non locale peut produire un effet secondaire inattendu . Les langages déclaratifs omettent généralement l'instruction d'affectation et le flux de contrôle. Ils décrivent quel calcul doit être effectué et non comment le calculer. Deux grandes catégories de langages déclaratifs sont les langages fonctionnels et les langages logiques .
Le principe d'un langage fonctionnel est d'utiliser le calcul lambda comme guide pour une sémantique bien définie . En mathématiques, une fonction est une règle qui associe des éléments d'une expression à une plage de valeurs . Considérons la fonction :
times_10(x) = 10 * x
L' expression
10 * x est mappée par la fonction times_10()sur une plage de valeurs . L'une des valeurs est 20. Cela se produit lorsque x est 2. Ainsi, l'application de la fonction s'écrit mathématiquement comme suit :
times_10(2) = 20
Un compilateur de langage fonctionnel ne stockera pas cette valeur dans une variable. Au lieu de cela, il poussera la valeur sur la pile de l'ordinateur avant de réinitialiser le compteur du programme à la fonction appelante. La fonction appelante extraira ensuite la valeur de la pile.
Les langages impératifs prennent en charge les fonctions. Par conséquent, la programmation fonctionnelle peut être réalisée dans un langage impératif, si le programmeur fait preuve de discipline. Cependant, un langage fonctionnel imposera cette discipline au programmeur par le biais de sa syntaxe. Les langages fonctionnels ont une syntaxe adaptée pour mettre l'accent sur le quoi .
Un programme fonctionnel est développé avec un ensemble de fonctions primitives suivies d'une seule fonction pilote. Considérez l' extrait :
function max( a, b ){/* code omitted */}
function min( a, b ){/* code omitted */}
function range( a, b, c ) {
return max( a, max( b, c ) ) - min( a, min( b, c ) );
}
Les primitives sont max()et min(). La fonction du pilote est range(). Exécution :
put( range( 10, 4, 7) );produira 6.
Les langages fonctionnels sont utilisés dans la recherche en informatique pour explorer de nouvelles fonctionnalités linguistiques. De plus, leur absence d'effets secondaires les a rendus populaires dans la programmation parallèle et la programmation concurrente . Cependant, les développeurs d'applications préfèrent les fonctionnalités orientées objet des langages impératifs .
Zézayer
Lisp (1958) signifie « LISt Processor ». Il est conçu pour traiter des listes . Une structure complète des données est formée en construisant des listes de listes. En mémoire, une structure de données arborescente est construite. En interne, la structure arborescente se prête bien aux fonctions récursives . éléments séparés par des espaces entre parenthèses. Ce qui suit est une liste de trois éléments. Les deux premiers éléments sont eux-mêmes des listes de deux éléments :
((A B) (HELLO WORLD) 94)
Lisp possède des fonctions permettant d'extraire et de reconstruire des éléments. La fonction head()renvoie une liste contenant le premier élément de la liste. La fonction tail()renvoie une liste contenant tout sauf le premier élément. La fonction cons()renvoie une liste qui est la concaténation d'autres listes. Par conséquent, l'expression suivante renverra la liste x:
cons(head(x), tail(x))
L'un des inconvénients de Lisp est que lorsque de nombreuses fonctions sont imbriquées, les parenthèses peuvent sembler déroutantes. Les environnements Lisp modernes aident à garantir la correspondance des parenthèses. Par ailleurs, Lisp prend en charge les opérations de langage impératives de l'instruction d'affectation et des boucles goto. De plus, Lisp ne se préoccupe pas du type de données des éléments au moment de la compilation. Au lieu de cela, il attribue (et peut réattribuer) les types de données au moment de l'exécution . L'attribution du type de données au moment de l'exécution est appelée liaison dynamique . Alors que la liaison dynamique augmente la flexibilité du langage, les erreurs de programmation peuvent persister jusqu'à tard dans le processus de développement du logiciel .
L'écriture de programmes Lisp volumineux, fiables et lisibles nécessite une réflexion préalable. S'il est correctement planifié, le programme peut être beaucoup plus court qu'un programme équivalent en langage impératif . Lisp est largement utilisé en intelligence artificielle . Cependant, son utilisation n'a été acceptée que parce qu'il comporte des opérations de langage impératif , ce qui rend possibles des effets secondaires imprévus.
ML
ML (1973) signifie « Meta Language ». ML vérifie que seules les données du même type sont comparées entre elles. Par exemple, cette fonction a un paramètre d'entrée (un entier) et renvoie un entier :
moments amusants_10 ( n : int ) : int = 10 * n ;
ML n'est pas excentrique comme Lisp . Voici une application de times_10():
fois_10 2
Il renvoie « 20 : int ». (Les résultats et le type de données sont tous deux renvoyés.)
Comme Lisp , ML est conçu pour traiter des listes. Contrairement à Lisp , chaque élément est du même type de données. De plus, ML attribue le type de données d'un élément au moment de la compilation . L'attribution du type de données au moment de la compilation est appelée liaison statique . La liaison statique augmente la fiabilité car le compilateur vérifie le contexte des variables avant qu'elles ne soient utilisées.
Prologue
Prolog (1972) signifie « PROgramming in LOGic ». C'est un langage de programmation logique , basé sur la logique formelle . Le langage a été développé par Alain Colmerauer et Philippe Roussel à Marseille, en France. Il s'agit d'une implémentation de la résolution de clauses définies linéaires sélectives , lancée par Robert Kowalski et d'autres à l' Université d'Édimbourg .
Les éléments constitutifs d'un programme Prolog sont des faits et des règles . Voici un exemple simple :
chat ( tom ). % tom est un chat souris ( jerry ). % jerry est une souris animal ( X ) :- chat ( X ). % chaque chat est un animal animal ( X ) : - souris ( X ). % chaque souris est un animal grand ( X ) :- chat ( X ). % chaque chat est grand petit ( X ) :- souris ( X ). % chaque souris est petite manger ( X , Y ) :- souris ( X ), fromage ( Y ). % chaque souris mange chaque fromage manger ( X , Y ) :- grand ( X ), petit ( Y ). % chaque gros animal mange chaque petit animal
Une fois tous les faits et toutes les règles saisis, une question peut être posée :
- Est-ce que Tom mangera Jerry ?
?- manger ( tom , jerry ). vrai
L'exemple suivant montre comment Prolog convertira une note alphabétique en sa valeur numérique :
numeric_grade ( 'A' , 4 ). numeric_grade ( 'B' , 3 ). numeric_grade ( 'C' , 2 ). numeric_grade ( 'D' , 1 ). numeric_grade ( 'F' , 0 ). numeric_grade ( X , - 1 ) :- pas X = 'A' , pas X = 'B' , pas X = 'C' , pas X = 'D' , pas X = 'F' . grade ( 'L'étudiant' , 'A' ).
?- grade ( 'L'étudiant' , X ), numeric_grade ( X , Y ). X = 'A' , Y = 4
Voici un exemple complet :
1) Tous les dragons projettent du feu, ou de manière équivalente, une chose projette du feu si la chose est un dragon :
billows_fire ( X ) :- est_un_dragon ( X ).
2) Une créature émet des gerbes de feu si l'un de ses parents émet des gerbes de feu :
billows_fire ( X ) :- est_une_créature ( X ), est_un_parent_de ( Y , X ), billows_fire ( Y ).
3) Une chose X est le parent d'une chose Y si X est la mère de Y ou X est le père de Y :
est_un_parent_de ( X , Y ):- est_la_mère_de ( X , Y ). est_un_parent_de ( X , Y ):- est_le_père_de ( X , Y ).
4) Une chose est une créature si la chose est un dragon :
est_une_créature ( X ) :- est_un_dragon ( X ).
5) Norberta est un dragon et Puff est une créature. Norberta est la mère de Puff.
est_un_dragon ( norberta ). est_une_créature ( puff ). est_la_mère_de ( norberta , puff ).
La règle (2) est une définition récursive (inductive). Elle peut être comprise de manière déclarative, sans qu'il soit nécessaire de comprendre comment elle est exécutée.
La règle (3) montre comment les fonctions sont représentées à l'aide de relations. Ici, les fonctions mère et père garantissent que chaque individu n'a qu'une seule mère et qu'un seul père.
Prolog est un langage non typé. Néanmoins, l'héritage peut être représenté à l'aide de prédicats. La règle (4) affirme qu'une créature est une super-classe d'un dragon.
Les réponses aux questions sont obtenues à l'aide d'un raisonnement à rebours . Étant donné la question :
?- des vagues de feu ( X ).
Prolog génère deux réponses :
X = norberta X = bouffée
Les applications pratiques de Prolog sont la représentation des connaissances et la résolution de problèmes en intelligence artificielle .
Programmation orientée objet
La programmation orientée objet est une méthode de programmation permettant d'exécuter des opérations ( fonctions ) sur des objets . L'idée de base est de regrouper les caractéristiques d'un phénomène dans un conteneur d'objets et de donner un nom au conteneur. Les opérations sur le phénomène sont également regroupées dans le conteneur. La programmation orientée objet s'est développée en combinant le besoin de conteneurs et le besoin d' une programmation fonctionnelle sûre . Cette méthode de programmation ne doit pas nécessairement se limiter à un langage orienté objet . Dans un langage orienté objet, un conteneur d'objets est appelé une classe . Dans un langage non orienté objet, une structure de données (également appelée enregistrement ) peut devenir un conteneur d'objets. Pour transformer une structure de données en conteneur d'objets, des opérations doivent être écrites spécifiquement pour la structure. La structure résultante est appelée un type de données abstrait . Cependant, l'héritage sera manquant. Néanmoins, cette lacune peut être surmontée.
Voici un fichier d'en-tête de langage de programmation C pour le type de données abstrait GRADE dans une application scolaire simple :
/* note.h */ /* ------- */ /* Utilisé pour permettre à plusieurs fichiers sources d'inclure */ /* ce fichier d'en-tête sans erreurs de duplication. */ /* ---------------------------------------------- */ #ifndef GRADE_H #define GRADE_H typedef struct { char lettre ; } GRADE ; /* Constructeur */ /* ----------- */ GRADE * grade_new ( char letter ); int grade_numeric ( char lettre ); #endif
La grade_new()fonction exécute le même algorithme que l’ opération de constructeur C++ .
Voici un fichier source de langage de programmation C pour le type de données abstrait GRADE dans une application scolaire simple :
/* note.c */ /* ------- */ #include "note.h" GRADE * grade_new ( lettre de caractère ) { GRADE * grade ; /* Allouer de la mémoire de tas */ /* -------------------- */ if ( ! ( grade = calloc ( 1 , sizeof ( GRADE ) ) ) ) { fprintf ( stderr , "ERREUR dans %s/%s/%d : calloc() a renvoyé une valeur vide. " , __FILE__ , __FUNCTION__ , __LINE__ ); exit ( 1 ); } note -> lettre = lettre ; retour note ; } int grade_numeric ( char letter ) { if ( ( letter == 'A' || letter == 'a' ) ) renvoie 4 ; else if ( ( letter == 'B' || letter == 'b' ) ) renvoie 3 ; else if ( ( letter == 'C' || letter == 'c' ) ) renvoie 2 ; else if ( ( letter == 'D' || letter == 'd' ) ) renvoie 1 ; else if ( ( letter == 'F' || letter == 'f' ) ) renvoie 0 ; else return -1 ; }
Dans le constructeur, la fonction calloc()est utilisée à la place de malloc()car chaque cellule mémoire sera mise à zéro.
Voici un fichier d'en-tête de langage de programmation C pour le type de données abstrait PERSON dans une application scolaire simple :
/* personne.h */ /* -------- */ #ifndef PERSON_H #define PERSON_H typedef struct { char * nom ; } PERSONNE ; /* Constructeur */ /* ----------- */ PERSONNE * nouvelle_personne ( char * nom ); #endif
Voici un fichier source de langage de programmation C pour le type de données abstrait PERSON dans une application scolaire simple :
/* personne.c */ /* -------- */ #include "personne.h" PERSONNE * personne_nouvelle ( char * nom ) { PERSONNE * personne ; si ( ! ( personne = calloc ( 1 , sizeof ( PERSONNE ) ) ) ) { fprintf ( stderr , "ERREUR dans %s/%s/%d : calloc() a renvoyé une valeur vide. " , __FILE__ , __FUNCTION__ , __LINE__ ); exit ( 1 ); } personne -> nom = nom ; renvoie personne ; }
Voici un fichier d'en-tête de langage de programmation C pour le type de données abstrait STUDENT dans une application scolaire simple :
/* étudiant.h */ /* --------- */ #ifndef ÉTUDIANT_H #define ÉTUDIANT_H #include "personne.h" #include "note.h" typedef struct { /* Un ÉTUDIANT est un sous-ensemble de PERSONNE. */ /* -------------------------------- */ PERSONNE * personne ; NOTE * note ; } ÉTUDIANT ; /* Constructeur */ /* ----------- */ ÉTUDIANT * student_new ( char * nom ); #endif
Voici un fichier source de langage de programmation C pour le type de données abstrait STUDENT dans une application scolaire simple :
/* étudiant.c */ /* --------- */ #include "étudiant.h" #include "personne.h" ÉTUDIANT * étudiant_nouveau ( char * nom ) { ÉTUDIANT * étudiant ; si ( ! ( student = calloc ( 1 , sizeof ( STUDENT ) ) ) ) { fprintf ( stderr , "ERREUR dans %s/%s/%d : calloc() a renvoyé une valeur vide. " , __FILE__ , __FUNCTION__ , __LINE__ ); exit ( 1 ); } /* Exécuter le constructeur de la superclasse PERSON. */ /* ------------------------------------------------- */ student -> person = person_new ( name ); return student ; }
Voici un programme pilote pour démonstration :
/* student_dvr.c */ /* ------------- */ #include <stdio.h> #include "student.h" int main ( void ) { ÉTUDIANT * étudiant = étudiant_new ( "L'étudiant" ); étudiant -> note = note_new ( 'a' ); printf ( "%s: Note numérique = %d " , /* Alors qu'un sous-ensemble existe, l'héritage n'existe pas. */ student -> person -> name , /* La programmation fonctionnelle exécute des fonctions juste-à-temps (JIT) */ grade_numeric ( student -> grade -> letter ) ); retourner 0 ; }
Voici un makefile pour tout compiler :
# makefile # -------- tous : student_dvr nettoyer : rm student_dvr *.o student_dvr : student_dvr . c grade . o student . o personne . o gcc student_dvr.c grade.o student.o personne.o -o student_dvr note.o : note.c note.h gcc -c note.c étudiant.o : étudiant . c étudiant . h gcc -c étudiant.c personne.o : personne . c personne . h gcc -c personne.c
La stratégie formelle pour construire des objets orientés objet est la suivante :
- Identifiez les objets. Il s'agira probablement de noms.
- Identifiez les attributs de chaque objet. Qu'est-ce qui permet de décrire l'objet ?
- Identifiez les actions de chaque objet. Il s'agira probablement de verbes.
- Identifiez les relations d'objet à objet. Il s'agira très probablement de verbes.
Par exemple:
- Une personne est un être humain identifié par un nom.
- Une note est une réussite identifiée par une lettre.
- Un étudiant est une personne qui obtient une note.
Syntaxe et sémantique

La syntaxe d'un programme informatique est une liste de règles de production qui forment sa grammaire . La grammaire d'un langage de programmation place correctement ses déclarations , expressions et instructions . sémantique d'un langage complète la syntaxe . La sémantique décrit les significations attachées à diverses constructions syntaxiques. Une construction syntaxique peut nécessiter une description sémantique car une règle de production peut avoir une interprétation invalide. De plus, différentes langues peuvent avoir la même syntaxe ; cependant, leurs comportements peuvent être différents.
La syntaxe d'une langue est décrite formellement en énumérant les règles de production. Alors que la syntaxe d'une langue naturelle est extrêmement compliquée, un sous-ensemble de la langue anglaise peut avoir cette liste de règles de production :
- une phrase est composée d'un syntagme nominal suivi d'un syntagme verbal ;
- un groupe nominal est composé d'un article suivi d'un adjectif suivi d'un nom ;
- un syntagme verbal est composé d'un verbe suivi d'un syntagme nominal ;
- un article est « le » ;
- un adjectif est « grand » ou
- un adjectif est « petit » ;
- un nom est « chat » ou
- un nom est « souris » ;
- un verbe est « mange » ;
Les mots en gras sont appelés non-terminaux . Les mots entre « guillemets simples » sont appelés terminaux .
À partir de cette liste de règles de production, des phrases complètes peuvent être formées en utilisant une série de remplacements. Le processus consiste à remplacer les non-terminaux par un non-terminal valide ou un terminal valide . Le processus de remplacement se répète jusqu'à ce qu'il ne reste que des terminaux . Une phrase valide est :
- phrase
- groupe nominal groupe verbal
- article adjectif nom verbe-locution
- le nom adjectif phrase verbale
- le grand nom -locution verbale
- le verbe du gros chat
- le verbe du gros chat
- le gros chat mange un groupe nominal
- le gros chat mange article adjectif nom
- le gros chat mange le nom adjectif
- le gros chat mange le petit nom
- le gros chat mange la petite souris
Cependant, une autre combinaison donne lieu à une phrase invalide :
- la petite souris mange le gros chat
Une sémantique est donc nécessaire pour décrire correctement le sens d’une activité alimentaire .
Une méthode de liste de règles de production est appelée la forme Backus-Naur (BNF). La BNF décrit la syntaxe d'un langage et possède elle-même une syntaxe . Cette définition récursive est un exemple de méta-langage . La syntaxe de la BNF comprend :
::=ce qui se traduit par est composé de a[n] lorsqu'un non-terminal est à sa droite. Cela se traduit par est lorsqu'un terminal est à sa droite.|ce qui se traduit par ou .<et>qui entourent les non-terminaux .
En utilisant BNF, un sous-ensemble de la langue anglaise peut avoir cette liste de règles de production :
< phrase > ::= < syntagme nominal >< syntagme verbal > < syntagme nominal > ::= < article >< adjectif >< nom > < syntagme verbal > ::= < verbe >< syntagme nominal > < article > ::= le < adjectif > ::= grand | petit < nom > ::= chat | souris < verbe > ::= mange
En utilisant BNF, un entier signé a la liste de règles de production :
< entier signé > ::= < signe >< entier > < signe > ::= + | - < entier > ::= < chiffre > | < chiffre >< entier > < chiffre > ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Notez la règle de production récursive :
< entier > ::= < chiffre > | < chiffre >< entier >
Cela permet un nombre infini de possibilités. Il est donc nécessaire de disposer d'une sémantique pour décrire une limitation du nombre de chiffres.
Notez la possibilité d'un zéro non significatif dans les règles de production :
< entier > :: = < chiffre > | < chiffre > < entier > < chiffre > ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Par conséquent, une sémantique est nécessaire pour décrire que les zéros non significatifs doivent être ignorés.
Il existe deux méthodes formelles permettant de décrire la sémantique . Il s'agit de la sémantique dénotationnelle et de la sémantique axiomatique .
Ingénierie logicielle et programmation informatique

L'ingénierie logicielle est une variété de techniques permettant de produire des programmes informatiques de qualité . La programmation informatique est le processus d'écriture ou de modification du code source . Dans un environnement formel, un analyste de systèmes recueillera des informations auprès des gestionnaires sur tous les processus de l'organisation à automatiser. Ce professionnel prépare ensuite un plan détaillé pour le système nouveau ou modifié. Le plan est analogue au plan directeur d'un architecte.
Objectifs de performance
L'analyste des systèmes a pour objectif de fournir la bonne information à la bonne personne au bon moment. Les facteurs critiques pour atteindre cet objectif sont les suivants :
- La qualité du résultat. Le résultat est-il utile à la prise de décision ?
- L'exactitude des résultats. Reflète-t-elle la situation réelle ?
- Le format de la sortie. La sortie est-elle facilement compréhensible ?
- La rapidité de la production. Les informations sensibles au temps sont importantes lors de la communication avec le client en temps réel.
Objectifs de coûts
L’atteinte des objectifs de performance doit être équilibrée avec tous les coûts, notamment :
- Coûts de développement.
- L'unicité a un prix. Un système réutilisable peut être coûteux. Cependant, il peut être préférable à un système à usage limité.
- Coûts du matériel.
- Coûts d'exploitation.
L’application d’un processus de développement de systèmes atténuera l’axiome suivant : plus une erreur est détectée tard dans le processus, plus sa correction est coûteuse.
Modèle de cascade
Le modèle en cascade est une mise en œuvre d'un processus de développement de systèmes . Comme l'indique l' étiquette en cascade , les phases de base se chevauchent :
- La phase d’enquête vise à comprendre le problème sous-jacent.
- La phase d'analyse consiste à comprendre les solutions possibles.
- La phase de conception consiste à planifier la meilleure solution.
- La phase d'implémentation consiste à programmer la meilleure solution.
- La phase de maintenance dure toute la durée de vie du système. Des modifications du système après son déploiement peuvent être nécessaires. Des défauts peuvent exister, notamment des défauts de spécification, de conception ou de codage. Des améliorations peuvent être nécessaires. Une adaptation peut être nécessaire pour réagir à un environnement changeant.
Programmeur informatique
Un programmeur informatique est un spécialiste chargé d'écrire ou de modifier le code source pour mettre en œuvre le plan détaillé. Une équipe de programmation est probablement nécessaire car la plupart des systèmes sont trop grands pour être réalisés par un seul programmeur. Cependant, l'ajout de programmeurs à un projet ne raccourcit pas nécessairement le temps d'achèvement. Au contraire, cela peut diminuer la qualité du système. Pour être efficaces, les modules de programme doivent être définis et distribués aux membres de l'équipe. De plus, les membres de l'équipe doivent interagir les uns avec les autres de manière significative et efficace.
Les programmeurs informatiques peuvent programmer en petits modules : programmer dans un seul module. Il y a de fortes chances qu'un module exécute des modules situés dans d'autres fichiers de code source. Par conséquent, les programmeurs informatiques peuvent programmer en grands modules : programmer des modules afin qu'ils puissent se coupler efficacement les uns aux autres. La programmation en grands modules comprend la contribution à l' interface de programmation d'application (API).
Modules du programme
La programmation modulaire est une technique permettant d'affiner les programmes en langage impératif . Les programmes affinés peuvent réduire la taille du logiciel, séparer les responsabilités et ainsi atténuer le vieillissement du logiciel . Un module de programme est une séquence d'instructions qui sont délimitées dans un bloc et identifiées ensemble par un nom. Les modules ont une fonction , un contexte et une logique :
- La fonction d’un module est ce qu’il fait.
- Le contexte d'un module correspond aux éléments sur lesquels l'opération est exécutée.
- La logique d’un module est la manière dont il exécute la fonction.
Le nom du module doit d'abord être dérivé de sa fonction , puis de son contexte . Sa logique ne doit pas faire partie du nom. Par exemple, function compute_square_root( x )ou function compute_square_root_integer( i : integer )sont des noms de module appropriés. Cependant, function compute_square_root_by_division( x )ce n'est pas le cas.
Le degré d'interaction au sein d'un module est son niveau de cohésion . La cohésion est un jugement de la relation entre le nom d'un module et sa fonction . Le degré d'interaction entre les modules est le niveau de couplage . Le couplage est un jugement de la relation entre le contexte d'un module et les éléments sur lesquels il est exécuté.
Cohésion
Les niveaux de cohésion du pire au meilleur sont :
- Cohésion fortuite : Un module a une cohésion fortuite s'il exécute plusieurs fonctions et que ces fonctions sont totalement indépendantes. Par exemple,
function read_sales_record_print_next_line_convert_to_float(). La cohésion fortuite se produit dans la pratique si la direction applique des règles stupides. Par exemple, « Chaque module aura entre 35 et 50 instructions exécutables. » - Cohésion logique : Un module est dit cohérent s'il dispose d'une série de fonctions, mais qu'une seule d'entre elles est exécutée. Par exemple,
function perform_arithmetic( perform_addition, a, b ). - Cohésion temporelle : Un module a une cohésion temporelle s'il remplit des fonctions liées au temps. Un exemple,
function initialize_variables_and_open_files(). Un autre exemple,stage_one(),stage_two(), ... - Cohésion procédurale : Un module est doté d'une cohésion procédurale s'il exécute plusieurs fonctions vaguement liées. Par exemple,
function read_part_number_update_employee_record(). - Cohésion communicationnelle : Un module présente une cohésion communicationnelle s'il remplit plusieurs fonctions étroitement liées. Par exemple,
function read_part_number_update_sales_record(). - Cohésion informationnelle : Un module a une cohésion informationnelle s'il exécute plusieurs fonctions, mais chaque fonction a ses propres points d'entrée et de sortie. De plus, les fonctions partagent la même structure de données. Les classes orientées objet fonctionnent à ce niveau.
- Cohésion fonctionnelle : un module possède une cohésion fonctionnelle s'il atteint un objectif unique en travaillant uniquement sur des variables locales. De plus, il peut être réutilisable dans d'autres contextes.
Couplage
Les niveaux de couplage du pire au meilleur sont :
- Couplage de contenu : Un module a un couplage de contenu s'il modifie une variable locale d'une autre fonction. COBOL faisait cela avec le verbe alter .
- Couplage commun : un module a un couplage commun s'il modifie une variable globale.
- Couplage de contrôle : un module possède un couplage de contrôle si un autre module peut modifier son flux de contrôle . Par exemple,
perform_arithmetic( perform_addition, a, b ). Au lieu de cela, le contrôle doit être sur la composition de l'objet renvoyé. - Couplage de tampon : Un module possède un couplage de tampon si un élément d'une structure de données passée en paramètre est modifié. Les classes orientées objet fonctionnent à ce niveau.
- Couplage de données : Un module possède un couplage de données si tous ses paramètres d'entrée sont nécessaires et qu'aucun d'entre eux n'est modifié. De plus, le résultat de la fonction est renvoyé sous la forme d'un objet unique.
Analyse des flux de données

L'analyse des flux de données est une méthode de conception utilisée pour obtenir des modules de cohésion fonctionnelle et de couplage de données . L'entrée de la méthode est un diagramme de flux de données . Un diagramme de flux de données est un ensemble d'ovales représentant des modules. Le nom de chaque module est affiché à l'intérieur de son ovale. Les modules peuvent être au niveau exécutable ou au niveau fonctionnel.
Le diagramme comporte également des flèches reliant les modules entre eux. Les flèches pointant vers les modules représentent un ensemble d'entrées. Chaque module ne doit avoir qu'une seule flèche pointant vers l'extérieur pour représenter son objet de sortie unique. (En option, une flèche d'exception supplémentaire pointe vers l'extérieur.) Une chaîne d'ovales transmettra un algorithme complet . Les modules d'entrée doivent démarrer le diagramme. Les modules d'entrée doivent se connecter aux modules de transformation. Les modules de transformation doivent se connecter aux modules de sortie.
Catégories fonctionnelles

Les programmes informatiques peuvent être classés selon des critères fonctionnels. Les principales catégories fonctionnelles sont les logiciels d'application et les logiciels système . Le logiciel système comprend le système d'exploitation , qui associe le matériel informatique au logiciel d'application. Le but du système d'exploitation est de fournir un environnement dans lequel le logiciel d'application s'exécute de manière pratique et efficace. Les logiciels d'application et les logiciels système exécutent tous deux des programmes utilitaires . Au niveau matériel, un programme de microcode contrôle les circuits dans toute l' unité centrale de traitement .
Logiciel d'application
Les logiciels d'application sont la clé pour libérer le potentiel du système informatique. Les logiciels d'application d'entreprise regroupent les applications de comptabilité, de personnel, de clientèle et de fournisseurs. Les exemples incluent les logiciels de planification des ressources de l'entreprise , de gestion de la relation client et de gestion de la chaîne d'approvisionnement .
Les applications d'entreprise peuvent être développées en interne sous forme de logiciel propriétaire unique . Elles peuvent également être achetées sous forme de logiciel prêt à l'emploi . Le logiciel acheté peut être modifié pour fournir un logiciel personnalisé . Si l'application est personnalisée, les ressources de l'entreprise sont alors utilisées ou externalisées. Le développement de logiciel externalisé peut être assuré par le fournisseur de logiciel d'origine ou par un développeur tiers.
Les avantages potentiels des logiciels internes sont que les fonctionnalités et les rapports peuvent être développés exactement selon les spécifications. La direction peut également être impliquée dans le processus de développement et offrir un niveau de contrôle. La direction peut décider de contrecarrer une nouvelle initiative d'un concurrent ou de mettre en œuvre une exigence d'un client ou d'un fournisseur. Une fusion ou une acquisition peut nécessiter des changements de logiciel d'entreprise. Les inconvénients potentiels des logiciels internes sont que les coûts en temps et en ressources peuvent être importants. De plus, des risques concernant les fonctionnalités et les performances peuvent se profiler.
Les avantages potentiels des logiciels prêts à l'emploi sont les suivants : les coûts initiaux sont identifiables, les besoins de base doivent être satisfaits et leurs performances et leur fiabilité ont fait leurs preuves. Les inconvénients potentiels des logiciels prêts à l'emploi sont qu'ils peuvent avoir des fonctionnalités inutiles qui déroutent les utilisateurs finaux, qu'ils peuvent manquer de fonctionnalités dont l'entreprise a besoin et que le flux de données peut ne pas correspondre aux processus de travail de l'entreprise.
Une approche permettant d'obtenir de manière économique une application d'entreprise personnalisée consiste à faire appel à un fournisseur de services d'application . Les entreprises spécialisées fournissent le matériel, les logiciels personnalisés et le support utilisateur final. Elles peuvent accélérer le développement de nouvelles applications car elles disposent d'un personnel qualifié en systèmes d'information. Le plus grand avantage est que cela libère les ressources internes de la gestion du personnel et des projets informatiques complexes. De nombreux fournisseurs de services d'application ciblent les petites entreprises à croissance rapide disposant de ressources informatiques limitées. En revanche, les grandes entreprises dotées de systèmes majeurs auront probablement leur infrastructure technique en place. L'un des risques est de devoir faire confiance à une organisation externe pour les informations sensibles. Un autre risque est de devoir faire confiance à la fiabilité de l'infrastructure du fournisseur.
Système opérateur

Un système d'exploitation est le logiciel de bas niveau qui prend en charge les fonctions de base d'un ordinateur, telles que la planification des processus et le contrôle des périphériques .
Dans les années 1950, le programmeur, qui était également l'opérateur, écrivait un programme et l'exécutait. Une fois le programme exécuté, le résultat était imprimé ou perforé sur une bande de papier ou des cartes pour un traitement ultérieur. Le plus souvent, le programme ne fonctionnait pas. Le programmeur regardait alors les voyants de la console et jouait avec les interrupteurs de la console. S'il avait moins de chance, une impression de mémoire était réalisée pour une étude plus approfondie. Dans les années 1960, les programmeurs ont réduit le temps perdu en automatisant le travail de l'opérateur. Un programme appelé système d'exploitation était conservé en permanence dans l'ordinateur.
Le terme système d'exploitation peut faire référence à deux niveaux de logiciel. Le système d'exploitation peut faire référence au programme du noyau qui gère les processus , la mémoire et les périphériques . Plus généralement, le système d'exploitation peut faire référence à l'ensemble du logiciel central. Le package comprend un programme du noyau, un interpréteur de ligne de commande , une interface utilisateur graphique , des programmes utilitaires et un éditeur .
Programme du noyau

L'objectif principal du noyau est de gérer les ressources limitées d'un ordinateur :
- Le programme du noyau doit effectuer la planification des processus , également connue sous le nom de changement de contexte . Le noyau crée un bloc de contrôle de processus lorsqu'un programme informatique est sélectionné pour exécution . Cependant, un programme en cours d'exécution n'obtient un accès exclusif à l' unité centrale de traitement que pendant une tranche de temps . Pour donner à chaque utilisateur l' apparence d'un accès continu , le noyau préempte rapidement chaque bloc de contrôle de processus pour en exécuter un autre. L'objectif des développeurs de systèmes est de minimiser la latence de répartition .

- Le programme du noyau doit effectuer la gestion de la mémoire .
- Lorsque le noyau charge initialement un exécutable en mémoire, il divise l'espace d'adressage de manière logique en régions . Le noyau maintient une table de régions principales et de nombreuses tables de régions par processus (pregion) - une pour chaque processus en cours d'exécution . Ces tables constituent l' espace d'adressage virtuel . La table de régions principales est utilisée pour déterminer où se trouve son contenu dans la mémoire physique . Les tables de pregion permettent à chaque processus d'avoir sa propre pregion de programme (texte), pregion de données et pregion de pile.
- La prégion du programme stocke les instructions de la machine. Étant donné que les instructions de la machine ne changent pas, la prégion du programme peut être partagée par de nombreux processus du même exécutable.
- Pour économiser du temps et de la mémoire, le noyau peut charger uniquement des blocs d'instructions d'exécution à partir du lecteur de disque, et non l'intégralité du fichier d'exécution.
- Le noyau est responsable de la traduction des adresses virtuelles en adresses physiques . Le noyau peut demander des données au contrôleur de mémoire et, à la place, recevoir une erreur de page . Si tel est le cas, le noyau accède à l' unité de gestion de la mémoire pour remplir la région de données physiques et traduire l'adresse.
- Le noyau alloue de la mémoire à partir du tas à la demande d'un processus. Lorsque le processus a terminé avec la mémoire, il peut demander qu'elle soit libérée . Si le processus se termine sans demander que toute la mémoire allouée soit libérée, le noyau effectue alors un nettoyage des mémoires pour libérer la mémoire.
- Le noyau garantit également qu'un processus n'accède qu'à sa propre mémoire, et non à celle du noyau ou d'autres processus.
- Le programme du noyau doit effectuer la gestion du système de fichiers . Le noyau dispose d'instructions pour créer, récupérer, mettre à jour et supprimer des fichiers.
- Le programme du noyau doit gérer les périphériques . Le noyau fournit des programmes pour standardiser et simplifier l'interface avec la souris, le clavier, les lecteurs de disque, les imprimantes et d'autres périphériques. De plus, le noyau doit arbitrer l'accès à un périphérique si deux processus le demandent en même temps.
- Le programme du noyau doit effectuer la gestion du réseau . Le noyau transmet et reçoit des paquets pour le compte des processus. L'un des services clés consiste à trouver un itinéraire efficace vers le système cible.
- Le programme du noyau doit fournir des fonctions au niveau du système que les programmeurs peuvent utiliser.
- Les programmeurs accèdent aux fichiers via une interface relativement simple qui exécute à son tour une interface d'E/S de bas niveau relativement complexe. L'interface de bas niveau comprend la création de fichiers, les descripteurs de fichiers , la recherche de fichiers, la lecture physique et l'écriture physique.
- Les programmeurs créent des processus via une interface relativement simple qui exécute à son tour une interface de bas niveau relativement complexe.
- Les programmeurs effectuent des opérations arithmétiques de date/heure via une interface relativement simple qui, à son tour, exécute une interface temporelle de bas niveau relativement complexe.
- Le programme du noyau doit fournir un canal de communication entre les processus en cours d'exécution. Pour un système logiciel de grande taille, il peut être souhaitable de concevoir le système en processus plus petits. Les processus peuvent communiquer entre eux en envoyant et en recevant des signaux .
À l'origine, les systèmes d'exploitation étaient programmés en assembleur ; cependant, les systèmes d'exploitation modernes sont généralement écrits dans des langages de niveau supérieur comme C , Objective-C et Swift .
Programme utilitaire
Un programme utilitaire est conçu pour faciliter l'administration du système et l'exécution des logiciels. Les systèmes d'exploitation exécutent des programmes utilitaires matériels pour vérifier l'état des lecteurs de disque, de la mémoire, des haut-parleurs et des imprimantes. Un programme utilitaire peut optimiser le placement d'un fichier sur un disque encombré. Les programmes utilitaires système surveillent les performances du matériel et du réseau. Lorsqu'une mesure se situe en dehors d'une plage acceptable, une alerte de déclenchement est générée.
Les programmes utilitaires incluent des programmes de compression afin que les fichiers de données soient stockés sur moins d'espace disque. Les programmes compressés permettent également de gagner du temps lorsque les fichiers de données sont transmis sur le réseau. Les programmes utilitaires peuvent trier et fusionner des ensembles de données. Les programmes utilitaires détectent les virus informatiques .
Programme de microcode





Un programme de microcode est l'interpréteur de niveau inférieur qui contrôle le chemin de données des ordinateurs pilotés par logiciel. (Les progrès du matériel ont fait migrer ces opérations vers des circuits d'exécution matériels .) Les instructions de microcode permettent au programmeur d'implémenter plus facilement le niveau logique numérique — le véritable matériel de l'ordinateur. Le niveau logique numérique est la frontière entre l'informatique et l'ingénierie informatique .
Une porte logique est un minuscule transistor qui peut renvoyer l'un des deux signaux : activé ou désactivé.
- Avoir un transistor forme la porte NON .
- La connexion de deux transistors en série forme la porte NAND .
- La connexion de deux transistors en parallèle forme la porte NOR .
- La connexion d'une porte NON à une porte NAND forme la porte ET .
- La connexion d'une porte NON à une porte NOR forme la porte OU .
Ces cinq portes constituent les éléments de base de l’algèbre binaire , c’est-à-dire les fonctions logiques numériques de l’ordinateur.
Les instructions de microcode sont des mnémoniques que les programmeurs peuvent utiliser pour exécuter des fonctions logiques numériques au lieu de les former en algèbre binaire. Elles sont stockées dans la mémoire de contrôle d'une unité centrale de traitement (CPU) . Ces instructions de niveau matériel déplacent les données tout au long du chemin de données .
Le cycle de micro-instruction commence lorsque le microséquenceur utilise son compteur de microprogramme pour récupérer l' instruction machine suivante dans la mémoire à accès aléatoire . L'étape suivante consiste à décoder l'instruction machine en sélectionnant la ligne de sortie appropriée vers le module matériel. L'étape finale consiste à exécuter l'instruction à l'aide de l'ensemble de portes du module matériel.

Les instructions permettant d'effectuer des opérations arithmétiques sont transmises via une unité arithmétique et logique (UAL). L'UAL dispose de circuits permettant d'effectuer des opérations élémentaires telles que l'addition, le décalage et la comparaison d'entiers. En combinant et en bouclant les opérations élémentaires via l'UAL, le processeur effectue son arithmétique complexe.
Les instructions du microcode déplacent les données entre le processeur et le contrôleur de mémoire . Les instructions du microcode du contrôleur de mémoire manipulent deux registres . Le registre d'adresse mémoire est utilisé pour accéder à l'adresse de chaque cellule mémoire. Le registre de données mémoire est utilisé pour définir et lire le contenu de chaque cellule.
Les instructions du microcode déplacent les données entre le processeur et les nombreux bus informatiques . Le bus du contrôleur de disque écrit et lit sur les disques durs . Les données sont également déplacées entre le processeur et d'autres unités fonctionnelles via le bus express d'interconnexion des composants périphériques.