Wird ein auf CHAR_MAX eingestellter Zeichenwert garantiert in CHAR_MIN umgebrochen?

10

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

Ausgabe:

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

Wir sehen, dass, wenn wir eine charVariable inkrementieren CHAR_MAX, diese umbrochen wird CHAR_MIN. Ist dieses Verhalten garantiert? Oder handelt es sich um undefiniertes oder implementierungsspezifisches Verhalten? Was sagt der C99-Standard dazu?

[Hinweis: Was passiert, wenn char oder C ein Wert größer als CHAR_MAX (127) gegeben wird - warum wird char c = 129 in -127 konvertiert? geht auf diese Frage nicht ein, weil sie über das Zuweisen eines Werts außerhalb des Bereichs sprechen, ohne einen Wert einem Wert außerhalb des Bereichs zu erhöhen.]

Einsamer Lernender
quelle
Ein Inkrement ist eine Zuordnung.
William Pursell
2
Dies hängt davon ab, ob das Zeichen signiert oder nicht signiert ist. Der Überlauf von vorzeichenbehafteten Ganzzahlen ist ein undefiniertes Verhalten. Die Ausgabe kann also alles sein.
DaBler

Antworten:

15

Die Frage ist zweifach: Erstens ist

char c = CHAR_MAX;
c += 1;

anders bewertet als

char c = CHAR_MAX;
c = c + 1;

und die Antwort ist nein, ist es nicht , weil C11 / C18 6.5.16.2p3 :

  1. Eine zusammengesetzte Zuweisung des Formulars E1 op = E2entspricht dem einfachen Zuweisungsausdruck, E1 = E1 op (E2)außer dass der l-Wert E1nur einmal ausgewertet wird und in Bezug auf einen unbestimmt sequenzierten Funktionsaufruf die Operation einer zusammengesetzten Zuweisung eine einzelne Auswertung ist. Wenn E1es einen atomaren Typ hat, ist die zusammengesetzte Zuweisung eine Lese-, Änderungs- und Schreiboperation mit memory_order_seq_cstSemantik der Speicherreihenfolge. 113)

Dann ist die Frage, was in passiert c = c + 1. Hier werden die Operanden zu +üblichen arithmetischen Umwandlungen durchlaufen, und cund 1werden daher gefördert int, es sei denn , eine wirklich verrückte Architektur erfordert , dass charwird gefördert unsigned int. Die Berechnung von +wird dann ausgewertet und das Ergebnis vom Typ int/ unsigned intzurück in konvertiert charund darin gespeichert c.

Es gibt 3 implementierungsdefinierte Möglichkeiten, wie dies bewertet werden kann:

  • CHAR_MINist 0 und daher charohne Vorzeichen.

    Entweder charwird dann gefördert intoder unsigned intund wenn es ein gefördert wird int, dann CHAR_MAX + 1wird zwangsläufig in ein passt intauch, und nicht überläuft oder wenn unsigned intes auf Null passen oder umwickeln können. Wenn der resultierende Wert, der numerisch entweder CHAR_MAX + 1oder 0nach der Modulo-Reduktion ist, zurück zu c, nach der Modulo-Reduktion, wird er 0, dhCHAR_MIN

  • Andernfalls charwird signiert. Wenn CHAR_MAX es kleiner als ist INT_MAX, CHAR_MAX + 1passt das Ergebnis von zu intund der Standard C11 / C18 6.3.1.3p3 gilt für die Konvertierung, die bei der Zuweisung erfolgt :

    1. Andernfalls wird der neue Typ signiert und der Wert kann nicht darin dargestellt werden. Entweder ist das Ergebnis implementierungsdefiniert oder es wird ein implementierungsdefiniertes Signal ausgelöst.
  • Wenn iff sizeof (int) == 1 und char signiert ist, charwird es zu einem hochgestuftint , und CHAR_MAX == INT_MAX=> CHAR_MAX + 1verursacht einen ganzzahligen Überlauf und das Verhalten ist undefiniert .

Dh die möglichen Ergebnisse sind:

  • Wenn chares sich um einen vorzeichenlosen Integer-Typ handelt, ist das Ergebnis immer 0, dh CHAR_MIN.

  • Andernfalls charhandelt es sich um einen vorzeichenbehafteten Ganzzahltyp, und das Verhalten ist implementierungsdefiniert / undefiniert:

    • CHAR_MIN oder ein anderer implementierungsdefinierter Wert,
    • ein implementierungsdefiniertes Signal wird ausgelöst, wodurch möglicherweise das Programm beendet wird.
    • oder das Verhalten ist auf einigen Plattformen undefiniert, auf denen sizeof (char) == sizeof (int).

Alle Zuwachs Operationen c = c + 1, c += 1, c++und ++chaben die gleichen Nebenwirkungen auf der gleichen Plattform. Der ausgewertete Wert des Ausdrucks c++ist der Wert cvor dem Inkrement. für die anderen drei ist es der Wert von cnach dem Inkrement.

Antti Haapala
quelle
1
sizeof(int) == 1würde erfordern CHAR_BITS >= 16, richtig?
8.
3
@ sepp2k <pedantic>IDK über CHAR_BITSaber CHAR_BITwürde >= 16</pedantic>.
Antti Haapala
2
Ein weiterer Grund, warum charimmer standardmäßig ohne Vorzeichen sein sollte.
Chqrlie
1
@chqrlie Ich stimme zu, leider kann es sein, dass es standardmäßig signiert wurde, weil unsigniert später in der Geschichte war. Es könnte zu schwierig sein, es jetzt auf einigen Cr * ppy-Systemen zu ändern, da eine schiere Menge defekter Programme erwartet, dass EOF in a passt char ..
Antti Haapala
1
Manchmal ist es auch klar, eine direkte Antwort hinzuzufügen: "Wird ein auf CHAR_MAX eingestellter Zeichenwert garantiert in CHAR_MIN umgebrochen?" -> Nein.
chux - Monica