Um valor de char definido como CHAR_MAX é garantido para envolver CHAR_MIN?

10

Meu código:

#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);
}

Resultado:

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

Vemos que, quando incrementamos um charconjunto de variáveis ​​para CHAR_MAX, ele se volta para CHAR_MIN. Esse comportamento é garantido? Ou será um comportamento indefinido ou um comportamento especificado pela implementação? O que o padrão C99 diz sobre isso?

[Nota: O que acontece quando atribuir valor maior que CHAR_MAX (127) a char ou C- por que char c = 129 será convertido em -127? não aborda essa pergunta porque eles falam sobre atribuir um valor fora do intervalo e não incrementar um valor para um valor fora do intervalo.]

Aluno Solitário
fonte
Um incremento é uma atribuição.
William Pursell
2
Depende se o caractere está assinado ou não. O excesso de números inteiros assinados é um comportamento indefinido. Portanto, a saída pode ser qualquer coisa.
DaBler 8/03

Respostas:

15

A questão é dupla: em primeiro lugar, é

char c = CHAR_MAX;
c += 1;

avaliado diferentemente de

char c = CHAR_MAX;
c = c + 1;

e a resposta é não, não é , porque C11 / C18 6.5.16.2p3 :

  1. Uma atribuição composta do formulário E1 op = E2é equivalente à expressão de atribuição simples, E1 = E1 op (E2)exceto que o valor l E1é avaliado apenas uma vez e, em relação a uma chamada de função sequenciada indeterminada, a operação de uma atribuição composta é uma única avaliação. Se E1tiver um tipo atômico, a atribuição composta é uma operação de leitura-modificação-gravação com memory_order_seq_cstsemântica de ordem de memória. 113)

Então, a questão é o que acontece c = c + 1. Aqui os operandos para +submeter conversões aritméticas usuais, e ce 1, portanto, são promovidos a int, a menos que uma arquitetura realmente maluco requer que charé promovido a unsigned int. O cálculo de +é então avaliado e o resultado do tipo int/ unsigned inté convertido novamente chare armazenado em c.

Existem três maneiras definidas pela implementação em que isso pode ser avaliado:

  • CHAR_MINé 0 e, portanto, charnão está assinado.

    Ou charentão é promovido para intou unsigned inte se é promovido para um int, então CHAR_MAX + 1necessariamente se encaixará em um inttambém e não transbordará, ou se unsigned intpode caber ou quebrar em torno de zero. Quando o valor resultante, que é numericamente CHAR_MAX + 1ou 0após a redução do módulo, volta para c, após a redução do módulo, ele se torna 0, ou seja,CHAR_MIN

  • Caso contrário, charserá assinado e, se CHAR_MAX for menor que INT_MAX, o resultado CHAR_MAX + 1será ajustado a int, e o padrão C11 / C18 6.3.1.3p3 se aplica à conversão que ocorre na atribuição :

    1. Caso contrário, o novo tipo é assinado e o valor não pode ser representado nele; o resultado é definido pela implementação ou um sinal definido pela implementação é gerado.
  • Ou, se if sizeof (int) == 1 e char for assinado, charserá promovido a um inte CHAR_MAX == INT_MAX=> CHAR_MAX + 1causará um estouro de número inteiro e o comportamento será indefinido .

Ou seja, os possíveis resultados são:

  • Se charé um tipo inteiro não assinado, o resultado é sempre 0, ou seja CHAR_MIN.

  • Caso contrário, charé um tipo inteiro assinado e o comportamento é definido / não definido pela implementação:

    • CHAR_MIN ou algum outro valor definido pela implementação,
    • um sinal definido pela implementação é gerado, possivelmente finalizando o programa,
    • ou o comportamento é indefinido em algumas plataformas em que sizeof (char) == sizeof (int).

Todas as operações de incremento c = c + 1, c += 1, c++e ++ctêm os mesmos efeitos colaterais sobre a mesma plataforma. O valor avaliado da expressão c++será o valor canterior ao incremento; para os outros três, será o valor capós o incremento.

Antti Haapala
fonte
11
sizeof(int) == 1exigiria CHAR_BITS >= 16, certo?
sepp2k 8/03
3
@ sepp2k <pedantic>IDK sobre, CHAR_BITSmas CHAR_BITfaria >= 16</pedantic>.
Antti Haapala
2
Mais um motivo pelo qual charsempre deve ser assinado por padrão.
chqrlie
11
@chqrlie Eu concordo, infelizmente pode ser que ele tenha sido assinado por padrão porque não assinado foi mais tarde na história, pode ser muito difícil mudar agora em alguns sistemas cr * ppy devido à grande quantidade de programas quebrados que esperam que o EOF caiba em um char ..
Antti Haapala
11
Às vezes, é claro que você também adiciona uma resposta direta: "Um valor de char definido como CHAR_MAX é garantido para envolver CHAR_MIN?" -> Não.
chux - Restabelece Monica