Avertissement étrange de conversion int courte GCC

J’ai un peu de code C, qui va exactement comme ceci:

short int fun16(void){ short int a = 2; short int b = 2; return a+b; } 

Lorsque j’essaie de le comstackr avec GCC, je reçois l’avertissement:

 warning: conversion to 'short int' from 'int' may alter its value [-Wconversion] return a+b; ^ 

Bien qu’il n’y ait pas de conversion visible. Les deux opérandes sont courts et même la valeur renvoyée est également courte. Alors, quel est le piège?

Lorsque vous effectuez des calculs arithmétiques, les opérandes sont soumis aux “conversions arithmétiques habituelles” (un sur-ensemble des “promotions sur les nombres entiers” citées dans la réponse d’Acme – il m’a battu à cet égard mais je continuerai à poster :-)). Ceux-ci élargissent short int pour plain int , donc:

 a + b 

calcule le même résultat que:

 ((int) a) + ((int) b) 

La déclaration return doit alors réduire cet int à un short int , et c’est là que gcc produit l’avertissement.

Citant la norme (§6.3.1.1 ¶2):

Les éléments suivants peuvent être utilisés dans une expression partout où un int ou un unsigned int peut être utilisé:

  • Un object ou une expression de type entier (autre que int ou unsigned int ) dont le rang de conversion d’entier est inférieur ou égal au rang d’ int et de unsigned int .
  • Un champ de bits de type _Bool , int , signed int ou unsigned int .

Si un int peut représenter toutes les valeurs du type d’origine, la valeur est convertie en un int ; sinon, il est converti en un unsigned int . Celles-ci sont appelées promotions entières . Tous les autres types ne sont pas modifiés par les promotions entières.

Le drapeau -Wconversion met en garde sur:

Avertir des conversions implicites susceptibles de modifier une valeur. Cela inclut les conversions entre real et integer, comme abs (x) lorsque x est double ; conversions entre signé et non signé, comme unsigned ui = -1; et des conversions en types plus petits, tels que sqrtf (M_PI) . Ne prévenez pas les envois explicites comme abs ((int) x) et ui = (unsigned) -1 , ou si la valeur n’est pas modifiée par la conversion comme dans abs (2.0) . Les avertissements concernant les conversions entre les entiers signés et non signés peuvent être désactivés à l’aide de -Wno-sign-conversion .

De la section 2.7 du langage de programmation C Conversion de type

  • Si l’un des opérandes est long double , convertissez l’autre en long double .
  • Sinon, si l’un des opérandes est double , convertissez l’autre en double .
  • Sinon, si l’un des opérandes est float , convertissez l’autre en float .
  • Sinon, convertissez char et short en int .
  • Ensuite, si l’un des opérandes est long , convertissez l’autre en long .

Lorsque les deux opérandes sont short , ils sont implicitement promus en opérations arithmétiques.

Si un int peut représenter toutes les valeurs du type d’origine, la valeur est convertie en un int; sinon, il est converti en un unsigned int. Celles-ci sont appelées promotions entières. Tous les autres types ne sont pas modifiés par les promotions entières.

Reportez-vous au message suivant: Pourquoi un court-métrage doit-il être converti en un int avant des opérations arithmétiques en C et C ++?

GCC n’effectuera ces upscalings implicites que pour les opérations qui génèrent des temporaries:

 ++i i += 2 

ne générera pas de temporaires.

 i = j + 1 

volonté.

Le code suivant:

 std::cout << sizeof(i) << " " << sizeof(i + 1) << " " << sizeof(i + static_cast(1)) << " " << sizeof(static_cast(i) + static_cast(1)) << " " << sizeof(++i) << " " << sizeof(i + 0x0F) << " " << sizeof(i += 1) << std::endl; 

Donnera la sortie suivante en mode release ou debug: 2 4 4 4 2 4 2

Cela indique que si certaines choses peuvent supprimer l'avertissement, elles n'empêchent pas le compilateur de passer à l'échelle supérieure à int. Donc, pour perdre l'avertissement, arrêtez la génération de temporaires.