340 votes

Qu'est-ce que la sécurité de type ?

Que signifie "sûr pour les types" ?

0 votes

293voto

FlySwat Points 61945

La sécurité des types signifie que le compilateur valide les types lors de la compilation et génère une erreur si vous essayez d'attribuer un type incorrect à une variable.

Quelques exemples simples :

// Fails, Trying to put an integer in a string
String one = 1;
// Also fails.
int foo = "bar";

Cela s'applique également aux arguments de méthode, puisque vous leur transmettez des types explicites :

int AddTwoNumbers(int a, int b)
{
    return a + b;
}

Si j'essayais d'appeler ça en utilisant :

int Sum = AddTwoNumbers(5, "5");

Le compilateur émettrait une erreur, car je passe une chaîne de caractères ("5"), alors qu'il attend un nombre entier.

Dans un langage faiblement typé, comme le javascript, je peux faire ce qui suit :

function AddTwoNumbers(a, b)
{
    return a + b;
}

si je l'appelle comme ça :

Sum = AddTwoNumbers(5, "5");

Javascript convertit automatiquement le 5 en une chaîne de caractères, et renvoie "55". Ceci est dû au fait que javascript utilise le signe + pour la concaténation des chaînes de caractères. Pour qu'il tienne compte du type, il faudrait faire quelque chose du genre :

function AddTwoNumbers(a, b)
{
    return Number(a) + Number(b);
}

Ou, peut-être :

function AddOnlyTwoNumbers(a, b)
{
    if (isNaN(a) || isNaN(b))
        return false;
    return Number(a) + Number(b);
}

si je l'appelle comme ça :

Sum = AddTwoNumbers(5, " dogs");

Javascript convertit automatiquement les 5 en une chaîne de caractères, et les ajoute, pour retourner "5 chiens".

Tous les langages dynamiques ne sont pas aussi indulgents que le javascript (en fait, un langage dynamique n'implique pas implicitement un langage à typage lâche (voir Python)), certains d'entre eux vous donneront même une erreur d'exécution en cas de casting de type invalide.

Bien que ce soit pratique, cela vous expose à de nombreuses erreurs qui peuvent être facilement manquées et identifiées uniquement en testant le programme en cours d'exécution. Personnellement, je préfère que mon compilateur me dise si j'ai fait cette erreur.

Maintenant, revenons à C#...

C# prend en charge une fonctionnalité du langage appelée covariance Cela signifie essentiellement que vous pouvez remplacer un type de base par un type enfant sans provoquer d'erreur, par exemple :

 public class Foo : Bar
 {
 }

Ici, j'ai créé une nouvelle classe (Foo) qui sous-classe Bar. Je peux maintenant créer une méthode :

 void DoSomething(Bar myBar)

Et appelez-le en utilisant soit un Foo, soit un Bar comme argument, les deux fonctionneront sans causer d'erreur. Cela fonctionne parce que C# sait que toute classe enfant de Bar implémentera l'interface de Bar.

Cependant, vous ne pouvez pas faire l'inverse :

void DoSomething(Foo myFoo)

Dans cette situation, je ne peux pas passer Bar à cette méthode, car le compilateur ne sait pas que Bar implémente l'interface de Foo. En effet, une classe enfant peut être (et sera généralement) très différente de la classe parent.

Bien sûr, j'ai maintenant dépassé le cadre de la question initiale, mais c'est bon à savoir :)

36 votes

Je pense que cette réponse est erronée : la sécurité des types n'est pas nécessairement appliquée au moment de la compilation. Je crois savoir que Scheme, par exemple, est considéré comme sûr du point de vue du type, mais qu'il est vérifié dynamiquement (la sécurité du type est appliquée au moment de l'exécution). Je paraphrase ici l'introduction de Types and Programming Languages, de Benjamin C. Pierce.

17 votes

Ce que vous décrivez est appelé polymorphisme, et non covariance. La covariance est utilisée dans les génériques.

0 votes

@NicolasRinaudo note que l'écart entre les langages dynamiques et statiques est en train d'être érodé par la compilation dynamique et la précompilation pour les langages "interprétés", et par la réflexion dans les langages "compilés". La réflexion permet, par exemple, le typage des canards au moment de l'exécution, de sorte qu'un langage compilé peut dire "hé, ceci a une méthode Quack(), je vais l'appeler et voir ce qui se passe". Les langages de type Pascal disposent également souvent d'une vérification (facultative) des débordements au moment de l'exécution, ce qui conduit à ces erreurs du "compilateur" qui se produisent au moment de l'exécution "cannot fit integer supplied into 8 bit destination {core dump}" (Impossible de faire entrer un entier dans une destination de 8 bits {core dump}).

93voto

Nicolas Rinaudo Points 1947

La sécurité des types ne doit pas être confondue avec le typage statique/dynamique ou le typage fort/faible.

Un langage sûr du point de vue du type est un langage dans lequel les seules opérations que l'on peut exécuter sur les données sont celles qui sont tolérées par le type des données. Autrement dit, si vos données sont de type X y X ne supporte pas l'opération y alors le langage ne vous permettra pas d'exécuter y(X) .

Cette définition ne fixe pas de règles sur quand c'est vérifié. Cela peut se faire au moment de la compilation (typage statique) ou au moment de l'exécution (typage dynamique), généralement par le biais d'exceptions. Il peut s'agir d'un peu des deux : certains langages à typage statique vous permettent de transférer des données d'un type à l'autre, et la validité des transferts doit être vérifiée à l'exécution (imaginez que vous essayez de transférer un objet de type Object à un Consumer - le compilateur n'a aucun moyen de savoir si c'est acceptable ou non).

La sécurité de type n'est pas nécessairement synonyme de typage fort - certains langages sont notoirement faiblement typés, mais peuvent tout de même être considérés comme sûrs. Prenez Javascript, par exemple : son système de types est aussi faible que possible, mais toujours strictement défini. Il permet le casting automatique de données (par exemple, des chaînes de caractères en ints), mais dans le cadre de règles bien définies. À ma connaissance, il n'existe aucun cas où un programme Javascript se comporterait de manière indéfinie, et si vous êtes assez intelligent (je ne le suis pas), vous devriez être capable de prédire ce qui se passera en lisant du code Javascript.

Le langage de programmation C est un exemple de langage non sécurisé par les types : la lecture/écriture d'une valeur de tableau en dehors des limites du tableau a un comportement non défini. par spécification . Il est impossible de prévoir ce qui va se passer. Le C est un langage qui possède un système de types, mais qui n'est pas sûr en termes de types.

1 votes

Quels sont les autres exemples de langages non sûrs du point de vue du type ? Que voulez-vous dire par "l'écriture d'une valeur de tableau en dehors des limites du tableau a un comportement non défini par spécification. Il est impossible de prédire ce qui se passera". Comme en Javascript, le résultat sera indéfini, n'est-ce pas ? Ou vraiment tout peut arriver. Pouvez-vous donner un exemple ?

1 votes

@AkshayrajKore bien sûr. Les tableaux sont des pointeurs de mémoire, donc en écrivant hors limites, vous pourriez écraser les données d'un autre programme - ce qui peut ne rien faire, planter le programme, provoquer l'effacement de votre disque dur - c'est indéfini et dépend de la personne qui lit ce morceau de mémoire et de la façon dont elle réagira.

0 votes

@Nicolas Rinaudo Ce n'est pas correct. Vous devriez vous renseigner sur la mémoire virtuelle. Chaque processus possède son propre espace d'adressage virtuel, de sorte qu'un processus ne peut pas "écraser les données d'un autre programme" de cette manière.

45voto

Gr3go Points 550

La sécurité des types n'est pas seulement une contrainte de temps de compilation, mais un élément essentiel de la stratégie de l'entreprise. temps de fonctionnement contrainte. J'ai l'impression que même après tout ce temps, nous pouvons apporter plus de clarté à ce sujet.

Il y a 2 problèmes principaux liés à la sécurité du type. La mémoire** et le type de données (avec ses opérations correspondantes).

Mémoire**

A char nécessite généralement 1 octet par caractère, ou 8 bits (cela dépend du langage, Java et C# stockent des caractères unicode qui nécessitent 16 bits). Un site int nécessite 4 octets, ou 32 bits (en général).

Visuellement :

char: |-|-|-|-|-|-|-|-|

int : |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Un langage sans risque de type ne permet pas l'insertion d'un int dans un char à l'endroit suivant temps d'exécution (ce qui devrait déclencher une sorte d'exception de classe ou d'absence de mémoire). Cependant, dans un langage non sécurisé, vous écraseriez les données existantes dans trois autres octets adjacents de la mémoire.

int >> char:

|-|-|-|-|-|-|-|-| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?| |?|?|?|?|?|?|?|?|

Dans le cas ci-dessus, les 3 octets à droite sont écrasés, donc tous les pointeurs vers cette mémoire (disons 3 caractères consécutifs) qui s'attendent à obtenir une valeur prévisible de caractère auront maintenant des déchets. Cela entraîne undefined dans votre programme (ou pire, peut-être dans d'autres programmes selon la façon dont le système d'exploitation alloue la mémoire - très peu probable de nos jours).

** Bien que ce premier problème ne concerne pas techniquement le type de données, les langages à sécurité de type l'abordent de manière inhérente et décrivent visuellement le problème à ceux qui ne savent pas à quoi ressemble l'allocation de mémoire.

Type de données

Le problème plus subtil et plus direct est celui où deux types de données utilisent la même allocation de mémoire. Prenons l'exemple d'un int par rapport à un int non signé. Les deux ont 32 bits. (Il pourrait tout aussi bien s'agir d'un char[4] et d'un int, mais le problème le plus courant est celui de l'uint par rapport à l'int).

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

|-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-| |-|-|-|-|-|-|-|-|

Un langage non sécurisé par des types permet au programmeur de faire référence à un espace de 32 bits correctement alloué, mais lorsque la valeur d'un unsigned int est lue dans l'espace d'un int (ou vice versa), nous avons à nouveau undefined comportement. Imaginez les problèmes que cela pourrait causer dans un programme bancaire :

"Mec ! J'ai fait un découvert de 30 $ et maintenant il me reste 65 506 $ !"

...'bien sûr, les programmes bancaires utilisent des types de données beaucoup plus importants ;) LOL !

Comme d'autres l'ont déjà souligné, le problème suivant concerne les opérations de calcul sur les types. Ce sujet a déjà été suffisamment traité.

Vitesse et sécurité

Aujourd'hui, la plupart des programmeurs n'ont jamais à s'inquiéter de ce genre de choses, sauf s'ils utilisent un langage comme C ou C++. Ces deux langages permettent aux programmeurs de violer facilement la sécurité des types au moment de l'exécution (référencement direct en mémoire), malgré les efforts des compilateurs pour minimiser le risque. CEPENDANT, tout n'est pas mauvais.

L'une des raisons pour lesquelles ces langages sont si rapides sur le plan informatique est qu'ils n'ont pas à vérifier la compatibilité des types lors des opérations d'exécution comme, par exemple, Java. Ils partent du principe que le développeur est un bon être rationnel qui n'ajoutera pas une chaîne de caractères et un nombre entier ensemble et pour cela, le développeur est récompensé par la vitesse/efficacité.

0 votes

Il est vrai que le fait d'assurer la sécurité du type impose des contraintes à la vitesse. Mais il est vraiment important que la sécurité de type soit assurée étant donné que le code C/C++ est plus sensible aux attaques de type BufferOverflow et autres attaques connexes. Les menaces de telles attaques sont réduites en garantissant la sécurité de type.

29voto

ididak Points 4208

De nombreuses réponses confondent la sécurité des types avec le typage statique et le typage dynamique. Un langage dynamiquement typé (comme smalltalk) peut également être sûr au niveau des types.

Une réponse courte : un langage est considéré comme sûr du point de vue des types si aucune opération ne conduit à un comportement non défini. Beaucoup considèrent que l'exigence de conversions de types explicites est nécessaire pour qu'un langage soit strictement typés, car les conversions automatiques peuvent parfois conduire à des comportements bien définis mais inattendus/intuitifs.

1 votes

Attendez, votre définition de la sécurité de type ne contient pas un seul mot "type" :D if no operation leads to undefined behavior .

1 votes

Par ailleurs, je ne suis pas d'accord avec une telle définition. Je pense que la sécurité de type signifie exactement 1. l'existence de types 2. la connaissance de ceux-ci par le compilateur, et les vérifications appropriées bien sûr.

15voto

Kekule Points 191

Un langage de programmation qui est "sûr au niveau des types" signifie les choses suivantes :

  1. Vous ne pouvez pas lire des variables non initialisées.
  2. Vous ne pouvez pas indexer les tableaux au-delà de leurs limites.
  3. Vous ne pouvez pas effectuer des castings de type non vérifiés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X