Une valeur char définie sur CHAR_MAX est-elle garantie de se terminer par CHAR_MIN?

10

Mon code:

#include <stdio.h>
#include <limits.h>

int main()
{
    char c = CHAR_MAX;
    c += 1;
    printf("CHAR_MIN=%d CHAR_MAX=%d c=%d (%c)\n", CHAR_MIN, CHAR_MAX, c, c);
}

Production:

CHAR_MIN=-128 CHAR_MAX=127 c=-128 ()

Nous voyons que lorsque nous incrémentons un charensemble de variables à CHAR_MAX, il s'enroule autour de CHAR_MIN. Ce comportement est-il garanti? Ou est-ce que ce sera un comportement non défini ou un comportement spécifié par l'implémentation? Que dit la norme C99 à ce sujet?

[Remarque: que se passe-t-il lorsque vous donnez une valeur supérieure à CHAR_MAX (127) à char ou C- pourquoi char c = 129 se convertira en -127? ne répond pas à cette question car ils parlent d'assigner une valeur hors plage et non d'incrémenter une valeur à une valeur hors plage.]

Apprenant solitaire
la source
Un incrément est une affectation.
William Pursell
2
Cela dépend si le caractère est signé ou non. Le débordement d'entiers signés est un comportement non défini. La sortie peut donc être n'importe quoi.
DaBler

Réponses:

15

La question est double: premièrement,

char c = CHAR_MAX;
c += 1;

évalué différemment de

char c = CHAR_MAX;
c = c + 1;

et la réponse est non, ce n'est pas le cas , car C11 / C18 6.5.16.2p3 :

  1. Une affectation composée du formulaire E1 op = E2est équivalente à l'expression d'affectation simple, E1 = E1 op (E2)sauf que la valeur l E1n'est évaluée qu'une seule fois, et en ce qui concerne un appel de fonction séquencé de façon indéterminée, l'opération d'une affectation composée est une évaluation unique. Si E1a un type atomique, l'affectation composée est une opération de lecture-modification-écriture avec memory_order_seq_cstune sémantique d'ordre mémoire. 113)

Ensuite, la question est de savoir ce qui se passe c = c + 1. Voici les opérandes à +subir des conversions arithmétiques habituelles, et cet 1sont donc promus int, à moins d' une architecture vraiment farfelue exige que charest promu unsigned int. Le calcul de +est ensuite évalué et le résultat de type int/ unsigned intest reconverti charet stocké dans c.

Il existe 3 façons définies par l'implémentation qui peuvent ensuite être évaluées:

  • CHAR_MINest 0 et charn'est donc pas signé.

    L'un charou l' autre est alors promu vers intou unsigned intet s'il est promu vers un int, il CHAR_MAX + 1rentrera nécessairement dans un intaussi et ne débordera pas, ou s'il unsigned intpeut s'ajuster ou se terminer à zéro. Lorsque la valeur résultante, qui est numériquement soit CHAR_MAX + 1ou 0après réduction modulo, de retour à c, après réduction modulo , il deviendra 0, à savoirCHAR_MIN

  • Sinon charest signé, alors si CHAR_MAX est plus petit que INT_MAX, le résultat de CHAR_MAX + 1s'adaptera à un int, et la norme C11 / C18 6.3.1.3p3 s'applique à la conversion qui se produit lors de l' affectation :

    1. Sinon, le nouveau type est signé et la valeur ne peut pas y être représentée; soit le résultat est défini par l'implémentation, soit un signal défini par l'implémentation est émis.
  • Ou, ssi sizeof (int) == 1 et char est signé, puis charest promu en int, et CHAR_MAX == INT_MAX=> CHAR_MAX + 1provoquera un débordement d'entier et le comportement ne sera pas défini .

C'est-à-dire que les résultats possibles sont:

  • Si charest un type entier non signé, le résultat est toujours 0, ie CHAR_MIN.

  • Sinon, il chars'agit d'un type d'entier signé et le comportement est défini par l'implémentation / non défini:

    • CHAR_MIN ou une autre valeur définie par l'implémentation,
    • un signal défini par l'implémentation est émis, mettant éventuellement fin au programme,
    • ou le comportement n'est pas défini sur certaines plates-formes où sizeof (char) == sizeof (int).

Toutes les opérations d'incrémentation c = c + 1, c += 1, c++et ++cont les mêmes effets secondaires sur la même plate - forme. La valeur évaluée de l'expression c++sera la valeur cavant l'incrément; pour les trois autres, ce sera la valeur caprès l'incrément.

Antti Haapala
la source
1
sizeof(int) == 1exigerait CHAR_BITS >= 16, non?
sepp2k
3
@ sepp2k <pedantic>IDK sur CHAR_BITSmais le CHAR_BITferait >= 16</pedantic>.
Antti Haapala
2
Une raison de plus pour laquelle chardevrait toujours être non signé par défaut.
chqrlie
1
@chqrlie Je suis d'accord, malheureusement, il se peut qu'il ait été signé par défaut car non signé était plus tard dans l'histoire, il pourrait être trop difficile de changer maintenant sur certains systèmes cr * ppy en raison de la quantité de programmes cassés qui s'attendent à ce que EOF s'intègre dans un char ..
Antti Haapala
1
Parfois, il est clair d'ajouter également une réponse directe: "Une valeur char définie sur CHAR_MAX est-elle garantie de se terminer par CHAR_MIN?" -> Non.
chux