C11 §6.5.7 Paragraphe 5:
Le résultat de
E1 >> E2
estE1
décalé vers la droiteE2
positions de bits. SiE1
a un type non signé ou siE1
a un type signé et une valeur non négative, la valeur du résultat fait partie intégrante du quotient deE1 / 2*^E2
. SiE1
a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation.
Mais, le document de référence viva64 dit:
int B; B = -1 >> 5; // unspecified behavior
J'ai exécuté ce code sur GCC et il donne toujours une sortie -1
.
Donc, le standard dit que "Si E1 a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation" , mais ce document dit que -1>>5;
est un comportement non spécifié .
Alors, c'est -1>>5;
comportement non spécifié en C? Qui est correct?
Les deux sont corrects. Le comportement défini par l'implémentation est un type particulier de comportement non spécifié.
Citant la section 3.4.1 de la norme C qui définit le "comportement défini par l'implémentation":
1 comportement défini par l'implémentation
comportement non spécifié où chaque implémentation documente la façon dont le choix est fait
2 EXEMPLE Un exemple de comportement défini par l'implémentation est la propagation du bit de poids fort lorsqu'un entier signé est décalé vers la droite.
De la section 3.4.4 définissant le "comportement non spécifié":
1 comportement non spécifié
utilisation d'une valeur non spécifiée ou d'un autre comportement lorsque la présente Norme internationale offre deux possibilités ou plus et n'impose aucune autre exigence sur laquelle est choisie dans tous les cas
2 EXEMPLE Un exemple de comportement non spécifié est l'ordre dans lequel les arguments d'une fonction sont évalués.
Quant à GCC, vous obtiendrez toujours la même réponse car l'opération est définie par l'implémentation. Il implémente le décalage à droite des nombres négatifs via l'extension de signe
De la documentation GCC :
Les résultats de certaines opérations au niveau du bit sur des entiers signés (C90 6.3, C99 et C11 6.5).
Les opérateurs au niveau du bit agissent sur la représentation de la valeur, y compris les bits de signe et de valeur, où le bit de signe est considéré immédiatement au-dessus du bit de valeur la plus élevée. Signé
>>
agit sur les nombres négatifs par extension de signe.En tant qu'extension du langage C, GCC n'utilise pas la latitude donnée en C99 et C11 uniquement pour traiter certains aspects des
<<
comme indéfini. Pourtant,-fsanitize=shift
(et-fsanitize=undefined
) diagnostiquera de tels cas. Ils sont également diagnostiqués lorsque des expressions constantes sont requises.
"Comportement non spécifié" et "implémentation définie" ne sont pas contradictoires. Cela signifie simplement que la norme C ne spécifie pas ce qui doit se produire et que diverses implémentations peuvent faire ce qu'elles jugent "correct".
L'exécuter plusieurs fois sur un même compilateur et obtenir le même résultat signifie seulement que ce compilateur particulier est cohérent. Vous pouvez obtenir des résultats différents sur un compilateur différent.
Le comportement défini par l'implémentation est une sous-classe de comportement non spécifié, c'est-à-dire un comportement qui n'est pas spécifié par la norme.
Le rapport d'anomalie # 154 à C89 a demandé au comité quelles sont les limites du comportement défini par l'implémentation ; le comité répond qu'une implémentation peut définir n'importe quel comportement qu'elle souhaite, et cela n'a pas besoin d'être constant.
Ce qu'une implémentation doit faire, c'est documenter comment ce choix est fait, par opposition à l'autre classe de comportement non spécifié où une implémentation conforme n'a même pas besoin prendre la peine de dire comment le choix est fait, peut-être parce que pour la plupart des implémentations, le texte dirait "au hasard" ou "selon le niveau d'optimisation du compilateur" "ou" selon l'allocation de registre pour les variables locales ".
Je ne reçois aucune des réponses actuelles. La norme C indique clairement que le décalage vers la droite d'un nombre négatif est un comportement défini par l'implémentation . Ce n'est pas un comportement non spécifié, ce qui signifie autre chose. Comme vous le citez correctement (C17 6.5.7 §5):
Le résultat de E1 >> E2 est des positions de bit E2 décalées vers la droite E1./- /
Si E1 a un type signé et une valeur négative, la valeur résultante est définie par l'implémentation.
Cela signifie que le compilateur doit documenter son comportement. Période.
En pratique: le document doit indiquer si le compilateur utilise un décalage arithmétique à droite ou un décalage logique à droite.
C'est par opposition au comportement non spécifié, qui est un comportement spécifique à l'implémentation qui n'a pas besoin d'être documenté. Un comportement non spécifié est utilisé dans deux cas:
Par exemple, un compilateur n'a pas besoin de documenter l'ordre d'évaluation dans du code comme celui-ci:
a = f1() + f2();
a += f1() + f2();
La documentation de l'ordre dans lequel les sous-expressions sont évaluées révélerait des détails sur le fonctionnement de l'arborescence des expressions internes et de l'optimiseur du compilateur, ce qui révélerait pourquoi un compilateur produit un meilleur code ou se compile plus rapidement que la concurrence. Ce fut une grande chose lorsque la norme C a été écrite à l'origine. De moins en moins de nos jours quand il y a de grands compilateurs open-source, donc ce n'est plus un secret.
De même, un compilateur n'a pas besoin de documenter ce que ce code imprime:
int a;
int ptr = &a;
printf("%d", *ptr);
a
est une valeur indéterminée et la sortie n'est pas spécifiée - en pratique, la sortie dépend de ce qui était stocké dans cette cellule particulière RAM auparavant. Ce que nous appellerions une "valeur de déchets" (Avant de crier "UB", voir (Pourquoi) utilise un comportement indéfini variable non initialisé? ).