2014-09-01 11 views
8

ich die Sprache C++ zu studieren, und ich habe einige Zweifel über Typumwandlung, könnten Sie mir erklären, was wie dies in einem Ausdruck kommt:Was passiert, wenn ich signierte und vorzeichenlose Typen mische?

unsigned int u = 10; 
int a = -42; 
std::cout << u - a << std::endl; 

ich weiß hier, dass das Ergebnis 52 sein wird, wenn ich die Anwendung Regeln, wenn wir zwei mathematische Operatoren haben. Aber ich frage mich, was passiert, wenn der Compiler zu einem vorzeichenlosen Wert ein temporäres von unsigned Typ zu konvertieren, was passiert danach? Der Ausdruck sollte jetzt 10 -4494967254 sein.

+0

Warum versuchst du es nicht zu sehen? – dandan78

+0

@ dandan78 Ich möchte verstehen, wie der Compiler auf dem zugrunde liegenden –

+2

@ dandan78 arbeitet: manchmal kann das naiv sein; vor allem, wenn irgendein Aspekt der Berechnung nicht definiert ist. – Bathsheba

Antwort

9

In einfachen Worten, wenn Sie Arten des gleichen Ranges (in der Folge von int, long int, long long int) mischen, der Typ ohne Vorzeichen „gewinnt“ und die Berechnungen werden in diesem Typ ohne Vorzeichen . Das Ergebnis hat denselben vorzeichenlosen Typ.

Wenn Sie Typen mit unterschiedlichem Rang mischen, "gewinnt" der höherrangige Typ, wenn er alle Werte des niedriger eingestuften Typs darstellen kann. Die Berechnungen werden innerhalb dieses Typs durchgeführt. Das Ergebnis ist von diesem Typ.

Wenn der höherrangige Typ schließlich nicht alle Werte eines niedrigeren Typs darstellen kann, wird die vorzeichenlose Version des höherrangigen Typs verwendet. Das Ergebnis ist von diesem Typ.

In Ihrem Fall mischten Sie Typen des gleichen Rangs (int und unsigned int), was bedeutet, dass der gesamte Ausdruck innerhalb unsigned int Typ ausgewertet wird. Der Ausdruck, wie Sie richtig sagten, ist jetzt 10 - 4294967254 (für 32 Bit int). Vorzeichenlose Typen folgen den Regeln der Modulo-Arithmetik mit 2^32 (4294967296) als Modulo. Wenn Sie das Ergebnis sorgfältig berechnen (was arithmetisch als 10 - 4294967254 + 4294967296 ausgedrückt werden kann), wird es als die erwartete 52 herauskommen.

+0

Entschuldigung ich verlor mich, wenn der Ausdruck wird: unsigned int temporary = 10 - 4294967254 (ok, ich habe das verstanden), aber ich kann nicht verstehen, warum der Ausdruck wird 10 - 4294967254 + 4294967296 (warum Sie den Ausdruck der Modulo hinzufügen Arithmetik?). –

+0

@Piero Borrelli: Eine Möglichkeit, das 'Modulo N'-Äquivalent eines negativen Wertes 'V' zu berechnen, besteht darin, so oft wie nötig' N' hinzuzufügen ('V + N',' V + 2N', 'V + 3N' usw.), bis Sie den ersten nicht negativen Wert erreichen. Im Fall von C++ additiven Operationen benötigt ein mathematisch negatives Ergebnis den Modulo-Wert nur einmal, um das richtige vorzeichenlose Ergebnis zu erhalten. – AnT

+0

@Piero Borrelli: Natürlich ist das eine rein arithmetische Regel. Der Compiler muss so etwas nicht machen. Es muss sich überhaupt keine Sorgen machen. Wenn die negativen Werte durch das Zweierkomplement dargestellt werden, liefert eine einfache Neuinterpretation dieser Darstellung als vorzeichenlose sofort das richtige Ergebnis. – AnT

1

1) Aufgrund der Standardpromotionsregeln wird der signed Typ a vor der Subtraktion in einen unsigned Typ umgewandelt. Dass die Förderung geschieht nach dieser Regel (C++ Standard 4.7/2):

Wenn der Zieltyp nicht signiert ist, der sich ergebende Wert ist die am wenigsten unsigned integer kongruent zu der Quelle integer (modulo 2 n, wobei n ist Anzahl der Bits, die zur Darstellung des Typs ohne Vorzeichen verwendet werden).

Algebraisch a wird eine sehr große positive Zahl sein und sicherlich größer als u.

2) u - a ist eine anonyme temporäre und wird ein unsigned Typ sein. (Sie können dies überprüfen, indem Sie auto t = u - a schreiben und den Typ von t in Ihrem Debugger überprüfen.) Mathematisch ist dies eine negative Zahl, aber bei impliziter Konvertierung in den unsigned-Typ wird eine ähnliche Umbruchregel wie oben aufgerufen.

Kurz gesagt, die beiden Konvertierungsoperationen haben gleiche und entgegengesetzte Effekte und das Ergebnis wird 52 sein. In der Praxis könnte der Compiler all diese Konvertierungen optimieren.

-1

hier ist der disassemble Code sagt:

es erste Sätze -42 zu sein Komplement und die Unteroperation tun. so das Ergebnis ist 10 + 42 0x0000000000400835 <+8>: movl $0xa,-0xc(%rbp) 0x000000000040083c <+15>: movl $0xffffffd6,-0x8(%rbp) 0x0000000000400843 <+22>: mov -0x8(%rbp),%eax 0x0000000000400846 <+25>: mov -0xc(%rbp),%edx 0x0000000000400849 <+28>: sub %eax,%edx 0x000000000040084b <+30>: mov %edx,%eax

+1

Im Allgemeinen kann disassemblierter Code nicht als eine sinnvolle Quelle zum Verständnis der Semantik auf Sprachniveau dienen. Die Code-Generierung ist eine Einwegfunktion. Es ist nicht möglich, "zurück zu verfolgen". d. h., herauszufinden, was der Compiler tatsächlich versucht hat, indem er den erzeugten Code betrachtet. – AnT

+0

Vielen Dank für Ihren Kommentar. –