Sur la nécessité en informatique

a marqué ce sujet comme résolu.

Dès lors, comment peux-tu pointer le C du doigt ? Si nous ne devions retenir qu’un langage pour apprendre la rigueur du code, l’importance d’un code correct et irréprochable, ce serait probablement le C.

Le C force le programmeur à raisonner sur son code, sous peine de programmes qui ne veulent rien dire (undefined behavior), mais sans fournir aucun outil à l’utilisateur pour vérifier que son raisonnement est correct. Au mieux tu peux utiliser les sanitizers (qui n’existent chez le grand public que depuis une poignée d’année) et valgrind pour te dire: j’ai vérifié que sur mes tests mon programme ne semble pas rencontrer de comportement dangereux à l’exécution (ce qui ne garantit en rien l’absence d’undefined behavior, même sur les chemins testés).

Le résultat en pratique est que tous les gros programmes C sont faux (ne veulent rien dire), mais que l’on maintient collectivement une forme de tolérance en demandant aux compilateurs de ne pas être trop méchants dans ces cas-là, ou en espérant qu’ils ne le deviendront pas dans le futur.

Je préfère utiliser un langage où il est possible d’écrire du code correct, parce que le langage va m’aider à vérifier que mon raisonnement est correct (ou me simplifier la vie pour ne pas me forcer à raisonner sur tel ou tel aspect au risque de me planter), et où les cas incorrects ont des conséquences prédictibles et débuggables (par exemple, un accès hors des bornes d’un tableau se comporte mieux en Python, où on a une exception propre, qu’en C où on peut modifier des structures internes n’importe où dans le programme).

Le résultat en pratique est que tous les gros programmes C sont faux (ne veulent rien dire), mais que l’on maintient collectivement une forme de tolérance en demandant aux compilateurs de ne pas être trop méchants dans ces cas-là, ou en espérant qu’ils ne le deviendront pas dans le futur.

Je te trouve un peu dur en terme de terminologie même si je vois ce que tu veux dire. Je veux dire, un programme qui a des bogues ou qui utilise des comportements indéfinis n’est aps un logiciel qui ne veut rien dire.

Sinon cela revient à dire qu’un livre comportant des fautes de français ne veut rien dire non plus. Ou qu’un livre exploitant du vocabulaire québécois n’est pas valide non plus. C’est un peu absurde, cela n’empêche pas qu’ils signifient quelque chose, même de correct en un sens.

Car beaucoup de cas indéfinis en C le sont au niveau du langage mais ne le sont pas tellement au runtime. Ces cas indéfinis ont été laissés ainsi pour laisser plus de souplesse aux compilateurs d’agir en fonction de la cible matérielle. Maintenant des compilateurs C de référence il n’y en a plus beaucoup (environ 3), ils se comportent tous à peu près pareil sur l’ensemble des architectures. Les cas problématiques sont souvent les petits compilateurs (genre pour microcontrôleur) mais c’est de moins en moins utilisé en pratique et de toute façon on n’exploite qu’un sous ensemble du C et rarement des logiciels aussi complets et lourds que sur PC.

Donc même un comportement indéfini dans le langage et la norme n’est finalement pas si gênant que cela sous entend. Oui c’est un risque que le comportement change àl lavenir ou que la portabilité ne soit pas maximale, mais d’un point de vue pratique le risque reste de nos jours plutôt faible pour de nombreux comportements indéfinis de la norme.

Et pour les logiciels critiques, type contrôle de moteur d’un avion, les limitations sont tellement importantes que seulement un sous ensemble du C est autorisé avec des outils de vérification poussées pour valider le travail. On ne pourrait pas imaginer aujourd’hui utiliser un programme écrit en Java, Python ou Rust malgré les garanties apportées car il y aurait bien trop de choses à valider encore derrière qui sont trop complexes.

où les cas incorrects ont des conséquences prédictibles et débuggables (par exemple, un accès hors des bornes d’un tableau se comporte mieux en Python, où on a une exception propre, qu’en C où on peut modifier des structures internes n’importe où dans le programme).

Le C a cet avantage est que pour le bas niveau il reste assez simple, peu verbeux et très souple. Pour avoir écrit du code bas niveau en Rust par exemple j’ai trouvé l’exercice pénible à cause des restrictions imposées sans un gain vraiment tangiblecar tu as des unsafe plus ou moins inévitables dans ce genre de contextes. Pour le haut niveau cela reste évidemment une bonne approche d’utilise Rust (ou autres) que C.

Le C garde son intérêt pour le bas niveau même aujourd’hui, mais il est évident que c’est le seul domaine où il subsiste en tant qu’assembleur portable et un peu plus confortable à utiliser.

+0 -0

Je veux dire, un programme qui a des bogues ou qui utilise des comportements indéfinis n’est aps un logiciel qui ne veut rien dire. Sinon cela revient à dire qu’un livre comportant des fautes de français ne veut rien dire non plus. Ou qu’un livre exploitant du vocabulaire québécois n’est pas valide non plus. C’est un peu absurde, cela n’empêche pas qu’ils signifient quelque chose, même de correct en un sens.

La comparaison n’est pas du tout valide. Quand on dit qu’un bout de code en C "a un comportement indéfini", en fait cela veut dire que le programme tout entier a un comportement indéfini. C’est un terme technique précis défini par les standards qui définissent le langage C, avec un sens précis. Cela veut dire que le comportement du programme n’est pas défini, et donc que le programme n’a pas de sens, il peut faire n’importe quoi. En particulier, le compilateur a le droit de faire absolument ce qu’il veut — en pratique un compilateur s’autorise à toujours supposer que le comportement d’un programme est toujours défini; quand on écrit un programme indéfini, il compile comme si c’était un autre programme défini qui l’arrange qu’on lui avait passé en entrée.

(Attention à ne pas confondre "comportement indéfini" et "comportement spécifié par l’implémentation", une autre situation décrite par le standard où le programme peut avoir plusieurs comportements possibles (et n’est donc pas portable) mais tous ces comportements sont clairement définis.)

Il y a des articles de vulgarisation raisonnables sur le sujet, par exemple le post de John Regher A Guide to Undefined Behavior in C and C++, Part 1

C and C++ are unsafe in a strong sense: executing an erroneous operation causes the entire program to be meaningless, as opposed to just the erroneous operation having an unpredictable result. In these languages erroneous operations are said to have undefined behavior. [..] since the program has no meaning at all, the implementation can do whatever it likes.

Je maintiens ma comparaison et mon propos. Tu parles de la norme d’un point de vue théorique, je te parle de la pratique.

Oui, je suis au courant qu’un UB implique que le compilateur peut faire ce qu’il veut. Cela ne signifie pas pour autant que le programme n’a vraiment aucun sens, et encore moins en entier.

Car en pratique le compilateur a un comportement déterministe et défini quand il rencontre ces cas de figures. Donc oui le programme reste fragile, la moindre évolution du compilateur peut faire changer le comportement du programme compilé derrière, mais dans les faits si tu te cantonnes à utiliser le même compilateur sur la même architecture le comportement sera fixe et donc aura un sens. Peut être pas le sens voulu exactement par le développeur mais il aura un sens.

Sans compter que le comportement indéfini, en général, n’impacte que la zone où il est rencontré. Cela signifie que si tu ne t’aventures pas dans une fonction où un tel comportement est écrit, cela ne posera pas de problèmes non plus. Donc le reste du programme garde son sens s’il est parfaitement écrit.

Je précise, au cas où, que je suis d’accord avec toi. Le C reste un langage désuet avec trop de comportements non spécifiés dans le standard. Mais cela fait aussi sa force en tant qu’assembleur un peu plus évolué. Cependant je trouve que tu es trop extrémiste en considérant que cette non spécification implique forcément que tout programme entrant dans ce genre de cas n’a aucun sens. En pratique ce n’est pas aussi vrai que tu le prétends.

+1 -0

Moi j’ai surtout l’impression que tu relaies des idées globalement fausses sur le langage C, qui se sont construites sur des années passées où (1) les compilateurs étaient beaucoup moins agressifs qu’aujourd’hui et il était donc plus facile d’ignorer le problème et (2) on n’avait souvent pas de meilleurs outils à disposition pour nos besoins.

Oui, il est possible d’écrire des millions de lignes de code C qui font des choses utiles dans la vraie vie (même si techniquement ces programmes n’ont pas de sens), d’ailleurs on l’a collectivement fait. Ça nous a aussi coûté un paquet d’emmerdes (buffer overflows, trous de sécurité énormés, segfaults à gogo, exploitation facile des undefined-code-execution) qui rendent notre couche technologique commune fragile, un nid à bug et à failles de sécurité.

Il est peut-être temps d’arrêter les frais et, surtout dans le contexte d’un topic où on parle de choix radicaux en informatique, de dire clairement qu’écrire un nouveau programme en C aujourd’hui est une mauvaise idée. Je reviens à mon premier commentaire sur le C dans ce topic, qui a lancé cette sous-discussion:

[…] nous, programmeurs, devrions mettre en place un cadre éthique fort pour justifier d’investir des efforts dans la correction du code. (En commençant par éviter les technologes qui encouragent les bugs, comme C par exemple.)

Si tu es d’accord avec le fait que, techniquement, les gros programmes C sont tous indéfinis, et que cela pose des problèmes, alors je ne comprends pas trop la finalité de tes messages dans cette discussion.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte