Encore une fois, j'ai pris deux jours pour écrire et déboguer seulement quatre cents lignes de code de bibliothèque système, l'idée est née - «comme si c'était bien si les programmes étaient écrits d'une manière moins pénible».
Et tout d'abord, puisque le débogage prend beaucoup plus de temps que l'écriture de code - vous avez besoin d'une protection contre le fou (vous y compris) au stade de l'écriture. Et je voudrais le recevoir du langage de programmation utilisé (YP).
Bien sûr, nous devons inventer un nouveau, le meilleur YaP!Non, nous allons d'abord essayer d'exprimer nos souhaits et de regarder ce que nous avons déjà inventé.
Alors, ce que j'aimerais recevoir:
- Résistance aux erreurs humaines, élimination des ambiguïtés lors de la compilation
- Résistance d'entrée
- Résistance à l'endommagement du programme ou des données - défaillance du support, piratage
- En même temps, tout le monde a une syntaxe et des fonctionnalités plus ou moins tolérables
Les domaines d'application sont les machines, le transport, les systèmes de contrôle industriels, l'IoT, l'intégration, y compris les téléphones.
Il n'est guère nécessaire pour le Web, il est construit (pour l'instant) sur le principe de «quitter et redémarrer» (tirer et oublier).
Assez rapidement, vous pouvez arriver à la conclusion que le langage doit être compilé (au moins Pi-compilé) afin que toutes les vérifications soient exécutées au maximum au stade de la compilation sans VS (Versus, ci-après l'opposition négative) "oh, cet objet n'a pas une telle propriété" en exécution. Même le scriptage des descriptions d'interface rend déjà obligatoire de couvrir complètement ces scripts avec des tests.
Personnellement, je ne veux pas payer avec mes ressources (y compris de l'argent pour du matériel plus rapide et plus cher) pour l'interprétation, il est donc conseillé d'avoir une compilation JIT minimum.
Donc, les exigences.
Tolérance aux erreurs humaines
Après avoir parcouru avec diligence les Talmuds de PVS-Studio, j'ai découvert que les erreurs les plus courantes sont les fautes de frappe et le copier-coller incomplet.
J'ajouterai également quelques incidents de mon expérience et rencontrés dans divers ouvrages, à titre d'exemples négatifs. De plus, les règles MISRA C ont été mises à jour en mémoire.
Après y avoir réfléchi un peu, je suis parvenu à la conclusion que les linters appliqués ex-facto souffrent d'une "erreur de survivant", car dans les anciens projets de graves erreurs ont déjà été corrigées.
a) Débarrassez-vous des noms similaires
- Un contrôle strict doit être effectué sur la visibilité des variables et des fonctions. Une fois scellé, vous pouvez utiliser un identifiant d'une portée plus générale, au lieu de celui souhaité
- Utilisez des noms insensibles à la casse. (VS) "Appelons la fonction en tant que variable, uniquement dans Camelcase", puis comparons avec quelque chose - en C, cela peut être fait (nous obtenons l'adresse de la fonction, qui est un certain nombre)
- Les noms avec une différence de 1 lettre devraient provoquer un avertissement (sans doute, vous pouvez le mettre en surbrillance dans l'EDI) mais une erreur de copier-coller très courante .x, .y, .w, .h.
- nous n'autorisons pas le même nom pour différentes entités - s'il existe une constante avec ce nom, il ne devrait pas y avoir de variable du même nom ou du même nom de type
- il est hautement souhaitable de vérifier la dénomination de tous les modules du projet - il est facile de confondre, surtout si différentes personnes écrivent des modules différents
b) Une fois mentionné - il doit y avoir modularité et de préférence hiérarchique - un projet VS de 12 000 fichiers dans un répertoire est un enfer de recherche.
La modularité reste nécessaire pour décrire les structures d'échange de données entre les différentes parties (modules, programmes) d'un projet. VS J'ai rencontré une erreur due à un alignement différent des nombres dans la structure d'échange du récepteur et de l'émetteur.
- Pour exclure la possibilité de doublon de liaison (mise en page).
c) Ambiguïtés
- Il doit y avoir un ordre spécifique d'appels de fonction. Lorsque vous écrivez X = funcA () + fB () ou Fn (funcA (), fB (), callC ()) - vous devez comprendre que la personne s'attend à recevoir les calculs dans l'ordre écrit, (VS) et non pas comme l'optimiseur l'a pensé.
- Exclure les opérateurs similaires. Et pas comme en C: + ++, <<<, | ||, & &&, = ==
- Il est conseillé d'avoir quelques opérateurs compréhensibles et avec une priorité évidente. Bonjour de l'opérateur ternaire.
- Le dépassement des opérateurs est plutôt préjudiciable. Vous écrivez i: = 2, mais (VS) cela provoque en fait une création implicite d'un objet pour lequel il n'y a pas assez de mémoire, et le disque se bloque lors de l'échange et votre satellite se bloque sur Mars :-(
En fait, par expérience personnelle, j'ai observé un crash sur la ligne ConnectionString = «DSN», il s'est avéré être un setter qui a ouvert la base de données (et le serveur n'était pas visible sur le réseau).
- Nous devons initialiser toutes les variables avec des valeurs par défaut.
- De plus, l'approche POO sauve de l'oubli la réaffectation de tous les champs de l'objet dans une nouvelle centième fonction.
- Le système de type doit être sûr - vous devez contrôler les dimensions des objets affectés - protection contre l'écrasement de la mémoire, débordement arithmétique de type 65535 + 1, perte de précision et de signification lors de la conversion, à l'exclusion des comparaisons incomparables - car l'entier 2 n'est pas égal à 2.0 dans le cas général.
Et même une division typique par 0 peut donner un + INF très défini, au lieu d'une erreur, une définition exacte du résultat est nécessaire.
Résistance d'entrée
- Le programme doit fonctionner sur toutes les données d'entrée et de préférence, approximativement en même temps. (VS) Bonjour à Android avec une réaction au bouton du combiné de 0,2 s à 5 s; c'est bien qu'Android ne conduise pas l'ABS automobile.
Par exemple, un programme doit traiter correctement à la fois 1 Ko de données et 1 To sans avoir épuisé les ressources du système.
- Il est très souhaitable d'avoir une gestion des erreurs fiable et sans ambiguïté dans RAII qui n'entraîne pas d'effets secondaires (fuites de ressources, par exemple). (VS) Une chose très drôle est la fuite des poignées, qui peut se manifester après plusieurs mois.
- Ce serait bien de se protéger du débordement de pile - désactiver la récursivité.
- Le problème du dépassement du volume disponible avec la mémoire requise, une croissance incontrôlée de la consommation due à la fragmentation lors de l'allocation / désallocation dynamique. Si la langue a un runtime dépendant du tas, la chose est probablement mauvaise - bonjour STL et Phobos. (VS) Il y avait une histoire avec un ancien temps C de Microsoft, qui n'a pas restitué la mémoire au système de manière adéquate, en raison de laquelle msbackup s'est écrasé sur de gros volumes (pour cette période).
- Nous avons besoin d'un bon travail en toute sécurité avec des chaînes - pas limité par les ressources. Il dépend fortement de l'implémentation (immuable, COW, tableaux R / W)
- Dépassement du temps de réaction du système, indépendant du programmateur. Il s'agit d'un problème typique de garbage collector. Bien qu'ils économisent de certaines erreurs de programmation - d'autres apportent - mal diagnostiqués.
- Dans une certaine classe de tâches, il s'avère que vous pouvez vous passer de mémoire dynamique ou en la sélectionnant une fois au démarrage.
- Pour contrôler la sortie au-delà des limites de la baie, et il est parfaitement acceptable d'écrire un avertissement d'exécution et de l'ignorer. Il s'agit très souvent d'erreurs non critiques.
- Avoir une protection contre les accès à une section mémoire qui n'a pas été initialisée par le programme, y compris à la région nulle, et à l'espace d'adressage de quelqu'un d'autre.
- Interprètes, JIT - les couches supplémentaires réduisent la fiabilité, il y a des problèmes avec la collecte des ordures (un sous-système très complexe - il fera ses erreurs), et avec un temps de réponse garanti. Nous l'excluons, mais il y a en principe la Java Micro Edition (où tant de choses sont coupées de Java que moi seul reste, il y avait un
article intéressant
de dernasherbrezon (désolé, tourné) et le .NET Micro Framework avec C #.
Cependant, par considération, ces options ont disparu:
- .NET Micro s'est avéré être un interprète ordinaire (barré par la vitesse);
- Java Micro ne convient qu'aux applications intégrées, car il est trop castré par l'API, et vous devrez passer au moins à SE Embedded pour le développement, qui a déjà été fermé ou Java normal, qui est trop monstrueux et imprévisible en réponse.
Cependant, il existe encore des options , et bien que cela ne ressemble pas à un blanc pour une fondation viable, il peut être comparé à d'autres langues, même obsolètes ou avec certains inconvénients.
- Résistance au travail multi-thread - protection des données privées d'un flux, et mécanismes garantissant l'échange entre les flux. Un programme avec 200 threads peut ne pas fonctionner du tout comme dans deux.
- La programmation contractuelle et les tests unitaires intégrés aident également à dormir paisiblement.
Résistance à l'endommagement du programme ou des données - défaillance du support, piratage
- Le programme doit être entièrement chargé en mémoire - sans charger les modules, surtout à distance.
- Effacer la mémoire à la sortie (et pas seulement l'allocation)
- Surveillance du débordement de pile, des zones variables, en particulier des chaînes.
- Redémarrez après un échec.
Soit dit en passant, l'approche lorsque le runtime a sa propre journalisation, et ne montre pas seulement que le renard du nord et stackrace, je suis très impressionné.
Langues - et tableau de conformité
À première vue, pour l'analyse, nous prenons des PL sûrs spécialement conçus:
- Oberon actif
- Ada
- BetterC (sous-ensemble dlang)
- IEC 61131-3 ST
- Safe-c
Et passez-les en revue en fonction des critères ci-dessus.
Mais c'est déjà le volume de l'article de suite, si le karma le permet.
Avec les facteurs susmentionnés mis en évidence dans le tableau, eh bien, peut-être que quelque chose d'autre sensible sera glané des commentaires.
Quant aux autres langages intéressants - C ++, Crystal, Go, Delphi, Nim, Red, Rust, Zig (ajouter au goût) alors je le laisserai à ceux qui souhaitent remplir le tableau de correspondance.
Avertissements:
- En principe, si un programme, disons en Python, consomme 30 Mo et que les exigences de réaction sont de quelques secondes, et que le micro-ordinateur a 600 Mo de mémoire libre et 600 MHz pour cent, alors pourquoi pas? Seul un tel programme sera fiable avec une certaine probabilité (bien que 96%), pas plus.
- De plus, le langage devrait essayer d'être pratique pour le programmeur - sinon personne ne l'utilisera. De tels articles "Je suis venu avec le langage de programmation idéal pour qu'il ne me soit plus pratique que d'écrire" ne sont pas rares non plus dans Habré, mais il s'agit d'autre chose.