2016-10-22 3 views
9

In C sehe ich viel Code, der einer size_t-Variablen ein Integer-Literal hinzufügt oder zuweist.Hinzufügen oder Zuweisen eines Integer-Literals zu einer size_t

size_t foo = 1; 
foo += 1; 

Welche Umwandlung findet hier und kann es immer vorkommen, dass ein size_t wird „aufgerüstet“ auf ein int und dann zu einem size_t umgewandelt zurück? Würde das immer noch umlaufen, wenn ich am Maximum wäre?

size_t foo = SIZE_MAX; 
foo += 1; 

Ist das definiertes Verhalten? Es ist ein vorzeichenloser Typ size_t, der eine signierte int hinzugefügt hat (die möglicherweise ein größerer Typ?) Und die zurück zu einem size_t konvertiert werden. Besteht die Gefahr eines vorzeichenbehafteten Ganzzahlüberlaufs?

Wäre es sinnvoll, etwas wie foo + bar + (size_t)1 anstelle von foo + bar + 1 zu schreiben? Ich sehe Code nie so, aber ich frage mich, ob es notwendig ist, wenn ganzzahlige Promotions mühsam sind.

C89 sagt nicht, wie ein size_t gewählt wird, oder was genau es ist:

Der Wert des Ergebnisses ist die Implementierung definiert, und seine Art (unsigned Integral-Typ) ist size_t definiert in die Überschrift.

+1

Gute Frage. Erfahrungsgemäß ist "size_t" bei jeder Implementierung, die ich jemals verwendet habe, gleich groß oder größer als "int". Wenn wir uns darauf verständigen könnten (was wir natürlich nicht tun), würden wir bemerken, dass ein "int" die "natürliche" Größe für einen Integer-Typ bei jeder gegebenen Implementierung ist und daher keinen Sinn ergibt für 'size_t' kleiner als diese natürliche Größe. – user3386109

+2

Beachten Sie, dass 'SIZE_MAX' nicht vom C89-Standard definiert wurde. –

+2

Ist diese Frage über den aktuellen Standard oder C89? – 2501

Antwort

4

Die aktuelle C-Standard erlaubt eine Möglichkeit einer Implementierung, die nicht definiertes Verhalten verursachen würde, wenn Sie den folgenden Code ausführen, aber eine solche Umsetzung nicht existiert, und wird wahrscheinlich nie:

size_t foo = SIZE_MAX; 
foo += 1; 

Der Typ size_t ist als vorzeichenloser Typ , mit einem minimalen Bereich: [0,65535].

Der Typ size_t kann als Synonym für den Typ unsigned short definiert werden. Der Typ unsigned short kann mit 16 Präzisionsbits definiert werden, mit dem Bereich: [0,65535]. In diesem Fall ist der Wert von SIZE_MAX 65535.

Der Typ int kann definiert werden mit 16 Präzisionsbits (plus ein Vorzeichenbit), Zweierkomplementdarstellung und Bereich: [-65536,65535].

Der Ausdruck foo + = 1 entspricht foo = foo + 1 (außer dass foo nur einmal ausgewertet wird, aber das ist hier irrelevant). Die Variable foo wird mit Integer-Promotions hochgestuft. Es wird zum Typ int hochgestuft, weil Typ int alle Werte vom Typ size_t und rank von size_t darstellen kann, da es ein Synonym für unsigned short ist und niedriger als der rank von int ist. Da die maximalen Werte von size_t und int identisch sind, verursacht die Berechnung einen vorzeichenbehafteten Überlauf, was zu undefiniertem Verhalten führt.

Dies gilt für den aktuellen Standard und sollte auch für C89 gelten, da es keine strengeren Einschränkungen für Typen gibt.

Lösung für signierten Überlauf für jede denkbare Implementierung zu vermeiden, ist eine unsigned int Integer-Konstante zu verwenden:

foo += 1u; 

In diesem Fall, wenn foo einen niedrigeren Rang als int hat, wird es in unsigned int mit üblichen gefördert werden arithmetische Konvertierungen


(Zitat von ISO/IEC 9899/201X 7,19 Gemeinsame Definitionen 2)
size_t
die die unsigned integer Typ des Ergebnisses des sizeof-Operator ist;

(Zitat von ISO/IEC 9899/201X 7.20.3 Limits anderen integer-Typ 2)
Grenze size_t
SIZE_MAX 65535

(Zitat von ISO/IEC 9899/201x 6.3.1.1 Boolescher Wert, Zeichen und Ganzzahlen 2)
Das folgende kann in einem Ausdruck überall dort verwendet werden, wo ein int oder unsigned int verwendet werden kann: Ein Objekt oder Ausdruck mit einem Integer-Typ (anders als int oder unsigned int) , deren Ganzzahlkonvertierungsrang kleiner oder gleich dem Rang von int und unsigned int ist.
Wenn ein int alle Werte des ursprünglichen Typs darstellen kann (wie durch die Breite beschränkt, für ein Bit-Feld), wird der Wert in ein Int konvertiert; andernfalls wird es in eine vorzeichenlose int konvertiert. Diese werden als ganzzahlige Werbeaktionen bezeichnet. Alle anderen Arten sind unverändert durch die Integer-Promotions.

+0

Es gab tatsächlich einen Fehler, der jetzt behoben ist. Der Int sollte eine Genauigkeit von 16 Bit haben, da das Vorzeichenbit nicht als ein Präzisionsbit gezählt wird. Die Breite des Typs int ist daher 17 Bits plus irgendwelche Füllbits, die diese abstrakte Implementierung enthält. – 2501

+0

Eine gute Antwort auf das gegebene Szenario von OP. Ich bin mir sicher, dass keine Plattform heute "size_t" als "unsigned short" mit dem gleichen positiven Bereich wie "int" hätte, wie Sie es auch vorgeschlagen haben. Und hoffe, dass keine zukünftige neuartige Maschine dies auch tut. – chux

+0

Wenn der Maximalwert von "int" größer ist als "size_t" und die Größe "size_t" in int konvertiert wird und der Wert hinzugefügt wird und das Ergebnis nicht "INT_MAX" überschreitet, wird das Verhalten definiert, wenn diese Ganzzahl resultiert wird zurück in size_t konvertiert? Zum Beispiel 'INT_MAX == 6' und' SIZE_MAX == 4' und 'size_t foo = 4; foo + = 2; 'ist es dann definiert, dass, wenn dieses Ergebnis 6 foo zugewiesen ist, es kein undefiniertes Verhalten gibt und es stattdessen um 1 geht? – newguy

3

Es kommt darauf an, da size_t ein implementierungsdefinierter vorzeichenloser Integraltyp ist.

Operationen mit einem size_t werden daher Promotionen einführen, aber diese hängen davon ab, was size_t tatsächlich ist, und welche anderen Typen in den Ausdruck tatsächlich involviert sind.

Wenn size_t entsprechen einen unsigned short (zB einen 16-Bit-Typen), dann

size_t foo = 1; 
foo += 1; 

würde (semantisch) fördern foo zu einem int, fügen 1, und dann das Ergebnis konvertieren zurück zu size_t zum Speichern in foo. (Ich sage "semantisch", denn das ist die Bedeutung des Codes gemäß dem Standard. Ein Compiler kann die "Als-ob" -Regel anwenden - also tun, was er will, solange es den gleichen Nettoeffekt liefert).

Auf einer anderen Seite, wenn size_t entsprechen einen long long unsigned (zB einen 64-Bit unterzeichnet Typen), dann würde der gleiche Code 1 fördert von long long unsigned Typ zu sein, das von foo, auf den Wert addieren und das Ergebnis speichern zurück in foo.

In beiden Fällen ist das Nettoergebnis gleich, es sei denn, ein Überlauf tritt auf. In diesem Fall gibt es keinen Überlauf, da sowohl int als auch size_t garantiert die Werte 1 und 2 darstellen können.

Wenn ein Überlauf auftritt (z. B. Hinzufügen eines größeren Integralwerts), kann das Verhalten variieren. Ein Überlauf eines vorzeichenbehafteten ganzzahligen Typs (z. B. int) führt zu undefiniertem Verhalten. Überlauf eines unsigned Integraltyps verwendet Modulo-Arithmetik.

Was den Code

size_t foo = SIZE_MAX; 
foo += 1; 

es möglich ist, die gleiche Art von Analyse zu tun.

Wenn size_t entspricht einem unsigned short dann foo zu int umgewandelt würden. Wenn int einem signed short entspricht, kann es nicht den Wert SIZE_MAX darstellen, sodass die Konvertierung überläuft und das Ergebnis nicht definiert ist. Wenn int einen größeren Bereich als short int darstellen kann (z. B. entspricht es long), wird die Konvertierung von foo zu int erfolgreich sein, die Erhöhung dieses Werts wird erfolgreich sein, und die Speicherung zurück zu size_t wird Modulo-Arithmetik verwenden und das Ergebnis von 0.

Wenn size_t zu unsigned long äquivalent ist, dann wird der Wert 1unsigned long zu konvertierenden hinzu, dass zu foo Modulo-Arithmetik verwenden (das heißt ein Ergebnis von Null erzeugen), und das wird in foo gespeichert werden.

Es ist möglich, ähnliche Analysen durchzuführen, wenn angenommen wird, dass size_t tatsächlich andere vorzeichenlose ganzzahlige Typen sind.

Hinweis: In modernen Systemen ist eine size_t, die die gleiche Größe oder kleiner als eine int ist ungewöhnlich. Solche Systeme haben jedoch existiert (z. B. Microsoft- und Borland-C-Compiler, die auf 16-Bit-MS-DOS auf Hardware mit einer 80286-CPU abzielen). Es sind auch noch 16-Bit-Mikroprozessoren in Produktion, hauptsächlich zur Verwendung in eingebetteten Systemen mit geringerem Energieverbrauch und geringen Durchsatzanforderungen, und C-Compiler, die auf sie abzielen (z. B. Keil C166-Compiler, der auf die Infeon XE166-Mikroprozessorfamilie abzielt). [Hinweis: Ich hatte nie Grund, den Keil-Compiler zu verwenden, aber angesichts seiner Zielplattform wäre es keine Überraschung, wenn es einen 16-Bit size_t unterstützt, der dieselbe Größe oder kleiner als der native int-Typ auf dieser Plattform ist ].

+1

"size_t war äquivalent zu einem unsigned short" und ... "würde (semantisch)' foo' zu einem int "" propagieren. Könnte sein. In einem solchen Fall ist mehr _likely_, "int" auch 16-Bit. und "foo" wird zu einem "unsigned". Das "If' size_t "ist gleichbedeutend mit einem" unsigned short ".." wird auf die gleiche Weise beeinflusst. Keine UB. – chux

+0

DOS verwendete 16-Bit 'int',' unsigned', 'short',' unsinged short' und 'size_t'. 'size_t' war nicht kleiner, aber gleich groß. Insbesondere kann "size_t" nicht 16-bit "signed int" sein. Die schmalste es sein kann, ist 16-bit unsigned einiger Typ/ – chux

+0

'size_t' ist ein quasi-* * unsigned Typ, der für den Typen' int' (zB zwischen '0' und '(1U << 31 über die positiven Werte definiert ist) - 1 '('2147483648') für x86/x86_64) –

2

foo += 1 bedeutet foo = foo + 1. Wenn size_t schmaler ist als int (dh int kann alle Werte von size_t darstellen), wird foo im Ausdruck foo + 1 zu int hochgestuft.

Der einzige mögliche Überlauf ist, wenn INT_MAX == SIZE_MAX. Theoretisch ist das möglich, z.B. 16-Bit Int und 15-Bit size_t. (Letzteres hätte wahrscheinlich 1 Auffüllbit).

Wahrscheinlicher ist, SIZE_MAX wird weniger als INT_MAX, so wird der Code Implementierung definiert aufgrund out-of-Range-Zuordnung. Normalerweise ist die Implementierungsdefinition die "offensichtliche", hohe Bits werden verworfen, so dass das Ergebnis 0 ist.

Als eine praktische Entscheidung würde ich nicht empfehlen, Ihren Code zu vermischen, um auf diese Fälle (15-Bit size_t oder nicht naheliegende Implementierungsdefinition) einzugehen, die wahrscheinlich nie passiert sind und nie werden. Stattdessen könnten Sie Tests zur Kompilierungszeit durchführen, die in diesen Fällen zu einem Fehler führen. Eine Kompilierungszeit Behauptung, dass INT_MAX < SIZE_MAX wäre in dieser Zeit und heute praktisch.

+0

size_t kann keine Genauigkeit von 15 Bit haben. Bitte sehen Sie meine Antwort, wo ich erkläre, wie das mit einer etwas anderen Konfiguration möglich ist. – 2501

+0

@ 2501 - Basierend auf was? Standard oder aktuelle bekannte Implementierungen? – enhzflep

+0

@ 2501 guter Punkt. Ich werde meine Antwort um 1 Bit erhöhen müssen: P Obwohl anscheinend dieses Limit nicht in C89 erschien, was OP fragt nach –

Verwandte Themen