Partager via


Conversions de type et sécurité de type (C++ moderne)

Ce document identifie les problèmes courants de conversion de type et décrit la façon de les éviter de votre code C++.

Lorsque vous écrivez le programme c++, il est important de vérifier que c'est de type sécurisé.Cela signifie que chaque variable, argument de fonction, et la valeur de retour de fonction stocke un type acceptable de données, et que les opérations qui impliquent des valeurs de différents types « ont un sens » et n'entraînent pas la perte de données, la traduction incorrecte des modèles binaires, ou l'altération de la mémoire.Un programme qui convertit jamais explicitement ou implicitement des valeurs d'un type en un autre est de type sécurisé par définition.Toutefois, les conversions de type, même conversions potentiellement dangereuses, sont parfois requises.Par exemple, vous devrez peut-être stocker le résultat d'une opération de virgule flottante dans une variable de type int, ou vous devrez peut-être passer la valeur dans int non signé à une fonction qui prend intsigné.Les deux exemples montrent des conversions potentiellement dangereuses car ils peuvent provoquer la perte de données ou la réinterprétation d'une valeur.

Lorsque le compilateur détecte une conversion potentiellement dangereuse, il émet une erreur ou un avertissement.Une erreur s'arrête la compilation ; un avertissement permet à la compilation pour continuer mais indiquer une erreur possible dans le code.Toutefois, même si les compilations de programme sans avertissements, il peut toujours contenir le code qui conduit aux conversions de types implicites qui produisent des résultats incorrects.Le type erreur peut également être entré par les conversions explicites, ou des casts, dans le code.

Conversions de types implicites

Lorsqu'une expression contient des opérandes de différents types intégrés, et aucun cast explicite n'est présent, le compilateur utilise des conversions standard prédéfinis pour convertir l'un des opérandes afin que les types correspondent.Le compilateur essaie les conversions dans une séquence bien définie jusqu'à ce qu'il réussisse.Si la conversion sélectionnée est une promotion, le compilateur n'émet pas d'avertissement.Si la conversion est limiter, le compilateur émet un avertissement sur la perte possible de données.Si la perte de données réelle se produit dépend des valeurs réelles impliquées, mais nous vous recommandons de traiter cet avertissement comme une erreur.Si un type défini par l'utilisateur est impliqué, les tests de compilateur utilise les conversions que vous avez spécifiées dans la définition de classe.Si elle ne peut pas trouver de conversion acceptable, le compilateur émet une erreur et ne compile pas le programme.Pour plus d'informations sur les règles qui régissent les conversions standard, consultez Conversions standard.Pour plus d'informations sur les conversions définies par l'utilisateur, consultez l' Conversions définies par l'utilisateur (C++/CLI).

Hh279667.collapse_all(fr-fr,VS.110).gifConversions étendues (promotion)

Dans une conversion étendue, une valeur dans une plus petite variable est assignée à une plus grande variable sans perte de données.Étant donné que les conversions étendues sont toujours sûres, le compilateur les exécute en mode silencieux et n'émet pas d'avertissements.Les conversions suivantes sont des conversions étendues.

From

Pour

Tout type intégral signé ou non signés sauf long long ou __int64

double

bool ou char

Tout autre type intégré

short ou wchar_t

int, long, long long

int, long

long long

float

double

Hh279667.collapse_all(fr-fr,VS.110).gifConversions restrictives (conversion)

Le compilateur exécute des conversions restrictives implicitement, mais vous avertit sur la perte de données.Prenez ces avertissements très sérieux au.Si vous êtes certain qu'aucune perte de données ne se produit parce que les valeurs de la variable plus grande s'adapteront toujours dans la variable plus petite, puis ajoutez un cast explicite afin que le compilateur n'émette plus un avertissement.Si vous n'êtes pas sûr que la conversion est sécurisée, ajoutez à votre code un certain type de contrôle d'exécution de gérer la perte possible de données afin qu'elle ne fasse pas générer votre programme des résultats incorrects.Pour les suggestions sur la manière de gérer ce scénario, consultez Comment : conversions restrictives de handle (C++).

Une conversion d'un type à virgule flottante à un type intégral est une conversion restrictive parce que la partie fractionnaire de la valeur à virgule flottante est ignorée et détruite.

L'exemple de code suivant illustre certaines conversions restrictives implicites, et les avertissements que le compilateur émet pour elles.

   int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
   wchar_t wch = 'A'; //OK
   char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
                 // to 'char', possible loss of data
   unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
                              // 'int' to 'unsigned char'
   int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
                 // 'int', possible loss of data
   int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
                // 'int', possible loss of data

Hh279667.collapse_all(fr-fr,VS.110).gifSigné (conversions non signés

Un type intégral signé et son équivalent non signés sont toujours la même taille, mais ils diffèrent dans la façon dont le modèle binaire est interprète pour la transformation de valeur.L'exemple de code suivant illustre ce qui se produit lorsque le même modèle binaire est interprète comme une valeur signée et comme valeur non signée.Le modèle binaire stocké dans les deux num et num2 ne change jamais de ce qui est indiqué dans l'illustration précédente.

   using namespace std;
   unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
   short num2 = num;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

   // Go the other way.
   num2 = -1;
   num = num2;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

Notez que les valeurs sont réinterprétées dans les deux sens.Si votre programme produit des résultats étranges dans lesquels le signe de la valeur est inversé de ce que vous attendez, recherchez les conversions implicites entre les types intégraux signés et non signés.Dans l'exemple suivant, le résultat de l'expression (0 à 1) est implicitement converti d' int à unsigned int lorsqu'il stocker dans num.Cela entraîne le modèle binaire d'être réinterprété.

   unsigned int u3 = 0 - 1;
   cout << u3 << endl; // prints 4294967295

Le compilateur ne signale pas sur les conversions implicites entre les types intégraux signés et non signés.Par conséquent, nous vous recommandons d'éviter des conversions signer-à- non signées complètement.Si vous ne pouvez pas les éviter, puis ajoutez à votre code un contrôle d'exécution à le déterminer si la valeur est convertie est supérieur ou égal à zéro et inférieure ou égale à la valeur maximale du type signé.Les valeurs dans cet intervalle transfèreront de signé à non signé ou non signé à archivé sans être réinterprété.

Hh279667.collapse_all(fr-fr,VS.110).gifConversions de pointeur

Dans beaucoup d'expressions, un tableau de style c est implicitement convertie en un pointeur vers le premier élément du tableau, et les conversions constantes peuvent se produire en silence.Bien que ce soit utile, il est également potentiellement sujet aux erreurs.Par exemple, l'exemple de code mal conçu suivant apparaît absurde, mais il compile dans Visual C++ et produit un résultat « p ».D'abord, le littéral constante de chaîne « d'aide » est converti en char* qui indique le premier élément du tableau ; ce pointeur est ensuite incrémenté par trois éléments afin qu'il maintenant des points au dernier élément « p ».

char* s = "Help" + 3;

Conversions explicites (casts)

À l'aide d'une opération de cast, vous pouvez instruire le compilateur convertir une valeur d'un type en un autre type.Le compilateur déclenche une erreur dans certains cas si les deux types sont complètement non liés, mais dans d'autres cas elle ne lève pas d'erreur même si l'opération n'est pas de type sécurisé.Utilisez les casts avec modération car une conversion d'un type en un autre est une source possible d ' erreur de programmation.Toutefois, les casts sont parfois requis, et tous les casts sont aussi dangereux.Une utilisation efficace d'un cast est lorsque votre code effectue une conversion restrictive et vous savez que la conversion n'est pas générer votre programme des résultats incorrects.En effet, cela indique au compilateur que vous savez que vous effectuez et pour cesser de vous tracasser avec les avertissements le concernant.Une autre utilisation est d'effectuer un cast d'une classe dérivée pointeur vers une classe de base pointeur.Une autre utilisation est d'effectuer un cast loin const- ness d'une variable pour le passer à une fonction qui requiert un argument non d'const .La plupart de ces opérations casts impliquent du risque.

Dans la programmation de style C, le même opérateur de cast de style C est utilisé pour tous les types de casts.

(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax

L'opérateur de cast de style C est identique à l'opérateur d'appel () et est par conséquent inapperçu dans le code et facile à donner sur.Les deux sont incorrects car il est difficile de reconnaître d'un coup d'œil ou recherchez, et elles sont assez disparates pour appeler n'importe quelle combinaison d' static, d' const, et d' reinterpret_cast.L'illustration ce qu'un cast à l'ancien fait réellement peut être difficile et sujette aux erreurs.Pour toutes ces raisons, lorsqu'un cast est requis, nous vous recommandons d'utiliser l'un des opérateurs de conversion suivants C++, qui sont dans certains cas plus de type sécurisé, et qui expriment beaucoup plus explicitement l'intention de programmation :

  • static_cast, pour les casts qui sont contrôlés au moment de la compilation uniquement.static_cast retourne une erreur si le compilateur détecte que vous essayez d'effectuer un cast entre types qui sont complètement incompatibles.Vous pouvez également l'utiliser pour effectuer un cast entre le pointeur de base et pointeur dérivé, mais le compilateur ne peut pas toujours indiquer si de ces conversions sont sécurisées au moment de l'exécution.

       double d = 1.58947;
       int i = d;  // warning C4244 possible loss of data
       int j = static_cast<int>(d);       // No warning.
       string s = static_cast<string>(d); // Error C2440:cannot convert from
                                          // double to std:string
    
       // No error but not necessarily safe.
       Base* b = new Base();
       Derived* d2 = static_cast<Derived*>(b);
    

    Pour plus d'informations, consultez l' static_cast.

  • dynamic_cast, pour sécurisé, le runtime) a activé les casts de pointeur base à pointeur dérivé.dynamic_cast est plus sûr qu' static_cast pour les casts aval, mais le contrôle d'exécution entraîne une charge mémoire.

       Base* b = new Base();
    
       // Run-time check to determine whether b is actually a Derived*
       Derived* d3 = dynamic_cast<Derived*>(b);
    
       // If b was originally a Derived*, then d3 is a valid pointer.
       if(d3)
       {
          // Safe to call Derived method.
          cout << d3->DoSomethingMore() << endl;
       }
       else
       {
          // Run-time check failed.
          cout << "d3 is null" << endl;
       }
    
       //Output: d3 is null;
    

    Pour plus d'informations, consultez l' dynamic_cast.

  • const_cast, pour effectuer un cast loin const- ness d'une variable, ou convertir une variable non d'const pour être const.Cast loin const- le ness à l'aide de cet opérateur est tout aussi rubrique aux erreurs qu'utilise un cast de style c, sauf qu'avec const-cast vous êtes moins pour effectuer le cast par erreur.Quelquefois vous devez effectuer un cast loin const- ness d'une variable, par exemple, pour passer une variable d' const à une fonction qui prend un paramètre non d'const .L'exemple suivant montre comment effectuer cette opération.

       void Func(double& d) { ... }
       void ConstCast()
       {
          const double pi = 3.14;
          Func(const_cast<double&>(pi)); //No error.
       }
    

    Pour plus d'informations, consultez l' const_cast.

  • reinterpret_cast, pour les casts entre les types non liés par exemple pointer à int.

    [!REMARQUE]

    Cet opérateur de cast n'est pas utilisé aussi souvent que les autres, et il n'est pas garanti d'être portable à d'autres compilateurs.

    L'exemple suivant montre comment reinterpret_cast diffère d' static_cast.

       const char* str = "hello";
       int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot
                                     // convert from 'const char *' to 'int'
       int j = (int)str; // C-style cast. Did the programmer really intend
                         // to do this?
       int k = reinterpret_cast<int>(str);// Programming intent is clear.
                                          // However, it is not 64-bit safe.
    

    Pour plus d'informations, consultez opérateur de reinterpret_cast.

Voir aussi

Concepts

Système de type C++ (C++ moderne)

Autres ressources

Accueil vers C++ (C++ moderne)

Guide de référence du langage C++

Référence de la bibliothèque C++ standard