32 votes

Meilleures pratiques en C / C++ avec les ints signés / non signés et les appels de fonction

Je pose cette question pour deux langues différentes : C et C++.

Quelle est la meilleure pratique pour appeler des fonctions qui ont une attente de signe entier opposé à ce que nous exigeons dans notre code ?

Par exemple :

uint32       _depth;                        // uint32 = DWORD
int          depth;

_BitScanForward(&_depth, (uint32)input);    // DWORD, DWORD
depth = (int)_depth;

_BitScanForward attend des paramètres DWORD (uint32). La variable input est de type int16 et je dois traiter le résultat _depth comme un int32 dans mon code.

  1. Est-ce que je dois me soucier du casting input comme indiqué ? Je sais que le compilateur probablement le faire pour moi, mais quelle est la meilleure pratique ?
  2. Est-il acceptable de déclarer _depth en tant que int32 et donc éviter de devoir le convertir ensuite comme indiqué ?

NOTE :

Mon commentaire sur le compilateur est basé sur l'expérience. J'ai écrit du code qui s'est compilé sans aucun avertissement dans VS mais qui a planté à l'exécution. Il s'est avéré que j'appelais une fonction avec un int de largeur incorrecte. Je ne laisse donc plus ce sujet au compilateur.

EDIT :

Les réponses sont utiles, merci. Permettez-moi d'affiner ma question. S'il n'y a pas de problème de largeur, c'est-à-dire que la fonction n'attend pas un int plus étroit que celui qui est transmis (ce qui échouera évidemment), est-il possible de compter sur le compilateur pour gérer les différences de signe et de largeur ?

37voto

chtz Points 6357

Je vous recommande vivement de cacher cette fonction dans une fonction d'enveloppe personnalisée conforme à votre API préférée (et d'effectuer un casting explicite approprié dans cette fonction). Dans le cas de l'utilisation de fonctions spécifiques à un compilateur, cela présente l'avantage supplémentaire de faciliter le portage vers d'autres compilateurs (si vous souhaitez le faire un jour), en réimplémentant simplement cette fonction d'enveloppe.

9voto

Zack Points 44583

Il est très important d'écrire un cast explicite quand on passe d'un type d'entier plus étroit que int à tout type d'entier qui est de la même largeur ou plus large que int . Si vous ne le faites pas, le compilateur va premièrement convertir la valeur en int à cause des règles de "promotion des entiers", puis au type de destination. C'est presque toujours faux, et nous ne concevrions pas le langage de cette façon si nous partions de zéro aujourd'hui, mais nous sommes coincés avec cela pour des raisons de compatibilité.

Les typologies fournies par le système, comme uint16_t , uint32_t , WORD y DWORD peut être plus étroite, plus large ou de la même taille que la int En C++, vous pouvez utiliser des modèles pour résoudre ce problème, mais en C, ce n'est pas possible. Par conséquent, vous pouvez vouloir écrire des casts explicites pour tout conversion impliquant ces derniers.

4voto

Hayt Points 110

Cela dépend de votre utilisation, etc :

Si je peux utiliser le type dont j'ai besoin, j'utilise ce type.

Si ce n'est pas le cas : Votre compilateur devrait vous avertir dans les cas où vous convertissez implicitement des types de données, ce qui peut entraîner des débordements ou des sous-exécutions. C'est pourquoi j'active généralement ces avertissements et je change les conversions implicites en conversions explicites.

Là, j'ai deux approches différentes :

Si je suis sûr à 100% de ne jamais dépasser les limites entre les int signés et non signés, j'utilise static_cast . (généralement pour la conversion de différentes API. Comme size() qui renvoie int vs size_t).

Quand je ne suis pas sûr ou qu'il est possible que je sois au-delà des limites, j'utilise boost::numeric_cast . Cela déclenche une exception lorsque vous effectuez un lancer au-delà des limites et montre ainsi quand cela se produit.

L'approche avec les exceptions adhère à la pratique consistant à échouer gravement/se casser/se terminer si quelque chose ne va pas, au lieu de continuer avec des données corrompues, puis de se casser ailleurs ou de faire d'autres choses avec des données non définies.

1voto

user6556709 Points 130

Tout d'abord, votre compilateur rendra les transferts implicites et vous donnera un avertissement à tout niveau d'avertissement significatif.

Les deux casts que vous effectuez sont des casts où le compilateur (ou vos collègues) ne peut pas facilement décider s'ils sont corrects, donc un casting explicite ou une conversion explicite avec un test de limite est la meilleure pratique. Le choix de la méthode dépend de votre connaissance des données. Le moyen le plus sûr est de vérifier les conditions limites. La méthode la plus économique consiste à effectuer un simple casting (en C++, utilisez static_cast et non les casts de style C).

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