En C++ 11, selon en.cppreference.com ,
Pour a signé et non négatif, la valeur de a << b est a * 2b s'il est représentable dans le type de retour, sinon, le comportement n'est pas défini.
Je crois comprendre que depuis 255 * 224 n’est pas représentable en tant que int32_t
, l’évaluation de (int32_t) 255 << 24
.__ donne un comportement non défini. Est-ce exact? Cela peut-il être dépendant du compilateur? C'est un environnement IP16, si cela compte.
Contexte: cela vient de un argument que j'ai avec un utilisateur .__ chez arduino.stackexchange.com. Selon lui, "il n'y a rien du tout Indéfini":
vous remarquez qu'une grande partie du décalage de bit est "implémentation définie" . Donc, vous ne pouvez pas citer chapitre-et-verset des spécifications. Tu dois partir à la documentation de GCC puisque c’est le seul endroit où on peut dire vous ce qui se passe réellement . gnu.org/software/gnu-c-manual/gnu-c-manual.html#Bit-Shifting - c'est seulement "indéfini" pour une valeur de décalage négative.
Edit: D'après les réponses reçues jusqu'à présent, il semblerait que ma lecture de norme C++ 11 soit correcte. Ensuite, l’essentiel de ma question est de savoir si Cette expression appelle un comportement non défini in gcc. Comme Davmac le dit. Dans son commentaire, je demande «si GCC, une implémentation, définit un comportement Même s'il n'est pas défini par le standard de langage».
D'après le manuel gcc auquel je me suis référé, il semblerait qu'il soit bien défini, Bien que je trouve que le libellé de ce manuel ressemble davantage à un tutoriel .__ qu'à une "loi linguistique". De la réponse de PSkocik (et du commentaire de Kane à cette réponse ), Il semblerait plutôt que ce soit indéfini. Donc, je suis encore dans le doute.
Je suppose que mon rêve serait d’avoir une déclaration claire dans une documentation gcc Indiquant que 1) gcc ne définit aucun comportement .__ explicitement non défini dans la norme, ou 2) gcc le définit comportement de la version XX.XX et s’engage à le maintenir défini dans toutes les versions ultérieures.
Edit 2: PSkocik a supprimé sa réponse, ce que je trouve regrettable caril a fourni des informations intéressantes. De sa réponse, le commentaire de Kane à La réponse, et mes propres expériences:
(int32_t)255<<24
génère une erreur d'exécution lors de la compilation avec clang et -fsanitize=undefined
-fsanitize=undefined
(int32_t)256<<24
donne une erreur d'exécution lors de la compilation avec g++ -std=c++11 -fsanitize=undefined
Le point 2 est cohérent avec l'interprétation selon laquelle gcc, en mode C++ 11, Définit le décalage à gauche plus largement que la norme. Conformément au point 3, Cette définition pourrait simplement être la définition de C++ 14. Cependant, le point 3 est incohérent avec l'idée que le manuel référencé est une définition complète .__ de <<
en gcc (mode C++ 11), comme le prévoit ce manuelno indice que (int32_t)256<<24
pourrait être indéfini.
"Un comportement indéfini est quelque chose pour lequel la norme n'impose aucune exigence." ce qui signifie qu'il peut même s'agir du comportement attendu/correct. Cette norme C++ indique les opérateurs de décalage.
8.5.7 Opérateurs de décalage [expr.shift] (projet de norme C++, N4713)
- Les opérandes doivent être du type énumération intégrale ou non délimitée et des promotions intégrales sont effectuées. Le type de résultat est celui de l'opérande gauche promu. Le comportement est indéfini si l'opérande de droite est négatif, ou supérieur ou égal à la longueur en bits de l'opérande de gauche promu. Sinon, si
E1
a un type signé et une valeur non négative et queE1×2E2
est représentable dans le type non signé correspondant du type de résultat, cette valeur, convertie en type de résultat, est la valeur résultante; sinon, le comportement est indéfini.
Comme @rustyx le note ci-dessous, "le libellé" E1×2E2
est représentable dans le type non signé correspondant du type de résultat "est C++ 14 . Still UB dans C++ 11 malheureusement. "
Le compilateur GNU C définit les décalages gauche/droite de la manière décrite dans le manuel :
GCC ne prend en charge que deux types d’entiers complémentaires, et tous les modèles de bits sont des valeurs ordinaires.
. . .
En tant qu’extension du langage C, GCC n’utilise pas les latitudes données en C99 et C11 uniquement pour traiter certains aspects du ‘
<<
’ signé comme non définis. Cependant,-fsanitize=shift
(et-fsanitize=undefined
) diagnostiquera de tels cas. Ils sont également diagnostiqués où des expressions constantes sont nécessaires.
Cela correspond donc à ce que vous avez trouvé - le code fait ce que vous attendez, seul un dépassement dépassant les bits disponibles (y compris le bit de signe) sera diagnostiqué.
En ce qui concerne le compilateur GNU C++, la documentation semble être vraiment manquant . Nous pouvons seulement deviner que, par omission, le décalage fonctionne en G ++ de la même manière que dans GCC, même si au moins le désinfectant semble être conscient des différences de langage:
-fsanitize=shift
Cette option permet de vérifier que le résultat d'une opération de décalage n'est pas indéfini. Notez que ce qui est considéré comme indéfini diffère légèrement entre C et C++, ainsi qu'entre ISO C90 et C99, etc.
Pour les compilateurs C++ antérieurs à C++ 14, si vous aviez une fonction comme celle-ci:
// check if the input is still positive,
// after a suspicious shift
bool test(int input)
{
if (input > 0)
{
input = input << 24;
// how can this become negative in C++11,
// unless you are relying on UB?
return (input > 0);
}
else
{
return false;
}
}
alors un compilateur optimiseur aurait été autorisé à le changer en ceci:
bool test(int input)
{
// unless you are relying on UB,
// input << 24 must fit into an int,
// and input is positive in that branch
return (input > 0);
}
Et tout le monde est content parce que vous obtenez une belle accélération dans vos versions de release.
Cependant, je ne suis pas au courant des compilateurs qui effectuent de telles optimisations pour les équipes de gauche, bien qu'il soit assez courant d'optimiser les ajouts à l'extérieur, comme comme dans cet exemple .