«Avez-vous déjà changé la valeur de 4?» - comment cela est-il entré dans le quiz Hayes-Thomas?

24

En 1989, Felix Lee, John Hayes et Angela Thomas ont écrit un test de Hacker prenant la forme d'un quiz avec de nombreuses blagues d'initiés, comme « Mangez-vous des moisissures visqueuses? "

J'envisage la série suivante:

0015 Ever change the value of 4?
0016 ... Unintentionally?
0017 ... In a language other than Fortran?

Y a-t-il une anecdote particulière qui rend le nombre «4» particulier dans la série?

Une implémentation de Fortran a-t-elle permis de modifier la valeur des constantes? Était-ce possible dans d'autres langues couramment utilisées à l'époque?

Michael Le Barbier Grünewald
la source
2
@Ordous ne me dérange pas si nous gardons la deuxième question ici, surtout si les répondeurs prennent soin d'expliquer pourquoi un tel comportement existe dans les langues modernes (c'est-à-dire qu'il y a des utilisations pratiques pour cela?). Cela dit, cela poserait également une excellente question sur Code Golf .
yannis
8
Connexes: Écrivez un programme qui fait 2 + 2 = 5 . Une réponse Java et Python y remplace 4pour 5dans les listes d'entiers internes.
Martijn Pieters
5
Et un commentaire sur cette page indique que vous pouvez redéfinir les littéraux dans FORTRAN IV; 4 = 5était possible.
Martijn Pieters
7
Et merci pour le lien de test de ce pirate. Vous me faites maintenant me sentir vieille et horrifiée de la fréquence à laquelle je peux répondre «oui» aux questions.
Martijn Pieters
5
J'ai changé une fois la valeur du zéro constant dans un programme fortran. C'était un bug très difficile à retrouver.
Bryan Oakley

Réponses:

32

Autrefois (dans les années 1970 et avant), certains ordinateurs n'avaient pas de MMU (et cela est vrai aujourd'hui pour les microcontrôleurs très bon marché).

Sur de tels systèmes, il n'y a pas de protection de la mémoire donc pas de segment en lecture seule dans l' espace d'adressage , et un programme buggy pourrait écraser une constante (soit dans la mémoire de données, soit même à l'intérieur du code machine).

Les compilateurs Fortran à cette époque ont passé des arguments formels par référence . Donc, si vous l'avez fait CALL FUN(4)et que SUBROUTINE FUN(I)son corps change I- par exemple avec une déclaration I = I + 1dans son corps, vous pourriez avoir une catastrophe, changer 4 en 5 dans l'appelant (ou pire).

Cela était également vrai sur les premiers micro-ordinateurs comme l' IBM PC AT original de 1984, avec MS-DOS

FWIW, je suis assez vieux pour avoir utilisé, à l'adolescence au début des années 1970, de tels ordinateurs: IBM1620 et CAB500 (dans un musée: ce sont des ordinateurs de l'époque des années 1960!). L'IBM1620 était assez amusant: il était utilisé dans les tables de mémoire pour les ajouts et les multiplications (et si vous écrasiez ces tables, le chaos s'ensuivait). Ainsi, non seulement vous pourriez écraser un 4, mais vous pourriez même écraser chaque futur ajout 2 + 2 ou multiplications 7 * 8 (mais j'ai vraiment oublié ces détails sales, donc cela pourrait être faux).

Aujourd'hui, vous pouvez remplacer le code BIOS dans la mémoire flash, si vous persévérez suffisamment. Malheureusement, je ne me sens plus aussi amusant, donc je n'ai jamais essayé. (J'ai même peur d'installer des LinuxBios sur ma carte mère).

Sur les ordinateurs et systèmes d'exploitation actuels, le fait de passer une constante par référence et de la modifier à l'intérieur de l'appelé provoquera simplement une violation de segmentation , ce qui semble familier à de nombreux développeurs C ou C ++.

BTW: être tatillonne: l'écrasement 4 n'est pas une question de langage, mais d'implémentation.

Basile Starynkevitch
la source
14
Le 1620 était surnommé CADET: Impossible d'ajouter, n'essaye même pas.
Pete Becker
L'astuce peut être presque répétée même maintenant avec gfortran. Les constantes sont placées dans leur segment et transmises par référence à un sous-programme. Par défaut, la section constante est en lecture seule, donc une erreur de protection de la mémoire tue le programme.
Netch
7

Il s'agissait d'un effet secondaire non intentionnel de la stratégie d'évaluation des appels de fonction de FORTRAN en combinaison avec une optimisation du compilateur erronée.

FORTRAN II a introduit des fonctions et sous-programmes définis par l'utilisateur avec leurs arguments passés par référence . (Eh bien, je ne sais pas. C'était probablement plus efficace que le passage par valeur sur le matériel IBM de l'époque.)

Normalement, le passage par référence signifie que vous devez passer une valeur l (comme une variable) au lieu d'une valeur r. Mais les concepteurs de FORTRAN ont décidé d'être utiles et de vous laisser passer des valeurs r comme arguments de toute façon. Le compilateur générerait automatiquement une variable pour vous. Donc, si vous avez écrit:

CALL SUBFOO(X + Y, 4)

le compilateur convertirait cela dans les coulisses en quelque chose comme

TEMP1 = X + Y
TEMP2 = 4
CALL SUBFOO(TEMP1, TEMP2)

Il y avait également une optimisation de compilateur commune appelée «pool littéral», qui consoliderait plusieurs instances de la même constante numérique dans la même variable générée automatiquement. (Plusieurs langues de la famille C l'exigent pour les littéraux de chaîne.) Donc, si vous avez écrit

CALL SUBBAR(4)
CALL SUBBAZ(4)

ce serait traité comme si c'était

FOUR = 4
CALL SUBBAR(FOUR)
CALL SUBBAZ(FOUR)

ce qui semble être une chose parfaitement raisonnable à faire jusqu'à ce que vous ayez un sous-programme qui modifie la valeur de ses paramètres.

SUBROUTINE SUBBAR(X)
    !...lots of code...
    X = 5
    !...lots of code...
END SUBROUTINE SUBBAR

Boom! CALL SUBBAR(4)changé la valeur du 4 dans le pool littéral en 5. Et puis vous vous demandez pourquoi SUBBAZsuppose que vous l'avez passé un 5 au lieu de ce que 4vous avez réellement écrit dans le code.

Les versions plus récentes de Fortran atténuent ce problème en vous permettant de déclarer la INTENTvariable comme INou OUT, et en vous donnant une erreur (ou au moins un avertissement) si vous passez une constante en OUTparamètre.

dan04
la source
5

Dans FORTRAN, lorsqu'une constante est passée à une autre procédure, elle n'est plus protégée. C'est à cela qu'ils se réfèrent. D'autres langages de programmation populaires à la même époque étaient C et Pascal qui n'avaient pas (et n'ont toujours pas) ce problème. Peut-être existe-t-il des langages de programmation plus anciens que je ne connais pas qui ont le même problème.

dj bazzie wazzie
la source
En outre, il fait référence au fait que le pool constant n'était pas dans un segment en lecture seule. Si c'était le cas, et que 4 est passé par référence et modifié par l'appelé, SEGV se produirait sans changer avec succès 4.
Basile Starynkevitch
En effet, tous les systèmes d'exploitation n'avaient pas de segment en lecture seule. Le piège pourrait être utilisé sur DOS par exemple, les systèmes d'exploitation avec des segments en lecture seule (utilisant la mémoire virtuelle) comme UNIX renverraient une erreur de segmentation au moment de l'exécution. Quoi qu'il en soit, le compilateur ne devrait pas le permettre.
dj bazzie wazzie
4
Pascal me manque :(
Gareth
1
Pour être plus précis, FORTRAN passe par référence. Donc, si vous passez une constante comme paramètre de fonction, vous pouvez changer cette valeur pour chaque utilisation de ce nombre.
Gabe
1
Uniquement si cette constante (passée par référence) reste dans un segment de lecture-écriture. S'il se trouve dans un .rodatasegment en lecture seule (comme le font les compilateurs actuels), il ne modifiera pas la constante mais entraînerait un SEGV.
Basile Starynkevitch