Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Dokument werden allgemeine Typkonvertierungsprobleme behandelt, und es wird beschrieben, wie Sie diese im C++-Code vermeiden können.
Beim Schreiben eines C++-Programms müssen Sie sicherzustellen, dass es typsicher ist. Das bedeutet, dass alle Variablen, Funktionsargumente und Rückgabewerte von Funktionen akzeptable Daten speichern und dass Vorgänge, bei denen Werte verschiedener Typen verwendet werden, einen „Sinn“ ergeben und nicht zu Datenverlust, falschen Interpretationen von Bitmustern oder Speicherschäden führen. Ein Programm, das nie explizit oder implizit Werte von einem Typ in einen anderen konvertiert, ist definitionsgemäß typsicher. Manchmal sind jedoch Typkonvertierungen oder sogar unsichere Konvertierungen erforderlich. Beispielsweise müssen Sie das Ergebnis eines Gleitkommavorgangs in einer Variablen des Typs int
speichern, oder Sie müssen den Wert in einer unsigned int
Funktion übergeben, die eine signed int
. Beide Beispiele veranschaulichen unsichere Konvertierungen, da sie zu Datenverlusten oder zur erneuten Interpretation eines Werts führen können.
Wenn der Compiler eine unsichere Konvertierung erkennt, wird entweder ein Fehler oder eine Warnung ausgegeben. Ein Fehler beendet die Kompilierung. Bei einer Warnung kann die Kompilierung fortgesetzt werden, es wird jedoch ein möglicher Fehler im Code angegeben. Auch wenn Ihr Programm ohne Warnungen kompiliert wird, kann es trotzdem noch Code enthalten, der zu impliziten Typkonvertierungen führt, welche falsche Ergebnisse erzeugen. Typfehler können auch durch explizite Konvertierungen oder Umwandlungen im Code verursacht werden.
Implizite Typkonvertierungen
Wenn ein Ausdruck Operanden verschiedener integrierter Typen enthält und keine expliziten Umwandlungen vorhanden sind, verwendet der Compiler integrierte Standardkonvertierungen , um einen der Operanden so zu konvertieren, dass die Typen übereinstimmen. Der Compiler testet die Konvertierungen in einer klar definierte Abfolge, bis eine erfolgreich ist. Wenn die ausgewählte Konvertierung eine Heraufstufung ist, gibt der Compiler keine Warnung aus. Wenn es sich bei der Konvertierung um eine Einschränkung handelt, gibt der Compiler eine Warnung zu möglichem Datenverlust aus. Ob wirklich ein Datenverlust auftritt, hängt von den verwendeten tatsächlichen Werten ab. Es wird jedoch empfohlen, diese Warnung als Fehler zu behandeln. Bei einem benutzerdefinierten Typ versucht der Compiler, die Konvertierungen zu verwenden, die Sie in der Klassendefinition angegeben haben. Wenn eine akzeptable Konvertierung nicht gefunden werden kann, gibt der Compiler einen Fehler aus und kompiliert das Programm nicht. Weitere Informationen zu den Regeln, die die Standardkonvertierungen regeln, finden Sie unter "Standardkonvertierungen". Weitere Informationen zu benutzerdefinierten Konvertierungen finden Sie unter User-Defined Konvertierungen (C++/CLI).
Erweiternde Konvertierungen (Heraufstufung)
Bei einer erweiternden Konvertierung wird ein Wert in einer kleineren Variable einer größeren Variable zugewiesen, ohne dass es zu einem Datenverlust kommt. Da die Verbreiterung immer sicher ist, führt der Compiler sie automatisch aus und gibt keine Warnungen aus. Die folgenden Konvertierungen sind erweiternde Konvertierungen.
Von | Beschreibung |
---|---|
Beliebiger signed oder unsigned integraler Typ außer long long oder __int64 |
double |
bool oder char |
Ein beliebiger anderer integrierter Typ |
short oder wchar_t |
int , long long long |
int , long |
long long |
float |
double |
Einschränkende Konvertierungen (Koersion)
Der Compiler führt einschränkende Konvertierungen implizit aus, warnt jedoch vor möglichem Datenverlust. Nehmen Sie diese Warnungen ernst. Wenn Sie sicher sind, dass kein Datenverlust auftritt, da die Werte in der größeren Variable immer in die kleinere Variable passen, fügen Sie eine explizite Umwandlung hinzu, damit der Compiler keine Warnung mehr ausgibt. Wenn Sie nicht sicher sind, dass die Konvertierung sicher ist, fügen Sie Ihrem Code eine Art Laufzeitüberprüfung hinzu, um mögliche Datenverluste zu behandeln, damit Ihr Programm keine falschen Ergebnisse erzeugt.
Jede Konvertierung von einem Gleitkommatyp zu einem ganzzahligen Typ ist eine einschränkende Konvertierung, da der Bruchteil des Gleitkommawerts verworfen wird und verloren geht.
Das folgende Codebeispiel zeigt einige implizite einschränkende Konvertierungen sowie die Warnungen, die der Compiler dafür ausgibt.
int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
wchar_t wch = 'A'; //OK
char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
// to 'char', possible loss of data
unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
// 'int' to 'unsigned char'
int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
// 'int', possible loss of data
int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
// 'int', possible loss of data
Konvertierungen zwischen Typen mit und ohne Vorzeichen
Ein ganzzahliger Typ mit Vorzeichen und seine Entsprechung ohne Vorzeichen haben immer die gleiche Größe, sie unterscheiden sich jedoch in der Interpretation des Bitmusters für die Werttransformation. Das folgende Codebeispiel zeigt, was geschieht, wenn das gleiche Bitmuster als Wert mit Vorzeichen und als Wert ohne Vorzeichen interpretiert wird. Das sowohl in num
als auch in num2
gespeicherte Bitmuster weicht nie von dem in der Abbildung oben Gezeigten ab.
using namespace std;
unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
short num2 = num;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: "unsigned val = 65535 signed val = -1"
// Go the other way.
num2 = -1;
num = num2;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: "unsigned val = 65535 signed val = -1"
Beachten Sie, dass die Werte in beide Richtungen neu interpretiert werden. Wenn Ihr Programm seltsame Ergebnisse erzeugt, in denen das Vorzeichen des Werts anders als erwartet umgekehrt erscheint, suchen Sie nach impliziten Konvertierungen zwischen ganzzahligen Typen mit und ohne Vorzeichen. Im folgenden Beispiel wird das Ergebnis des Ausdrucks ( 0 - 1) implizit in int
unsigned int
den Speicherort num
konvertiert. Dies führt zu einer Neuinterpretation des Bitmusters.
unsigned int u3 = 0 - 1;
cout << u3 << endl; // prints 4294967295
Der Compiler warnt nicht vor impliziten Konvertierungen zwischen signierten und nicht signierten integralen Typen. Daher wird empfohlen, signierte zu nicht signierte Konvertierungen vollständig zu vermeiden. Wenn Sie sie nicht vermeiden können, fügen Sie eine Laufzeitüberprüfung hinzu, um zu ermitteln, ob der konvertierte Wert größer oder gleich Null und kleiner als oder gleich dem Maximalwert des signierten Typs ist. Werte in diesem Bereich werden von Typen mit Vorzeichen in Typen ohne Vorzeichen oder Typen ohne Vorzeichen in Typen mit Vorzeichen übertragen, ohne neu interpretiert zu werden.
Zeigerkonvertierungen
In vielen Ausdrücken wird ein Array im C-Format implizit in einen Zeiger auf das erste Element im Array konvertiert, und konstante Konvertierungen können automatisch ausgeführt werden. Dieser Prozess ist zwar sinnvoll, kann aber auch fehleranfällig sein. Beispielsweise scheint das folgende schlecht gestaltete Codebeispiel unsinnig zu sein, und dennoch wird es kompiliert und erzeugt ein Ergebnis von "p". Zuerst wird die literale Zeichenfolgenkonstante "Help" in einen char*
konvertiert, der auf das erste Element des Arrays zeigt. Dieser Zeiger wird dann um drei Elemente erhöht, damit er auf das letzte Element "p" zeigt.
char* s = "Help" + 3;
Explizite Konvertierungen (Umwandlungen)
Mithilfe eines Umwandlungsvorgangs können Sie den Compiler anweisen, einen Wert eines bestimmten Typs in einen anderen Typ zu konvertieren. Der Compiler löst in einigen Fällen einen Fehler aus, wenn die beiden Typen vollständig nicht miteinander verknüpft sind, aber in anderen Fällen wird kein Fehler ausgelöst, auch wenn der Vorgang nicht typsicher ist. Verwenden Sie Umwandlungen möglichst selten, da Konvertierungen von einem Typ in einen anderen eine potenzielle Quelle für Programmfehler darstellen. Manchmal sind Umwandlungen jedoch erforderlich, und nicht alle Umwandlungen sind gleichermaßen gefährlich. Eine effektive Verwendung einer Umwandlung besteht darin, dass Ihr Code eine schmale Konvertierung ausführt und Sie wissen, dass die Konvertierung das Programm nicht dazu veranlasst, falsche Ergebnisse zu erzielen. Dadurch erfährt der Compiler, dass Sie wissen, was Sie tun, und wird angewiesen, keine weiteren Warnungen mehr dazu auszugeben. Eine weitere Verwendungsmöglichkeit ist die Umwandlung von Zeigern auf eine abgeleitete Klasse in Zeiger auf eine Basisklasse. Eine weitere Verwendung besteht darin, die Konstität einer Variablen zu entfernen, um sie an eine Funktion zu übergeben, die ein Nicht-Const-Argument erfordert. Die meisten dieser Umwandlungsvorgänge gehen mit gewissen Risiken einher.
Bei der Programmierung im C-Format wird der gleiche Umwandlungsoperator im C-Format für alle Arten von Umwandlungen verwendet.
(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax
Der Umwandlungsoperator im C-Format ist mit dem Aufrufoperator () identisch und daher unauffällig im Code und leicht zu übersehen. Beide sind schlecht, weil sie auf einen Blick schwer zu erkennen sind oder suchen, und sie sind unterschiedlich genug, um eine beliebige Kombination von static
, und const
.reinterpret_cast
Zu ermitteln, was bei einer Umwandlung im alten Stil tatsächlich geschieht, kann kompliziert und fehleranfällig sein. Wenn eine Umwandlung erforderlich ist, empfiehlt es sich aus diesen Gründen, einen der folgenden C++-Umwandlungsoperatoren zu verwenden, die manchmal deutlich typsicherer sind und die Programmierabsicht genauer zum Ausdruck bringen:
static_cast
, für Umwandlungen, die zur Kompilierungszeit überprüft werden.static_cast
gibt einen Fehler zurück, wenn der Compiler erkennt, dass Sie zwischen Typen umwandeln möchten, die vollständig inkompatibel sind. Eine Verwendung für Umwandlungen zwischen Zeigern auf eine Basisklasse und Zeigern auf eine abgeleitete Klasse ist ebenfalls möglich. Für den Compiler ist jedoch nicht immer erkennbar, ob solche Konvertierungen zur Laufzeit sicher sind.double d = 1.58947; int i = d; // warning C4244 possible loss of data int j = static_cast<int>(d); // No warning. string s = static_cast<string>(d); // Error C2440:cannot convert from // double to std::string // No error but not necessarily safe. Base* b = new Base(); Derived* d2 = static_cast<Derived*>(b);
Weitere Informationen finden Sie unter
static_cast
.dynamic_cast
, für sichere, laufzeitgecheckte Umwandlungen von Zeiger-zu-Basis zu Zeiger-zu-Zeiger abgeleitet. Einedynamic_cast
ist sicherer als einestatic_cast
für Downcasts, aber die Laufzeitüberprüfung verursacht etwas Mehraufwand.Base* b = new Base(); // Run-time check to determine whether b is actually a Derived* Derived* d3 = dynamic_cast<Derived*>(b); // If b was originally a Derived*, then d3 is a valid pointer. if(d3) { // Safe to call Derived method. cout << d3->DoSomethingMore() << endl; } else { // Run-time check failed. cout << "d3 is null" << endl; } //Output: d3 is null;
Weitere Informationen finden Sie unter
dynamic_cast
.const_cast
, um dieconst
-ness einer Variablen zu entfernen oder eine Nichtvariableconst
zu konvertierenconst
.const
Umwandlungsabwandlung -ness durch Verwendung dieses Operators ist genauso fehleranfällig wie die Verwendung eines C-Stil-Casts, außer dass beiconst_cast
Ihnen die Wahrscheinlichkeit, dass die Umwandlung versehentlich ausgeführt wird. Manchmal müssen Sie dieconst
"-ness" einer Variablen entfernen, z. B. um eineconst
Variable an eine Funktion zu übergeben, die einen Nicht-Parameterconst
verwendet. Das folgende Beispiel zeigt die erforderliche Vorgehensweise.void Func(double& d) { ... } void ConstCast() { const double pi = 3.14; Func(const_cast<double&>(pi)); //No error. }
Weitere Informationen finden Sie unter
const_cast
.reinterpret_cast
, für Umwandlungen zwischen nicht verknüpften Typen wie einem Zeigertyp und einemint
.Hinweis
Dieser Umwandlungsoperator wird nicht so oft verwendet wie die anderen, und es ist nicht garantiert, dass er für andere Compiler portierbar ist.
Das folgende Beispiel veranschaulicht, wie
reinterpret_cast
sich die Unterschiede unterscheidenstatic_cast
.const char* str = "hello"; int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot // convert from 'const char *' to 'int' int j = (int)str; // C-style cast. Did the programmer really intend // to do this? int k = reinterpret_cast<int>(str);// Programming intent is clear. // However, it is not 64-bit safe.
Weitere Informationen finden Sie unter
reinterpret_cast
"Operator".
Siehe auch
C++-Typsystem
Willkommen bei C++
C++-Sprachreferenz
C++-Standardbibliothek