Czy gwarantuje się, że wartość char ustawiona na CHAR_MAX ma być zawijana na CHAR_MIN?

10

Mój kod:

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

Wynik:

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

Widzimy, że kiedy zwiększamy charzestaw zmiennych do CHAR_MAX, zawija się ono do CHAR_MIN. Czy to zachowanie jest gwarantowane? A może będzie to zachowanie niezdefiniowane lub określone w implementacji? Co na ten temat mówi standard C99?

[Uwaga: Co się stanie, jeśli wartość char niż CAR_MAX (127) będzie większa od C lub C - dlaczego char c = 129 zamieni się na -127? nie rozwiązuje tego pytania, ponieważ mówią o przypisywaniu wartości spoza zakresu, a nie zwiększaniu wartości do wartości spoza zakresu.]

Samotny uczeń
źródło
Przyrost to zadanie.
William Pursell
2
Zależy to od tego, czy znak jest podpisany czy niepodpisany. Przepełnienie podpisanych liczb całkowitych jest niezdefiniowanym zachowaniem. Tak więc wynik może być dowolny.
DaBler,

Odpowiedzi:

15

Pytanie jest dwojakie: po pierwsze, jest

char c = CHAR_MAX;
c += 1;

ocenione inaczej niż

char c = CHAR_MAX;
c = c + 1;

a odpowiedź brzmi nie, nie jest , ponieważ C11 / C18 6.5.16.2p3 :

  1. Przypisanie złożone postaci E1 op = E2jest równoznaczne z prostym wyrażeniem przypisania, E1 = E1 op (E2)z tym wyjątkiem, że wartość E1jest oceniana tylko raz, a w odniesieniu do wywołania funkcji w nieokreślonej kolejności działanie przypisania złożonego jest pojedynczą oceną. Jeśli E1ma typ atomowy, przypisanie złożone jest operacją odczytu-modyfikacji-zapisu z memory_order_seq_cstsemantyką kolejności pamięci. 113)

Następnie pojawia się pytanie, co się dzieje c = c + 1. Oto argumenty do +poddane zwyczajowym arytmetycznych konwersje, a ci 1dlatego są promowane na int, chyba że naprawdę głupi architektura wymaga, charjest promowany do unsigned int. Następnie obliczane +jest obliczenie , a wynik typu int/ unsigned intjest konwertowany z powrotem chari zapisywany w c.

Istnieją 3 zdefiniowane sposoby wdrożenia, w których można to następnie ocenić:

  • CHAR_MINma wartość 0 i dlatego charjest niepodpisany.

    Albo charjest następnie podwyższony do intlub unsigned intjeśli jest promowany do stopnia int, wówczas CHAR_MAX + 1koniecznie wpasować się intteż i nie przelewowy, lub unsigned intmoże pasować lub owinąć wokół zera. Jeżeli otrzymana wartość, która jest liczbowo albo CHAR_MAX + 1czy 0po redukcji modulo, z powrotem do cpo redukcji modulo stanie się 0, czyliCHAR_MIN

  • W przeciwnym razie charjest podpisany, a jeśli CHAR_MAX jest mniejszy niż INT_MAX, wynik CHAR_MAX + 1będzie pasował int, a do konwersji, która ma miejsce po przypisaniu, stosuje się standard C11 / C18 6.3.1.3p3 :

    1. W przeciwnym razie nowy typ jest podpisany i nie można w nim reprezentować wartości; albo wynik jest zdefiniowany w implementacji, albo podniesiony jest sygnał zdefiniowany w implementacji.
  • Lub, iff sizeof (int) == 1 i char jest podpisany, a następnie charawansowany na int, a CHAR_MAX == INT_MAX=> CHAR_MAX + 1spowoduje przepełnienie liczb całkowitych, a zachowanie będzie niezdefiniowane .

To znaczy możliwe wyniki to:

  • Jeśli charjest liczbą całkowitą bez znaku, wynikiem jest zawsze 0, tj CHAR_MIN.

  • W przeciwnym razie charjest typem całkowitym ze znakiem, a zachowanie jest zdefiniowane / niezdefiniowane w implementacji:

    • CHAR_MIN lub jakąś inną wartość zdefiniowaną w implementacji,
    • generowany jest sygnał zdefiniowany w implementacji, prawdopodobnie kończący program,
    • lub zachowanie jest niezdefiniowane na niektórych platformach, na których sizeof (char) == sizeof (int).

Wszystkie operacje inkrementacji c = c + 1, c += 1, c++i ++cmają takie same skutki uboczne na tej samej platformie. Obliczona wartość wyrażenia c++będzie wartością cprzed przyrostem; dla pozostałych trzech będzie to wartość cpo zwiększeniu.

Antti Haapala
źródło
1
sizeof(int) == 1wymagałoby CHAR_BITS >= 16, prawda?
sepp2k
3
@ sepp2k <pedantic>IDK o, CHAR_BITSale CHAR_BITchciałbym >= 16</pedantic>.
Antti Haapala
2
Jest jeszcze jeden powód, dla którego charzawsze powinien być domyślnie niepodpisany.
chqrlie
1
@chqrlie Zgadzam się, niestety może być tak, że został podpisany domyślnie, ponieważ niepodpisany był później w historii, może być zbyt trudny do zmiany w niektórych systemach cr * ppy ze względu na ogromną liczbę uszkodzonych programów oczekujących, że EOF zmieści się w char ..
Antti Haapala
1
Czasami jasne jest również dodanie bezpośredniej odpowiedzi: „Czy gwarantowane jest ustawienie wartości char na CHAR_MAX na CHAR_MIN?” -> Nie.
Chux - Przywróć Monikę