Le langage de programmation C était-il considéré comme un langage de bas niveau quand il est sorti?

151

Actuellement, le C est considéré comme une langue de bas niveau , mais était-il considéré dans les années 70 comme de bas niveau? Le terme était-il même utilisé alors?

Beaucoup de langues populaires de niveau supérieur n'existaient pas avant le milieu des années 80 et au-delà. Je suis donc curieux de savoir si et comment la nature du bas niveau a changé au fil des ans.

Joeyfb
la source
13
En 1978, certains mentors de la programmation m'ont décrit le langage de montage glorifié.
Ben Crowell
7
@BenCrowell Je ne suis pas sûr de ce que «glorifié» signifie dans le contexte de votre déclaration, mais j'ai déjà eu l'idée d'appeler C un langage d'assemblage universel (= indépendant de la plate-forme) .
Melebius
13
Il est intéressant de noter que le langage C n'est pas un langage de bas niveau et qu'il l'est moins aujourd'hui qu'il ne l'était dans les années 70, car la machine abstraite de C est plus éloignée du matériel moderne que du PDP-11.
Tom
5
Lorsque vous réfléchissez à cela, vous pouvez également penser à l'origine: Unix est écrit en C, c fonctionne sous Unix et compile plusieurs unités Unix sur d'autres plates-formes. Tout ce qui n'était pas nécessaire pour compiler Unix était une surcharge inutile, et l'objectif principal de C était de rendre aussi facile que possible l'écriture / le portage d'un compilateur. Pour ce faire, c’était le niveau correct EXACT, je ne pense donc pas que le niveau C soit élevé ou bas, c’est un outil pour porter Unix qui, comme presque tous les outils Unix, est extrêmement adaptable à de nombreux problèmes.
Bill K
4
Il est à noter que le lisp a été inventé en 1958
Premier le

Réponses:

143

Pour répondre aux aspects historiques de la question:

La philosophie de conception est expliquée dans The C Programming Language écrit par Brian Kernighan et le concepteur C, Dennis Ritchie, le "K & R" dont vous avez peut-être entendu parler. La préface à la première édition dit

C n’est pas un langage "très haut niveau", ni un "grand" ...

et l'introduction dit

C est un langage relativement "bas niveau" ... C ne fournit aucune opération permettant de traiter directement des objets composites tels que des chaînes de caractères, des ensembles, des listes ou des tableaux. Aucune opération ne manipule un tableau ou une chaîne entière ...

La liste est longue avant que le texte continue:

Bien que l’absence de certaines de ces caractéristiques puisse sembler être une grave lacune, le fait de garder une langue de taille modeste présente de réels avantages.

(Je n'ai que la deuxième édition de 1988, mais le commentaire ci-dessous indique que le texte cité est le même dans la première édition de 1978.)

Alors, oui, les termes "haut niveau" et "bas niveau" étaient utilisés à l'époque, mais C était conçu pour se situer quelque part dans le spectre entre les deux. Il était possible d'écrire du code en C portable sur toutes les plates-formes matérielles, ce qui était le principal critère permettant de déterminer si un langage était considéré de haut niveau à l'époque. Cependant, C manquait de certaines caractéristiques caractéristiques des langages de haut niveau, ce qui était une décision de conception en faveur de la simplicité.

gatkin
la source
42
C'est une excellente réponse! Preuve historique vérifiable + témoignage d'un acteur le mieux informé de la signification des niveaux bas et élevé au cours de la période visée par le PO. En passant, je confirme que les citations figuraient déjà dans l'édition de 1978.
Christophe
157

Cela dépend de votre définition du langage de haut niveau et de bas niveau. Lorsque C a été développé, tout ce qui était de niveau supérieur à l’assemblage était considéré comme un langage de haut niveau. C'est une barre basse pour effacer. Plus tard, cette terminologie a évolué au point que certains considèrent aujourd'hui que même Java est un langage de bas niveau.

Même dans le paysage linguistique de haut niveau des années 70, il est utile de souligner que le C est un niveau assez bas. Le langage C est fondamentalement B plus un système de types simple, et B n’est guère plus qu’une couche de syntaxe procédurale / structurée pratique pour l’assemblage. Etant donné que le système de typage est un ajustement ultérieur au-dessus du langage B non typé, vous pouvez toujours omettre les annotations de type à certains endroits int.

C laisse délibérément de côté des fonctionnalités coûteuses ou difficiles à implémenter qui étaient déjà bien établies à l’époque, telles que

  • gestion automatique de la mémoire
  • fonctions imbriquées ou fermetures
  • POO ou coroutines de base
  • systèmes de types plus expressifs (par exemple, types à plage restreinte, types définis par l'utilisateur tels que types d'enregistrement, typage fort,…)

C a quelques caractéristiques intéressantes:

  • prise en charge de la récursivité (conséquence des variables automatiques basées sur la pile comparées aux langues dans lesquelles toutes les variables ont une durée de vie globale)
  • pointeurs de fonction
  • Les types de données définis par l'utilisateur (structures et unions) ont été implémentés peu de temps après la première publication de C.
  • La représentation sous forme de chaîne de C (pointeur sur caractères) est en réalité une énorme amélioration par rapport à B qui codait plusieurs lettres dans un seul mot machine.
  • Les fichiers d’en-tête de C étaient un hack d’efficacité pour garder les unités de compilation petites, mais fournissaient également un système de module simple.
  • Pointeurs sans restriction et arithmétique de pointeur de type assemblage, par rapport à des références plus sûres. Les pointeurs sont une fonctionnalité intrinsèquement dangereuse mais sont également très utiles pour la programmation de bas niveau.

Au moment du développement de C, d'autres langages innovants tels que COBOL, Lisp, ALGOL (dans divers dialectes), PL / I, SNOBOL, Simula et Pascal étaient déjà publiés et / ou étaient largement utilisés pour des domaines de problèmes spécifiques. Mais la plupart de ces langues existantes étaient destinées à la programmation mainframe ou étaient des projets de recherche académique. Par exemple, lorsque ALGOL-60 a été conçu pour la première fois en tant que langage de programmation universel, la technologie et l'informatique nécessaires à sa mise en œuvre n'existaient pas encore. Certains d'entre eux (certains dialectes ALGOL, PL / I, Pascal) étaient également destinés à la programmation de bas niveau, mais ils avaient tendance à avoir des compilateurs plus complexes ou étaient trop sûrs (par exemple, aucun pointeur non restreint). Pascal manque notamment d'un bon support pour les tableaux de longueur variable.

Comparé à ces langues, C rejette les fonctionnalités «élégantes» et coûteuses afin d’être plus pratique pour le développement de bas niveau. C n’a jamais été avant tout un projet de recherche sur la conception linguistique. Au lieu de cela, il s'agissait d'un dérivé du développement du noyau Unix sur le mini-ordinateur PDP-11, qui était comparativement soumis à des contraintes de ressources. Pour son créneau (un langage minimaliste de bas niveau pour écrire Unix avec un compilateur à passe unique facile à porter), C a absolument excellé - et plus de 45 ans plus tard, il est toujours la lingua franca de la programmation système.

Amon
la source
17
Juste un petit ajout pour ceux qui ne savent pas ce qui était nécessaire pour la famille ALGOL existante et les langages Pascal: ces langages avaient des fonctions imbriquées lexiquement où vous pouviez accéder à une variable (locale) déclarée dans des fonctions externes. Cela signifiait soit que vous deviez maintenir un "affichage" - un tableau de pointeurs vers les étendues lexicales externes - à chaque appel et retour de fonction (qui changeait le niveau lexical) - ou que vous deviez enchaîner des étendues lexicales dans la pile et chacun de ces accès variables il fallait plusieurs sauts indirects dans la pile pour le trouver. Coûteux! C a largué tout ça. Ça me manque encore.
davidbak
12
@davidbak: L'ABI System V x86-64 (ou convention d'appel sous Linux / OS X) se définit %r10comme le "pointeur de chaîne statique", qui correspond exactement à ce dont vous parlez. Pour C, il ne s'agit que d'un autre registre de travail, mais j'imagine que Pascal s'en servirait. (Fonctions imbriquées GNU C utilisent pour passer un pointeur vers le champ extérieur lorsqu'une telle fonction n'inline pas (par exemple si vous faites un pointeur de fonction si le compilateur crée un trampoline de code machine sur la pile): Acceptabilité régulière utilisation de r10 et r11 )
Peter Cordes
2
@PeterCordes Fascinant! Pascal était encore largement utilisé lorsque le Système V est sorti (bien que je ne sache pas quand l’ABI formel SysV a été défini). Votre réponse liée est très informative.
davidbak
3
C a des types définis par l'utilisateur (struct / union). Je suppose que le reste de ces "fonctionnalités" ont été omises, car elles ont une utilisation nulle ou négative (à moins que vous ne participiez à un concours de code obfusqué :-)), car elles nuisent à l'objectif de garder le langage à la fois simple et expressif. .
jamesqf
6
@ jamesqf: mais très tôt, C n'avait pas d'affectation de structure; Je suppose que vous devez mémoriser ou copier les membres individuellement au lieu d’écrire a = b;pour copier toute une structure comme vous le pouvez dans ISO C89. Ainsi, au début du langage C, les types définis par l'utilisateur étaient définitivement de seconde classe et ne pouvaient être passés que par référence en tant que arguments de fonction. L'aversion de C pour les tableaux et pourquoi
Peter Cordes
37

Au début des années 1970, C était une bouffée d’air frais utilisant des constructions modernes si efficaces que l’ensemble du système UNIX pouvait être réécrit du langage d’assemblage en C avec un encombrement ou une performance négligeable. À l'époque, de nombreux contemporains l'ont qualifié de langage de haut niveau.

Les auteurs de C, principalement Dennis Ritchie, étaient plus circonspects. Dans l'article du Bell System Technical Journal, il était écrit que "C n'est pas un langage de très haut niveau". Avec un sourire ironique et avec l’intention d’être provocateur, Dennis Ritchie dirait que c’est un langage peu développé. Chef parmi ses objectifs de conception pour C était de garder le langage proche de la machine tout en assurant la portabilité, c’est-à-dire l’indépendance de la machine.

Pour plus d'informations, consultez l' article original de BSTJ:

Merci Dennis. Repose en paix.

bud wonsiewicz
la source
3
Il s'agissait en fait de wrappers typés et d'une syntaxe plus agréable pour l'assemblage de PDP, si vous me le demandez.
einpoklum
2
@einpoklum j'ai tendance à être d'accord avec cela. J'ai appris le C aux environs de 1980 à l'université, sur un PDP-11 / 34a, et cela a été décrit par l'un des profs comme un "langage de montage portable". Probablement parce qu'en plus du PDP-11, nous avions plusieurs machines Superbrain CP / M dans le laboratoire sur lesquelles se trouvaient des compilateurs C. en.wikipedia.org/wiki/Intertec_Superbrain
dgnuff
2
Aussi une excellente réponse basée sur des preuves historiques vérifiables (wow! Il y avait une pré-version de K & R disponible sur le marché!).
Christophe
7
@einpoklum N'est-ce pas un peu tiré par les cheveux? J'ai eu l'occasion d'écrire du code système et d'application au milieu des années 80 en assembleur (Z80 et M68K), un "assembleur portable" appelé M (syntaxe PDP11 avec un registre abstrait de 16 bits et un jeu d'instructions), et C. Comparativement à l'assembleur , C est définitivement un langage de haut niveau: la productivité de la programmation en C était d’un ordre de grandeur supérieur à celui d’Assembler! Bien sûr, pas de chaînes (SNOBOL), pas de support de fichiers de données natif (COBOL), pas de code auto-générateur (LISP), pas de mathématiques avancées (APL), pas d'objets (SIMULA), nous pouvons donc convenir que ce n'était pas très élevé
Christophe
21

Comme je l'ai écrit ailleurs sur ce site, quelqu'un a qualifié le modèle de gestion malloc / free memory de "programmation de bas niveau".

C'est drôle comment la définition de "bas niveau" change avec le temps. Lorsque j'ai appris à programmer pour la première fois, toute langue qui fournissait un modèle de tas normalisé permettant un modèle d'allocation / libre simple était considérée comme un niveau élevé. En programmation de bas niveau, vous devez garder la mémoire vous-même (pas les allocations, mais les emplacements de mémoire eux-mêmes!), Ou écrire votre propre allocateur de tas si vous vous sentez vraiment fantaisie.

Pour contexte, c'était au début des années 90, bien après la sortie de C.

Maçon Wheeler
la source
La bibliothèque standard n’était-elle pas seulement une chose depuis la normalisation à la fin des années 80 (enfin, basée sur les API Unix existantes)? En outre, la programmation par le noyau (qui était le domaine de problèmes d'origine du C) nécessite naturellement des éléments de bas niveau tels que la gestion de la mémoire manuelle. À l'époque, C devait être le langage de programmation de niveau supérieur dans lequel un noyau sérieux avait été écrit (je pense qu'aujourd'hui le noyau NT utilise également une bonne quantité de C ++).
amon
6
@hyde, j’ai utilisé l’Algol 60, deux dialectes FORTRAN différents et plusieurs dialectes BASIC différents dans les années 1970, et aucune de ces langues n’avait de pointeur ni d’allocateur de tas.
Salomon Slow
7
À strictement parler, vous pouvez toujours programmer sans malloc()en appelant directement brk(2)ou en mmap(2)gérant vous-même la mémoire qui en résulte. C'est un PITA massif qui ne présente aucun avantage concevable (à moins que vous ne mettiez en œuvre un système similaire à celui d'un malloc), mais vous pouvez le faire.
Kevin le
1
@amon - à l'exception des remarquables machines à pile Burroughs programmées dans ALGOL de bas en haut ... et beaucoup plus tôt qu'Unix. Oh, et d'ailleurs, Multics, qui a inspiré Unix: Écrit en PL / I. Similaire à ALGOL, niveau supérieur à C.
davidbak
6
En fait, la plupart des gens qui n'utilisent pas Multics aujourd'hui ont probablement plus à voir avec le fait qu'il ne fonctionnait que sur des mainframes extrêmement coûteux - c'est-à-dire la dépense scandaleuse de mainframe typique de la journée, plus la dépense supplémentaire d'un demi-cabinet de matériel spécialisé pour mettre en œuvre la mémoire virtuelle avec sécurité. Lorsque des mini-ordinateurs 32 bits tels que le VAX-11 sont sortis, tout le monde, à l'exception des banques et du gouvernement, a démantelé IBM et les Seven Dwarfs et a transféré son traitement à grande échelle sur des "mini" ordinateurs.
davidbak
15

De nombreuses réponses ont déjà fait référence aux premiers articles qui disaient «C n’est pas un langage de haut niveau».

Cependant, je ne peux pas résister à la tentation: beaucoup, si ce n'est la plupart ou tous les HLL à l'époque - Algol, Algol-60, PL / 1, Pascal - fournissaient une vérification des limites de la matrice et une détection de dépassement numérique.

La dernière fois que j'ai vérifié les tampons et les débordements d'entiers étaient à l'origine de nombreuses failles de sécurité. ... Oui, toujours le cas ...

La situation en matière de gestion dynamique de la mémoire était plus compliquée, mais le style C malloc / free représentait un grand pas en arrière en termes de sécurité.

Donc, si votre définition de HLL inclut «empêche automatiquement de nombreux bogues de bas niveau», l’état déplorable de la cybersécurité serait très différent, probablement meilleur, si C et UNIX n’avaient pas existé.

Krazy Glew
la source
7
Étant donné que j’ai été impliqué dans l’implémentation d’Intel MPX et du compilateur de contrôle pointeur / bornes qui en a résulté, je peux vous renvoyer aux documents sur leurs performances: essentiellement 5-15%. Une grande partie de cela concerne des analyses de compilateur qui étaient à peine possibles dans les années 1970 et 1980 - comparées à des contrôles naïfs qui pourraient être 50% plus lents. Cependant, je pense qu’il est juste de dire que C et UNIX ont retardé de 20 ans le travail sur de telles analyses - lorsque C est devenu le langage de programmation le plus populaire, la demande en matière de sécurité était bien moindre.
Krazy Glew
9
@jamesqf En outre, de nombreuses machines antérieures à C avaient un support matériel spécial pour la vérification des limites et les dépassements d'entiers. Puisque C n’utilisait pas ce matériel, il a finalement été déconseillé et supprimé.
Krazy Glew
5
@jamesqf Par exemple: le MIPS RISC ISA était à l'origine basé sur les repères de Stanford, qui étaient à l'origine écrits en Pascal, mais plus tard déplacés en C. Puisque Pascal a vérifié le dépassement d'entier signé, MIPS l'a fait dans des instructions telles que ADD. Vers 2010, je travaillais chez MIPS, mon patron voulait supprimer les instructions inutilisées dans MIPSr6, et des études ont montré que les contrôles de débordement n'étaient presque jamais utilisés. Mais il s’est avéré que Javascript effectuait de tels contrôles, mais ne pouvait pas utiliser les instructions peu coûteuses en raison du manque de prise en charge du système d’exploitation.
Krazy Glew
3
@KrazyGlew - Il est très intéressant que vous abordiez cette question car en C / C ++, la lutte entre les utilisateurs et les rédacteurs du compilateur sur le "comportement indéfini" dû au débordement d'entiers signés est en train de chauffer, car les rédacteurs actuels du compilateur ont repris l'ancien mantra "Faire un faux programme est pire qu’aucun péché "et l’a porté à 11. De nombreuses publications sur stackoverflow et ailleurs reflètent cela ...
davidbak
2
Si Rust possédait des éléments intrinsèques SIMD tels que C, il pourrait s'agir d'un langage d'assemblage portable presque idéal. C devient de pire en pire en tant que langage d’assemblage portable en raison de l’optimisation agressive basée sur UB, et de l’impossibilité d’exposer de manière portable les nouvelles opérations primitives prises en charge par les processeurs modernes math). Faire en sorte que les compilateurs C soient efficaces sur les processeurs prenant en charge le matériel informatique nécessite souvent des éléments intrinsèques non portables. Faire en sorte que le compilateur émule popcnt sur des cibles sans cela est mieux que la reconnaissance idiomatique popcnt.
Peter Cordes
8

Considérons des langues plus anciennes et beaucoup plus anciennes que C (1972):

Fortran - 1957 (niveau légèrement supérieur à C)

Lisp - 1958

Cobol - 1959

Fortran IV - 1961 (niveau légèrement supérieur à C)

PL / 1 - 1964

APL - 1966

Plus un langage de niveau moyen tel que RPG (1959), principalement un langage de programmation destiné à remplacer les systèmes d’enregistrement d’unités basés sur un tableau de connexion.

Dans cette perspective, C semblait être un langage de très bas niveau, à peine supérieur aux assembleurs de macros utilisés à l’époque sur des mainframes. Dans le cas des ordinateurs centraux IBM, les macros assembleur étaient utilisées pour l’accès à la base de données, telle que BDAM (méthode d’accès de base au disque), car les interfaces de la base de données n’avaient pas été portées sur Cobol Les programmes Cobol sont encore utilisés aujourd'hui sur les ordinateurs centraux IBM.

rcgldr
la source
2
Si vous souhaitez répertorier des langues plus anciennes et plus avancées, n'oubliez pas LISP .
Déduplicateur
@ Déduplicateur - je l'ai ajouté à la liste. Je me concentrais sur ce qui était utilisé sur les ordinateurs centraux IBM, et je ne me souviens pas de la popularité de LISP, mais APL était également un langage de niche pour les ordinateurs centraux IBM (via des consoles de partage de temps) et IBM 1130. Comme dans LISP, APL est l’un des les langages de haut niveau plus uniques, par exemple le coût de code nécessaire pour créer le jeu de la vie de Conway avec une version actuelle de APL: youtube.com/watch?v=a9xAKttWgP4 .
rcgldr
2
Je ne considérerais pas que RPG ou COBOL soit particulièrement élevé, du moins pas avant COBOL-85. Lorsque vous grattez la surface de COBOL, vous voyez qu'il s'agit essentiellement d'une collection de macros d'assembleur très avancées. Pour commencer, il manque les fonctions, les procédures et la récursivité, ainsi que toute sorte de portée. Tout stockage doit être déclaré en haut du programme, ce qui entraîne des ouvertures extrêmement longues ou une réutilisation douloureuse des variables.
Idrougge
J'ai de mauvais souvenirs d'utilisation de Fortran IV, je ne me souviens pas qu'il soit sensiblement "de niveau supérieur" par rapport à C.
DaveG
@idrougge - COBOL et RPG, ainsi que des jeux d’instructions mainframe, incluaient une prise en charge complète du BCD emballé ou non empaqueté, une exigence des logiciels financiers dans des pays comme les États-Unis. Je considère que les opérateurs natifs associés tels que "move correspondant" sont de haut niveau. RPG était inhabituel en ce sens que vous avez spécifié un lien entre les champs d’entrée bruts et les champs de sortie formatés et / ou les accumulateurs, mais pas l’ordre des opérations, similaire à la programmation du plug-in qu’il a remplacé.
rcgldr
6

La réponse à votre question dépend du langage C concerné.

Le langage décrit dans le manuel de référence C de 1974 de Dennis Ritchie était un langage de bas niveau offrant une certaine commodité de programmation par rapport aux langages de niveau supérieur. Les dialectes dérivés de cette langue ont également tendance à être des langages de programmation de bas niveau.

Cependant, lors de la publication de la norme 1989/1990 C, elle ne décrivait pas le langage de bas niveau devenu populaire pour la programmation de machines réelles, mais décrivait un langage de niveau supérieur qui pouvait être - mais n'était pas obligé - -implémenté en termes de niveau inférieur.

En tant qu'auteurs de la note C, l'un des éléments qui ont rendu le langage utile est que de nombreuses implémentations peuvent être traitées comme des assembleurs de haut niveau. Parce que C était également utilisé comme alternative à d'autres langages de haut niveau et que de nombreuses applications n'exigeaient pas la capacité de faire des choses que les langages de haut niveau ne pouvaient pas faire, les auteurs de la norme ont permis aux implémentations de se comporter de manière arbitraire. si les programmes ont essayé d'utiliser des constructions de bas niveau. Par conséquent, le langage décrit par le standard C n'a jamais été un langage de programmation bas niveau.

Pour comprendre cette distinction, considérons comment Ritchie's Language et C89 afficheraient l'extrait de code:

struct foo { int x,y; float z; } *p;
...
p[3].y+=1;

sur une plate-forme où "char" correspond à 8 bits, "int" à 16 bits big-endian, "float" à 32 bits et les structures n'ayant pas d'exigences de remplissage ou d'alignement particulières, la taille de "struct foo" est de 8 octets.

Dans la langue de Ritchie, le comportement de la dernière instruction prend l'adresse stockée dans "p", lui ajoute 3 * 8 + 2 [ie 26] octets, et extrait une valeur de 16 bits des octets de cette adresse et de la suivante. , ajoutez un à cette valeur, puis écrivez cette valeur de 16 bits sur les deux mêmes octets. Le comportement serait défini comme agissant sur les 26ème et 27ème octets suivant celui de l'adresse p sans tenir compte du type d'objet qui y était stocké.

Dans le langage défini par le standard C, dans le cas où * p identifie un élément d'un "struct foo []" suivi d'au moins trois éléments plus complets de ce type, la dernière instruction ajoute un membre à y le troisième élément après * p. Le comportement ne serait défini par la norme dans aucune autre circonstance.

Le langage de Ritchie était un langage de programmation de bas niveau car, s'il permettait au programmeur d'utiliser des abstractions telles que des tableaux et des structures lorsque cela convenait, il définissait le comportement en termes de disposition sous-jacente des objets en mémoire. En revanche, le langage décrit par C89 et les normes ultérieures définit les choses en termes d'abstraction de niveau supérieur et ne définit que le comportement du code qui est cohérent avec cela. Une mise en œuvre de qualité adaptée à la programmation de bas niveau se comportera de manière utile dans plus de cas que prévu par la norme, mais il n'existe aucun document "officiel" spécifiant ce qu'une mise en œuvre doit faire pour être adaptée à ces objectifs.

Le langage C inventé par Dennis Ritchie est donc un langage de bas niveau et a été reconnu comme tel. Cependant, le langage inventé par le Comité de normalisation C n'a jamais été un langage de bas niveau en l'absence de garanties fournies par la mise en œuvre qui vont au-delà des mandats de la norme.

supercat
la source