La corruption de la mémoire était-elle un problème courant dans les grands programmes écrits en langage assembleur?

Jan 21 2021

Les bogues de corruption de mémoire ont toujours été un problème courant dans les grands programmes et projets C. C'était un problème dans BSD 4.3 à l'époque, et c'est toujours un problème aujourd'hui. Quelle que soit la précision avec laquelle le programme est écrit, s'il est suffisamment volumineux, il est souvent possible de découvrir un autre bogue de lecture ou d'écriture hors limites dans le code.

Mais il fut un temps où les grands programmes, y compris les systèmes d'exploitation, étaient écrits en assemblage, et non en C. Les bogues de corruption de la mémoire étaient-ils un problème courant dans les grands programmes d'assemblage? Et comment s'est-il comparé aux programmes C?

Réponses

53 Jean-FrançoisFabre Jan 21 2021 at 17:23

Le codage en assemblage est brutal.

Pointeurs escrocs

Les langages d'assemblage s'appuient encore plus sur des pointeurs (via des registres d'adresses), vous ne pouvez donc même pas compter sur le compilateur ou les outils d'analyse statique pour vous avertir de telles corruptions de mémoire / dépassements de tampon par opposition à C.

Par exemple en C, un bon compilateur peut émettre un avertissement là-bas:

 char x[10];
 x[20] = 'c';

C'est limité. Dès que le tableau se désintègre en un pointeur, de telles vérifications ne peuvent pas être effectuées, mais c'est un début.

En assemblage, sans un temps d'exécution approprié ou des outils binaires d'exécution formels, vous ne pouvez pas détecter de telles erreurs.

Registres non fiables (principalement d'adresses)

Un autre facteur aggravant pour l'assemblage est que la convention de conservation des registres et d'appel de routine n'est pas standard / garantie.

Si une routine est appelée et ne sauvegarde pas un registre particulier par erreur, elle retourne à l'appelant avec un registre modifié (à côté des registres "scratch" qui sont connus pour être mis dans la corbeille à la sortie), et l'appelant ne s'attend pas il, ce qui conduit à lire / écrire à l'adresse incorrecte. Par exemple en code 68k:

    move.b  d0,(a3)+
    bsr  a_routine
    move.b  d0,(a3)+   ; memory corruption, a3 has changed unexpectedly
    ...

a_routine:
    movem.l a0-a2,-(a7)
    ; do stuff
    lea some_table(pc),a3    ; change a3 if some condition is met
    movem.l (a7)+,a0-a2   ; the routine forgot to save a3 !
    rts

L'utilisation d'une routine écrite par quelqu'un d'autre qui n'utilise pas les mêmes conventions de sauvegarde de registre peut conduire au même problème. Je sauvegarde généralement tous les registres avant d'utiliser la routine de quelqu'un d'autre.

D'un autre côté, un compilateur utilise la pile ou le passage de paramètre de registre standard, gère les variables locales en utilisant pile / autre périphérique, préserve les registres si nécessaire, et tout est cohérent dans tout le programme, garanti par le compilateur (sauf s'il y a des bogues, de cours)

Modes d'adressage non fiables

J'ai corrigé de nombreuses violations de la mémoire dans les anciens jeux Amiga. Les exécuter dans un environnement virtuel avec MMU activé déclenche parfois des erreurs de lecture / écriture dans des adresses fausses complètes. La plupart du temps, ces lectures / écritures n'ont aucun effet car les lectures renvoient 0 et les écritures vont dans les bois, mais selon la configuration de la mémoire, cela peut avoir des conséquences désagréables.

Il y a également eu des cas d'erreurs de résolution. J'ai vu des trucs comme:

 move.l $40000,a0

au lieu de immédiat

 move.l #$40000,a0

dans ce cas, le registre d'adresses contient ce qui se trouve $40000(probablement la corbeille) et non l' $40000adresse. Cela conduit à une corruption catastrophique de la mémoire dans certains cas. Le jeu finit généralement par faire l'action qui n'a pas fonctionné ailleurs sans résoudre ce problème, de sorte que le jeu fonctionne correctement la plupart du temps. Mais il y a des moments où les jeux ont dû être correctement corrigés pour restaurer un comportement correct.

En C, une valeur trompeuse pour un pointeur entraîne un avertissement.

(Nous avons abandonné un jeu comme "Wicked" qui avait de plus en plus de corruption graphique au fur et à mesure que vous aviez avancé dans les niveaux, mais aussi en fonction de la façon dont vous passiez les niveaux et de leur ordre ...)

Tailles de données non fiables

En assemblage, il n'y a pas de types. Cela signifie que si je fais

move.w #$4000,d0           ; copy only 16 bits
move.l #1,(a0,d0.l)    ; indexed write on d1, long

le d0registre ne change que la moitié des données. Peut-être ce que je voulais, peut-être pas. Ensuite, s'il d0contient zéro sur les 32-16 bits les plus significatifs, le code fait ce qui est attendu, sinon il ajoute a0et d0(plage complète) et l'écriture résultante est "dans les bois". Un correctif est:

move.l #1,(a0,d0.w)    ; indexed write on d1, long

Mais alors si d0> $7FFFil fait quelque chose de mal aussi, parce que d0c'est considéré comme négatif alors (pas le cas avec d0.l). Il d0faut donc une extension de signe ou un masquage ...

Ces erreurs de taille peuvent être vues sur un code C, par exemple lors de l'assignation à une shortvariable (qui tronque le résultat) mais même dans ce cas, vous obtenez juste un mauvais résultat la plupart du temps, pas des problèmes fatals comme ci-dessus (c'est-à-dire: si vous ne ne mentez pas au compilateur en forçant un mauvais type de casts)

Les assembleurs n'ont pas de types, mais les bons assembleurs permettent d'utiliser des structures ( STRUCTmot-clé) qui permettent d'élever un peu le code en calculant automatiquement les décalages de structure. Mais une lecture de mauvaise taille peut être catastrophique, que vous utilisiez ou non des structures / décalages définis

move.w  the_offset(a0),d0

à la place de

move.l  the_offset(a0),d0

n'est pas cochée et vous donne les mauvaises données dans d0. Assurez-vous de boire suffisamment de café pendant le codage, ou écrivez simplement de la documentation à la place ...

Alignement des données non fiables

L'assembleur avertit généralement du code non aligné, mais pas des pointeurs non alignés (car les pointeurs n'ont pas de type), qui peuvent déclencher des erreurs de bus.

Les langages de haut niveau utilisent des types et évitent la plupart de ces erreurs en effectuant un alignement / remplissage (à moins que, encore une fois, on ne mente).

Vous pouvez cependant écrire des programmes d'assemblage avec succès. En utilisant une méthodologie stricte de passage de paramètres / sauvegarde de registre et en essayant de couvrir 100% de votre code par des tests, et un débogueur (symbolique ou non, c'est toujours le code que vous avez écrit). Cela ne supprimera pas tous les bogues potentiels, en particulier ceux causés par de mauvaises données d'entrée, mais cela aidera.

24 jackbochsler Jan 22 2021 at 05:41

J'ai passé l'essentiel de ma carrière à écrire des assembleur, solo, petites équipes et grandes équipes (Cray, SGI, Sun, Oracle). J'ai travaillé sur des systèmes embarqués, des OS, des VM et des chargeurs d'amorçage. La corruption de la mémoire était rarement, voire jamais, un problème. Nous avons embauché des personnes pointues, et celles qui ont échoué ont été gérées dans des emplois différents plus adaptés à leurs compétences.

Nous avons également testé fanatiquement - à la fois au niveau de l'unité et au niveau du système. Nous avions des tests automatisés qui se déroulaient constamment à la fois sur des simulateurs et du matériel réel.

Vers la fin de ma carrière, j'ai interviewé une entreprise et j'ai demandé comment ils effectuaient leurs tests automatisés. Leur réponse de "Quoi?!?" était tout ce que j'avais besoin d'entendre, j'ai mis fin à l'entrevue.

19 RETRAC Jan 21 2021 at 23:10

De simples erreurs idiotes abondent dans l'assemblage, peu importe votre attention. Il s'avère que même les compilateurs stupides pour les langages de haut niveau mal définis (comme C) contraignent une vaste gamme d'erreurs possibles comme sémantiquement ou syntaxiquement invalides. Une erreur avec une seule touche supplémentaire ou oubliée est beaucoup plus susceptible de refuser de compiler que d'assembler. Les constructions que vous pouvez valablement exprimer dans l'assemblage qui n'ont tout simplement aucun sens parce que vous le faites mal sont moins susceptibles de se traduire en quelque chose qui est accepté comme valide C. plus susceptible de plisser les yeux et de dire "hein?" et réécrivez le monstre que vous venez d'écrire.

Ainsi, le développement et le débogage d'assemblages sont, en effet, douloureusement impitoyables. Mais la plupart de ces erreurs briser les choses difficiles , et apparaîtraient dans le développement et le débogage. Je risquerais de supposer que, si les développeurs suivent la même architecture de base et les mêmes bonnes pratiques de développement, le produit final devrait être à peu près aussi robuste. Le type d'erreurs qu'un compilateur détecte peut être détecté avec de bonnes pratiques de développement, et le type d'erreurs que les compilateurs ne détectent pas peut être détecté ou non avec de telles pratiques. Il faudra cependant beaucoup plus de temps pour atteindre le même niveau.

14 WalterMitty Jan 23 2021 at 02:48

J'ai écrit le ramasse-miettes original pour MDL, un langage semblable à Lisp, en 1971-72. C'était tout un défi pour moi à l'époque. Il a été écrit en MIDAS, un assembleur pour le PDP-10 exécutant ITS.

Éviter la corruption de la mémoire était le nom du jeu dans ce projet. Toute l'équipe avait peur d'une démo réussie qui plantait et brûlait lorsque le ramasse-miettes était appelé. Et je n'avais pas de très bon plan de débogage pour ce code. J'ai fait plus de vérifications de bureau que je n'en ai jamais fait avant ou depuis. Des trucs comme s'assurer qu'il n'y avait pas d'erreurs de clôture. S'assurer que lorsqu'un groupe de vecteurs a été déplacé, la cible ne contient pas de déchets. Encore et encore, tester mes hypothèses.

Je n'ai jamais trouvé de bogues dans ce code, à l'exception de ceux trouvés par vérification de bureau. Après notre mise en ligne, aucun n'a jamais fait surface pendant ma montre.

Je ne suis tout simplement pas aussi intelligent qu'il y a cinquante ans. Je ne pourrais rien faire de tel aujourd'hui. Et les systèmes d'aujourd'hui sont des milliers de fois plus gros que ne l'était MDL.

7 Raffzahn Jan 22 2021 at 00:00

Les bogues de corruption de mémoire ont toujours été un problème courant dans les grands programmes C [...] Mais il fut un temps où les grands programmes, y compris les systèmes d'exploitation, étaient écrits en assemblage, pas en C.

Vous savez qu'il existe d'autres langues qui étaient déjà assez courantes au début? Comme COBOL, FORTRAN ou PL / 1?

Les bogues de corruption de la mémoire étaient-ils un problème courant dans les grands programmes d'assemblage?

Cela dépend bien sûr de plusieurs facteurs, comme

  • l'assembleur utilisé, car différents programmes d'assembleur offrent différents niveaux de prise en charge de la programmation.
  • la structure du programme, car les programmes particulièrement volumineux adhèrent à une structure vérifiable
  • modularisation et interfaces claires
  • le type de programme écrit, car toutes les tâches ne nécessitent pas de violon de pointeur
  • meilleur style de pratique

Un bon assembleur ne s'assure pas seulement que les données sont alignées, mais propose également des outils pour gérer des types de données complexes, des structures et autres de manière abstraite, réduisant ainsi le besoin de calculer «manuellement» les pointeurs.

Un assembleur utilisé pour tout projet sérieux est comme toujours un assembleur de macro (* 1), donc capable d'encasuler des opérations primitives dans des instructions de macro de plus haut niveau, permettant une programmation plus centrée sur l'application tout en évitant de nombreux pièges de la gestion des pointeurs (* 2).

Les types de programmes sont également très influents. Les applications se composent généralement de divers modules, dont beaucoup peuvent être écrits presque ou complets sans (ou seulement contrôlés) l'utilisation de pointeurs. Encore une fois, l'utilisation des outils fournis par l'assembleur est la clé d'un code moins défectueux.

Ensuite, il y aurait les meilleures pratiques - qui vont de pair avec la plupart des précédentes. N'écrivez simplement pas de programmes / modules qui ont besoin de plusieurs registres de base, qui transmettent de gros morceaux de mémoire au lieu de structures de requête dédiées, etc.

Mais les meilleures pratiques commencent déjà tôt et avec des choses apparemment simples. Prenons simplement l'exemple d'un processeur primitif (désolé) comme le 6502 ayant peut-être un ensemble de tables, toutes ajustées aux bordures de page pour les performances. Lors du chargement de l'adresse de l'une de ces tables dans un pointeur de page zéro pour un accès indexé, l'utilisation des outils que l'assembleur signifierait

     LDA   #<Table
     STA   Pointer

Certains programmes que j'ai vus plutôt aller

     LDA   #0
     STA   Pointer

(ou pire, si sur un 65C02)

     STZ   Pointer

L'argumentation habituelle est «Mais il est de toute façon aligné». Vraiment? Cela peut-il être garanti pour toutes les futures itérations? Qu'en est-il un jour où l'espace d'adressage devient restreint et qu'ils doivent être déplacés vers des adresses non alignées? Il faut s'attendre à de nombreuses erreurs géniales (difficiles à trouver).

Les meilleures pratiques nous ramènent donc à l'utilisation de l'assembleur et de tous les outils qu'il propose.

N'essayez pas de jouer à l' assembleur au lieu de l'assembleur - laissez-le faire son travail à votre place.

Et puis il y a le runtime, quelque chose qui s'applique à toutes les langues mais qui est souvent oublié. En plus de choses comme la vérification de la pile ou la vérification des limites des paramètres, l'un des moyens les plus efficaces de détecter les erreurs de pointeur consiste simplement à verrouiller la première et la dernière page de mémoire contre l'écriture et la lecture (* 3). Il détecte non seulement l'erreur de pointeur nul bien-aimée, mais également tous les nombres positifs ou négatifs faibles qui sont souvent le résultat d'une indexation antérieure qui a mal tourné. Bien sûr, Runtime est toujours le dernier recours, mais celui-ci est facile.

Surtout, la raison la plus pertinente est peut-être

  • l'ISA de la machine

en réduisant les risques de corruption de la mémoire en réduisant le besoin de gérer avec des pointeurs du tout.

Certaines structures CPU nécessitent simplement moins d'opérations de pointeur (directes) que d'autres. Il existe un écart énorme entre les architectures qui incluent des opérations de mémoire à mémoire et celles qui ne le font pas, comme les architectures de chargement / stockage basées sur un accumulateur. La gestion du pointeur nécessite intrinsèquement pour tout ce qui est plus grand qu'un seul élément (octet / mot).

Par exemple pour transférer un champ, disons un nom de client à partir de la mémoire, a / 360 utilise une seule opération MVC avec les adresses et la longueur de transfert générées par l'assembleur à partir de la définition des données, tandis qu'une architecture de chargement / stockage, conçue pour gérer chaque octet séparé, doit mettre en place des pointeurs et de la longueur dans les registres et une boucle autour d'un élément unique en mouvement.

Étant donné que de telles opérations sont assez courantes, le potentiel d'erreurs qui en résulte est également courant. Ou, d'une manière plus générale, on peut dire que:

Les programmes pour les processeurs CISC sont généralement moins sujets aux erreurs que ceux écrits pour les machines RISC.

Bien sûr et comme d'habitude, tout peut être gâché par une mauvaise programmation.

Et comment s'est-il comparé aux programmes C?

À peu près la même chose - ou mieux, C est l'équivalent HLL de l'ISA CPU le plus primitif, donc tout ce qui offre des instructions de niveau supérieur sera mieux.

C est intrinsèquement un langage RISCy. Les opérations fournies sont réduites au minimum, ce qui va avec une capacité minimale de contrôle des opérations non intentionnelles. L'utilisation de pointeurs non cochés est non seulement standard mais nécessaire pour de nombreuses opérations, ouvrant de nombreuses possibilités de corruption de la mémoire.

Prenez en revanche un HLL comme ADA, ici il est presque impossible de créer des ravages de pointeur - à moins que cela ne soit intentionnel et explicitement déclaré comme option. Une bonne partie de celui-ci est (comme avec l'ISA auparavant) due à des types de données plus élevés et à leur traitement de manière sécurisée.


Pour la partie expérience, j'ai fait la majeure partie de ma vie professionnelle (> 30 ans) dans des projets d'assemblage, avec comme 80% Mainframe (/ 370) 20% Micros (principalement 8080 / x86) - plus privé beaucoup plus :) Projets couverts par la programmation mainframe aussi grand que 2+ millions de LOC (instructions seulement) tandis que les micro-projets ont gardé environ 10-20k LOC.


* 1 - Non, quelque chose qui propose de remplacer des passages de texte par du texte préfabriqué est au mieux un préprocesseur textuel, mais pas un assembleur de macros. Un assembleur de macros est un méta-outil pour créer le langage nécessaire à un projet. Il offre des outils pour exploiter les informations que l'assembleur rassemble sur la source (taille du champ, type de champ, et bien d'autres) ainsi que des structures de contrôle pour formuler la manipulation, utilisées pour générer le code approprié.

* 2 - Il est facile de déplorer que C ne soit pas adapté à des capacités macro sérieuses, cela aurait non seulement supprimé le besoin de nombreuses constructions obscures, mais aussi permis beaucoup d'avancées en étendant le langage sans qu'il soit nécessaire d'en écrire un nouveau.

* 3 - Personnellement, je préfère rendre la page 0 protégée en écriture uniquement et remplir les 256 premiers octets avec un zéro binaire. De cette façon, toutes les écritures de pointeur nul (ou faible) entraînent toujours une erreur machine, mais la lecture à partir d'un pointeur nul renvoie, selon le type, un octet / demi-mot / mot / double mot contenant zéro - enfin, ou une chaîne nulle :) Je sais, c'est paresseux, mais cela rend la vie beaucoup plus facile si on doit incooper le code des autres. De plus, la page restante peut être utilisée pour des valeurs constantes pratiques telles que des pointeurs vers diverses sources globales, des chaînes d'identification, un contenu de champ constant et des tables de traduction.

6 waltinator Jan 22 2021 at 09:17

J'ai écrit des mods OS en assemblage sur CDC G-21, Univac 1108, DECSystem-10, DECSystem-20, tous les systèmes 36 bits, plus 2 assembleurs IBM 1401.

La «corruption de la mémoire» existait, principalement sous la forme d'une entrée sur une liste «Choses à ne pas faire».

Sur un Univac 1108, j'ai trouvé une erreur matérielle où la première extraction d'un demi-mot (l'adresse du gestionnaire d'interruption) après une interruption matérielle renverrait tous les 1, au lieu du contenu de l'adresse. Dans les mauvaises herbes, avec les interruptions désactivées, pas de protection de la mémoire. Il va et vient, là où il s'arrête, personne ne le sait.

5 Peter-ReinstateMonica Jan 22 2021 at 19:31

Vous comparez des pommes et des poires. Les langages de haut niveau ont été inventés parce que les programmes atteignaient une taille ingérable avec l'assembleur. Exemple: "La V1 avait 4 501 lignes de code d'assemblage pour son noyau, son initialisation et son shell. Parmi celles-ci, 3 976 représentent le noyau et 374 pour le shell." (D'après cette réponse .)

Le. V1. Coquille. Avait. 347. Lignes. De. Code.

Le bash d'aujourd'hui a peut-être 100000 lignes de code (un wc sur le dépôt donne 170k), sans compter les bibliothèques centrales comme readline et la localisation. Les langages de haut niveau sont utilisés en partie pour la portabilité mais aussi parce qu'il est pratiquement impossible d'écrire des programmes de la taille actuelle en assembleur. Ce n'est pas seulement plus sujet aux erreurs - c'est presque impossible.

4 supercat Jan 22 2021 at 03:45

Je ne pense pas que la corruption de la mémoire soit généralement plus un problème dans le langage assembleur que dans tout autre langage qui utilise des opérations d'indice de tableau non vérifiées, lors de la comparaison de programmes qui exécutent des tâches similaires. Bien que l'écriture d'un code assembleur correct puisse nécessiter une attention aux détails au-delà de ceux qui seraient pertinents dans un langage comme C, certains aspects du langage assembleur sont en fait plus sûrs que C. En langage assembleur, si le code effectue une séquence de charges et de magasins, un assembleur produire les instructions de chargement et de stockage dans l'ordre donné sans se demander si elles sont toutes nécessaires. En C, en revanche, si un compilateur intelligent comme clang est invoqué avec un paramètre d'optimisation autre que -O0et donné quelque chose comme:

extern char x[],y[];
int test(int index)
{
    y[0] = 1;
    if (x+2 == y+index)
        y[index] = 2;
    return y[0];
}

il peut déterminer que la valeur y[0]lorsque l' returnexécution d'une instruction sera toujours 1, et il n'y a donc pas besoin de recharger sa valeur après avoir écrit y[index], même si les circonstances ne défini où l'écriture à l' index pourrait se produire serait si x[]deux octets, y[]se pour le suivre immédiatement, et indexest égal à zéro, ce qui implique qu'il y[0]resterait en fait le nombre 2.

3 phyrfox Jan 23 2021 at 23:33

Assembler nécessite une connaissance plus approfondie du matériel que vous utilisez que d'autres langages comme C ou Java. La vérité est, cependant, l'assembleur a été utilisé dans presque tout, depuis les premières voitures informatisées, les premiers systèmes de jeux vidéo jusqu'aux années 1990, jusqu'aux appareils Internet des objets que nous utilisons aujourd'hui.

Alors que C offrait la sécurité de type, il n'offrait toujours pas d'autres mesures de sécurité telles que la vérification du pointeur vide ou les tableaux bornés (du moins, pas sans code supplémentaire). C'était assez facile d'écrire un programme qui planterait et brûlerait aussi bien que n'importe quel programme d'assembleur.

Des dizaines de milliers de jeux vidéo ont été écrits en assembleur, compos pour écrire des démos petites mais impressionnantes en seulement quelques kilo-octets de code / données depuis des décennies maintenant, des milliers de voitures utilisent encore une forme d'assembleur aujourd'hui, ainsi que quelques moins connus systèmes d'exploitation (par exemple MenuetOS ). Vous pourriez avoir des dizaines, voire des centaines de choses dans votre maison qui ont été programmées dans l'assembleur que vous ne connaissez même pas.

Le principal problème avec la programmation en assembleur est que vous devez planifier plus vigoureusement que vous ne le faites dans un langage comme C.Il est parfaitement possible d'écrire un programme avec même 100k lignes de code en assembleur sans un seul bogue, et il est également possible d'écrire un programme avec 20 lignes de code qui a 5 bogues.

Ce n'est pas l'outil qui pose problème, c'est le programmeur. Je dirais que la corruption de la mémoire était un problème courant dans la programmation précoce en général. Cela ne se limitait pas à l'assembleur, mais aussi au C (qui était connu pour ses fuites de mémoire et à l'accès à des plages de mémoire invalides), C ++ et d'autres langages où vous pouviez accéder directement à la mémoire, même BASIC (qui avait la capacité de lire / écrire des I / O sur le CPU).

Même avec les langages modernes qui ont des protections, nous verrons des erreurs de programmation qui plantent les jeux. Pourquoi? Parce qu'il n'y a pas assez de soin apporté à la conception de l'application. La gestion de la mémoire n'a pas disparu, elle a été cachée dans un coin où il est plus difficile de visualiser, causant toutes sortes de ravages aléatoires dans le code moderne.

Pratiquement toutes les langues sont susceptibles de subir divers types de corruption de la mémoire si elles ne sont pas utilisées correctement. Aujourd'hui, le problème le plus courant concerne les fuites de mémoire, qui sont plus faciles que jamais à introduire accidentellement en raison de fermetures et d'abstractions.

Il est injuste de dire que l'assembleur était intrinsèquement plus ou moins corrompant la mémoire que les autres langages, il avait juste une mauvaise réputation en raison de la difficulté d'écrire du code approprié.

2 JohnDoty Jan 23 2021 at 02:12

C'était un problème très courant. Le compilateur FORTRAN d'IBM pour le 1130 en avait quelques-uns: ceux dont je me souviens impliquaient des cas de syntaxe incorrecte qui n'ont pas été détectés. Le passage à des langages de niveau supérieur proches de la machine n'a évidemment pas aidé: les premiers systèmes Multics écrits en PL / I plantaient fréquemment. Je pense que la culture et la technique de programmation ont plus à voir avec l'amélioration de cette situation que la langue.

2 JohnDallman Jan 24 2021 at 21:26

J'ai fait quelques années de programmation assembleur, suivies par des décennies de C. Les programmes assembleurs ne semblaient pas avoir plus de bogues de pointeur que C, mais une raison importante à cela était que la programmation assembleur est un travail relativement lent.

Les équipes dans lesquelles j'étais voulaient tester leur travail chaque fois qu'elles écrivaient un incrément de fonctionnalité, qui était généralement toutes les 10 à 20 instructions d'assembleur. Dans les langages de niveau supérieur, vous testez généralement après un nombre similaire de lignes de code, qui ont beaucoup plus de fonctionnalités. Cela équivaut à la sécurité d'un HLL.

Assembler a cessé d'être utilisé pour des tâches de programmation à grande échelle parce qu'il donnait une productivité moindre et parce qu'il n'était généralement pas portable sur d'autres types d'ordinateurs. Au cours des 25 dernières années, j'ai écrit environ 8 lignes d'assembleur, et c'était pour générer des conditions d'erreur pour tester un gestionnaire d'erreurs.

1 postasaguest Jan 22 2021 at 23:25

Pas quand je travaillais avec des ordinateurs à l'époque. Nous avons eu de nombreux problèmes mais je n'ai jamais rencontré de problèmes de corruption de mémoire.

Maintenant, j'ai travaillé sur plusieurs machines IBM 7090,360,370, s / 3, s / 7 et aussi des micros basés sur 8080 et Z80. D'autres ordinateurs ont peut-être eu des problèmes de mémoire.