2013-03-27 6 views
11

Angenommen, dass auf einer C-Implementierung (z. B. auf einem x86 C-Compiler) USHRT_MAX = 65535 und INT_MAX = 2147483647. Ist also die folgende Aussage klar definiert?int promotion: Ist das folgende klar definiert?

unsigned short product = USHRT_MAX * USHRT_MAX; 

Gemäß dem folgenden in der Standard-C99 beide Operanden zu int gefördert werden (da int alle möglichen Werte von unsigned short darstellen kann), und daher ist das Ergebnis nicht gut definiert, da ein Überlauf auftritt (65535^2 = 4294836225 > 2147483647), was bedeutet, dass der Wert von product ist nicht gut definiert:

6.3.1.1-1

Wenn ein int ca n repräsentieren alle Werte des Originaltyps, der Wert ist konvertiert zu einem int; Andernfalls wird es in einen unsigned int konvertiert. Diese werden als ganzzahlige Werbeaktionen bezeichnet. (48) Alle anderen Typen sind , die von den ganzzahligen Werbeaktionen nicht geändert werden.

48) Die ganze Zahl aktionen nur angewandt werden: als Teil der üblichen arithmetischen Umwandlungen, auf bestimmtes Argumente Ausdrücke, auf die Operanden der unären +, - und ~ Operatoren und auf beiden Operanden die Schalen Betreiber, wie in ihren jeweiligen Unterklauseln angegeben.

jedoch nach dem folgenden wird das Ergebnis gut definiert, da Berechnungen unsigned Operanden beteiligt nicht überlaufen:

6.2.5-9

Der Bereich der nicht-negativen Werte eines vorzeichenbehafteten ganzzahligen Typs sind ein Teilbereich des entsprechenden vorzeichenlosen ganzzahligen Typs und die Darstellung von der gleiche Wert in jedem Typ ist der gleiche. (31) Eine Berechnung mit vorzeichenlose Operanden können niemals überlaufen, weil ein Ergebnis, das nicht durch den resultierenden vorzeichenlosen Integer-Typ dargestellt werden kann, modulo die Zahl ist, die um eins größer als der größte Wert ist, der vom resultierenden Typ dargestellt werden kann .

Ist die Variable product in der oben genannten Erklärung haben einen gut definierten Wert?

BEARBEITEN: Was soll im folgenden Fall passieren?

unsigned short lhs = USHRT_MAX; 
unsigned short rhs = USHRT_MAX; 
unsigned short product = lhs * rhs; 
+2

Moral: Verwenden Sie keine Arten kleiner als 'int' für die Arithmetik, die Regeln sind einfach zu verwirrend, damit sterbliche Programmierer den richtigen Code schreiben können ... –

+0

Es gibt keinen Unterschied zwischen den beiden Teilen des Codes. Promotions zu "int" werden unabhängig davon auftreten, ob Sie eine Konstante oder eine Variable verwenden. –

+2

Nur als Nitpick gibt es im ersten Fall keine * promotion * zu "int". 'USHRT_MAX' wie angegeben * ist * ein' int'. Das ganze Bild würde sich ändern, wenn es als '65535U' deklariert würde. –

Antwort

4

Die Promotion gewinnt.

sagt Abschnitt 5.2.4.2.1 über die Konstanten USHRT_MAX etc .:

Die unten angegebenen Werte werden durch konstante Ausdrücke geeignet für den Einsatz in #if Vorverarbeitung Richtlinien ersetzt werden. Außer CHAR_BIT und MB_LEN_MAX sind die folgenden durch Ausdrücke zu ersetzen, die denselben Typ haben wie ein Ausdruck, der ein Objekt des entsprechenden Typs ist, der gemäß den ganzzahligen Promotionen konvertiert wurde.

So ist die Multiplikation auf int s ist, und beinhaltet keine unsigned Operanden, eindeutig, gibt es keine konforme Art und Weise USHRT_MAX zu implementieren, um eine Operation mit unsigned Operanden, wenn USHRT_MAX < INT_MAX zu erhalten. Sie haben also Überlauf und undefiniertes Verhalten.

In Bezug auf die Frage hinzugefügt

EDIT: Was im folgenden Fall geschehen soll?

unsigned short lhs = USHRT_MAX; 
unsigned short rhs = USHRT_MAX; 
unsigned short product = lhs * rhs; 

, die genau die gleiche Situation. Die Operanden * unterliegen den ganzzahligen Promotions, alle Werte vom Typ unsigned short können als int s durch die Annahme der Werte USHRT_MAX und INT_MAX dargestellt werden, so dass die Multiplikation auf int s erfolgt und mit den angegebenen Werten überläuft.

Sie müssen mindestens einen Operanden in einen vorzeichenlosen Typ konvertieren, der nicht zu int in overder hochgestuft wird, damit die Multiplikation mit unsignierten Operanden durchgeführt wird.

1

Sie UB erhalten, da durch die Zeit der Multiplikationsoperator angewendet wird, werden die Operanden bereits ganze Zahlen unterzeichnet (wegen der Aktionen zu int auftretenden zuerst).

Sie können Behelfslösung, dass mit diesem:

unsigned short product = USHRT_MAX * (unsigned)USHRT_MAX; 

Der Nachweis, dass (unsigned)some_integer unsigned bleibt:

#include <stdio.h> 

int main(void) 
{ 
    printf("1u * (-1) = %f\n", (((unsigned)1) * (-1)) + 0.0); 
    printf("1 * (-1) = %f\n", (1 * (-1)) + 0.0); 
    return 0; 
} 

Ausgang (ideone):

1u * (-1) = 4294967295.000000 
1 * (-1) = -1.000000 

Guter Fang, btw.

+0

Dies wird nicht funktionieren. Entsprechend den üblichen arithmetischen Umrechnungen: "Wenn der Typ des Operanden mit vorzeichenbehafteten Integer-Typ alle Werte des Typs des Operanden mit vorzeichenloser Integer-Zahl repräsentieren kann, wird der Operand mit dem vorzeichenlosen Integer-Typ in den Typ von konvertiert der Operand mit vorzeichenbehaftetem Integer-Typ. " Daher werden beide Operanden zu "int" hochgestuft und ein Überlauf tritt auf. – Alexandros

+0

Das wird funktionieren. Ein 'unsigned int' wird nicht magisch in 'signed int' umgewandelt. Auf keinen Fall. Siehe das Update. –

+0

@Alexandros: Es wird funktionieren, weil ein 'unsigned' bei der Ganzzahlerhöhung gleich bleibt (6.3.1.1/2," Ein Objekt oder Ausdruck mit einem ganzzahligen Typ (anders als int oder unsigned int) ... " "unsigned" -Operand wird nicht in "int" konvertiert, obwohl "USHRT_MAX" in "int" konvertiert wird. Als nächstes wählen die "üblichen arithmetischen Konvertierungen" für die Multiplikation "unsigned". –

Verwandte Themen