C ++ Standardkonvertierungen

C ++ Standardkonvertierungen
Es gibt zwei Entitätstypen in C ++, die grundlegenden Typen und die zusammengesetzten Typen. Die grundlegenden Typen sind die Skalartypen. Die zusammengesetzten Typen sind der Rest der Entitätstypen. Die Konvertierung kann von einem Entitätstyp zu einem anderen entsprechenden Typ stattfinden. Betrachten Sie das folgende Programm: #include
#enthalten
Verwenden von Namespace STD;
int main ()

int rt1 = sqrt (5);
int rt2 = sqrt (8);
Cout<Rückkehr 0;

Die Ausgabe ist 2, 2, Dies bedeutet, dass das Programm die Quadratwurzel von 5 als 2 und die Quadratwurzel von 8 auch als 2 zurückgegeben hat. Also die ersten beiden Aussagen in der hauptsächlich() Funktion hat die Antworten auf die Quadratwurzel von 5 und die Quadratwurzel von 8 geführt. In diesem Artikel wird keine Bodenbeläge oder Decke in C besprochen++. In diesem Artikel wird vielmehr die Konvertierung eines C ++ -Typs in einen anderen geeigneten C ++ -Typ erörtert. Angeben einer Annäherung an Wert, Präzision oder Einschränkung hinzugefügt oder entfernt. Grundkenntnisse von C ++ ist eine Voraussetzung, um diesen Artikel zu verstehen.

Artikelinhalt

  • Integrale Konvertierungen
  • Schwimmpunktkonvertierungen
  • Schwimmende Integralkonvertierungen
  • Ganzzahl -Konvertierungsranking
  • Integrale Werbeaktionen
  • Übliche arithmetische Konvertierungen
  • Schwimmende Punktförderung
  • Zeigerkonvertierungen
  • Funktion zu Zeigerkonvertierungen
  • Boolesche Konvertierungen
  • Lvalue, Prvalue und XValue
  • XVALUE
  • Lvalue-to-Relevalue-Konvertierungen
  • Array-to-Pointer-Konvertierungen
  • Funktionen zu Zeigerkonvertierungen
  • Temporäre Materialisierungsumwandlungen
  • Qualifikationsumwandlungen
  • Abschluss

Integrale Konvertierungen

Integrale Konvertierungen sind Ganzzahlkonvertierungen. Nicht signierte Ganzzahlen umfassen „vorzeichenloses Zeichen“, „nicht signiertes kurzes int“, „nicht signiert int“, „nicht signiertes langes int“ und „nicht signiertes lange int int int."Die entsprechenden unterzeichneten Ganzzahlen enthalten" Signed Char "," Short Int "," int "," Long int "und" Long Long Int ".”Jeder INT -Typ sollte in so vielen Bytes wie sein Vorgänger gehalten werden. Für die meisten Systeme kann ein Entitätstyp ohne Probleme in einen entsprechenden Typ konvertiert werden. Das Problem tritt beim Konvertieren von einem größeren Bereichstyp in einen kleineren Bereichstyp oder beim Umwandeln einer signierten Zahl in eine entsprechende nicht signierte Zahl auf.

Jeder Compiler hat einen maximalen Wert, den er für den kurzen Int dauern kann. Wenn eine Zahl höher als das Maximum, das für einen INT bestimmt ist, dem kurzen Int zugeordnet ist, folgt der Compiler einem Algorithmus und gibt eine Zahl innerhalb des Bereichs des kurzen Int zurück. Wenn der Programmierer Glück hat, warnt der Compiler vor Ärger bei der Verwendung unangemessener Konvertierung. Die gleiche Erklärung gilt für Conversions anderer Int -Typen.

Der Benutzer sollte die Dokumentation des Compilers konsultieren, um die Grenzwerte für jeden Entitätstyp zu bestimmen.

Wenn eine negativ signierte kurze Int -Zahl in eine nicht signierte kurze Int -Zahl umgewandelt werden soll, folgt der Compiler einem Algorithmus und gibt eine positive Zahl innerhalb des Bereichs des nicht signierten Short Int zurück. Diese Art von Umwandlung sollte vermieden werden. Die gleiche Erklärung gilt für Conversions anderer Int -Typen.

Jede Ganzzahlnummer, mit Ausnahme von 0, kann in boolean true konvertiert werden. 0 wird in boolean false umgewandelt. Der folgende Code zeigt dies:

int a = -27647;
float b = 2.5;
int c = 0;
bool a1 = a;
bool b1 = b;
bool c1 = c;
Cout<Cout<Cout<Die Ausgabe ist:

1
1
0

1 bedeutet wahr und 0 bedeutet falsch in der Ausgabe

Schwimmpunktkonvertierungen

Schwimmpunkttypen umfassen "Float", "Double" und "Long Double".„Gleitkomma-Typen werden nicht in signiert und nicht signiert, wie Ganzzahlen. Jeder Typ kann eine signierte oder nicht signierte Nummer haben. Ein Gleitkomma-Typ sollte mindestens die gleiche Präzision wie sein Vorgänger haben. Das heißt, "langes Doppel" sollte gleich oder mehr Präzision wie "doppelt" haben, und "Doppel" sollte gleich oder mehr Präzision haben wie "Schweben.”

Denken Sie daran, dass der Bereich eines Gleitpunkts nicht kontinuierlich ist. Vielmehr ist es in kleinen Schritten. Je größer die Genauigkeit des Typs ist, desto kleiner die Schritte und desto größer die Anzahl der Bytes, um die Anzahl zu speichern. Wenn also eine Schwimmpunktzahl von einem niedrigeren Präzisionstyp zu einem höheren Genauigkeitstyp umgewandelt wird, muss der Programmierer eine falsche Zunahme der Präzision und eine mögliche Erhöhung der Anzahl der Bytes für die Zahl-Storage akzeptieren. Wenn eine Schwimmpunktzahl von einem höheren Präzisionstyp zu einem niedrigeren Genauigkeitstyp umgewandelt wird, muss der Programmierer einen Präzisionsverlust akzeptieren. Wenn die Anzahl der Bytes für die Zahlen-Storage reduziert werden muss, folgt der Compiler einem Algorithmus und gibt eine Zahl als Ersatz zurück (was wahrscheinlich nicht das ist, was der Programmierer will). Beachten Sie auch Probleme außerhalb des Bereichs.

Schwimmende Integralkonvertierungen

Eine schwimmende Punktzahl wird in eine Ganzzahl umgewandelt, indem der Bruchteil abgeschnitten wird. Der folgende Code zeigt dies:

float f = 56.953;
int i = f;
Cout<Die Ausgabe ist 56. Die Bereiche für den Schwimmer und die Ganzzahl müssen kompatibel sein.

Wenn eine Ganzzahl in einen Schwimmer konvertiert wird, ist der als Float angezeigte Wert der gleiche wie ein Tipp als Ganzzahl. Das Float -Äquivalent kann jedoch den genauen Wert sein oder einen leichten Bruchunterschied aufweisen, der nicht angezeigt wird. Der Grund für den fraktionalen Unterschied ist, dass im Computer schwimmende Punktzahlen in kleinen Bruchschritten dargestellt werden, und so wäre es ein Zufall, die Ganzzahl genau darzustellen. Obwohl die als Float angezeigte Ganzzahl dieselbe ist wie bei der Eingabe, kann die Anzeige eine Annäherung an das sein, was gespeichert wird.

Ganzzahl -Konvertierungsranking

Jeder ganzzahlige Typ hat einen Rang, der ihm gegeben wurde. Diese Rangliste hilft bei der Konvertierung. Das Ranking ist relativ; Die Ränge befinden sich nicht auf festen Ebenen. Mit Ausnahme von Char und Signed Char haben keine zwei unterzeichneten Ganzzahlen den gleichen Rang (vorausgesetzt, CHAR ist unterschrieben). Unsignierte Ganzzahltypen haben das gleiche Ranking wie die entsprechenden signierten Ganzzahltypen. Das Ranking lautet wie folgt:

  • Angenommen, Char ist unterschrieben, dann haben Char und Signed Char den gleichen Rang.
  • Der Rang eines signierten Ganzzahltyps ist größer als der Rang eines signierten Ganzzahlenstyps einer geringeren Anzahl von Speicherbytes. Der Rang eines signierten langen Int ist also größer als der Rang eines signierten langen INT, der größer ist als der Rang eines signierten INT, der größer ist als der Rang eines signierten Kurzfilms, der größer ist als der Rang eines signierten Zeichens.
  • Der Rang eines unsignierten ganzzahligen Typs entspricht dem Rang des entsprechenden signierten Ganzzahltyps.
  • Der Rang eines nicht signierten SHAR entspricht dem Rang eines unterzeichneten SHAR.
  • Bool hat den geringsten Rang; Sein Rang ist geringer als der von signiertem Char.
  • char16_t hat den gleichen Rang wie der kurze int. char32_t hat den gleichen Rang wie der int. Für den G ++ - Compiler hat WCHAR_T den gleichen Rang wie der int.

Integrale Werbeaktionen

Integrale Promotionen sind ganzzahlige Werbeaktionen. Es gibt keinen Grund, warum eine ganze Zahl von weniger Bytes nicht durch eine ganze Zahl größerer Bytes dargestellt werden kann. Integer Promotions befasst sich mit allem, was folgt:

  • Ein signiertes kurzes Int (zwei Bytes) kann in ein signiertes INT (vier Bytes) umgewandelt werden. Ein nicht signiertes kurzes Int (zwei Bytes) kann in ein nicht signiertes INT (vier Bytes) umgewandelt werden. Hinweis: Umwandlung eines kurzen Int in einen langen oder langen Langen int führt zu einer Lagerverschwendung (Objektposition) Bytes und einer Speicherverschwendung. Bool, char16_t, char32_t und wchar_t sind von dieser Aktion ausgenommen (mit dem G ++ - Compiler, char32_t und wchar_t haben die gleiche Anzahl von Bytes).
  • Mit dem G ++ - Compiler kann ein char16_t -Typ in einen signierten INT -Typ oder einen nicht signierten INT -Typ konvertiert werden. Ein char32_t -Typ kann in einen signierten INT -Typ oder einen nicht signierten INT -Typ konvertiert werden. und ein wchar_t -Typ kann in einen signierten oder nicht signierten INT -Typ konvertiert werden.
  • Ein Bool -Typ kann in einen INT -Typ umgewandelt werden. In diesem Fall wird True 1 (vier Bytes) und Falsch wird zu 0 (vier Bytes). Int kann signiert oder signiert werden.
  • Integer Promotion existiert auch für den nicht genehmigten Aufzählungsart - siehe später.

Übliche arithmetische Konvertierungen

Betrachten Sie den folgenden Code:

float f = 2.5;
int i = f;
Cout<Der Code erstellt ohne Angabe von Warnungen oder Fehler, die die Ausgabe von angeben 2, Das ist wahrscheinlich nicht das, was erwartet wurde. = ist ein binärer Bediener, weil es einen linken und rechten Operand benötigt. Betrachten Sie den folgenden Code:

int i1 = 7;
int i2 = 2;
float flt = i1 / i2;
Cout<Die Ausgabe ist 3, Aber das ist falsch; es sollte sein 3.5. Der Divisionsbetreiber ist auch ein Binärbetreiber.

C ++ hat übliche arithmetische Konvertierungen, die der Programmierer wissen muss, um Fehler bei der Codierung zu vermeiden. Die üblichen arithmetischen Konvertierungen von Binäroperatoren sind wie folgt:

  • Wenn der Operand vom Typ "langes Doppel" ist, wird der andere in long Double umgewandelt.
  • Andernfalls wird der andere Operand doppelt so.
  • Ansonsten, wenn der Operand float ist, wird der andere in Schwimmer konvertiert. Im obigen Code ist das Ergebnis von I1/I2 offiziell 2; Deshalb ist FLT 2. Das Ergebnis der Binärdatei /wird als rechter Operand für den Binäroperator angewendet, =. Der Endwert von 2 ist also ein Schwimmer (kein int).

Andernfalls würde die Ganzzahl -Promotion wie folgt stattfinden:

  • Wenn beide Operanden vom gleichen Typ sind, findet keine weitere Konvertierung statt.
  • Andernfalls, wenn beide Operanden signierte Ganzzahltypen sind oder beides unsignierte Ganzzahltypen sind, wird der Operand des Typs mit dem niedrigeren ganzzahligen Rang in den Typ des Operanden mit dem höheren Rang umgewandelt.
  • Andernfalls, wenn ein Operand unterschrieben und der andere nicht signiert ist und der nicht signierte Operandtyp größer oder gleich dem Rang des signierten Operandentyps ist und der Wert des signierten Operanden größer oder gleich Null ist Der signierte Operand wird in den nicht signierten Operandentyp umgewandelt (wobei der Bereich berücksichtigt wird). Wenn der signierte Operand negativ ist, folgt der Compiler einem Algorithmus und gibt eine Zahl zurück, die für den Programmierer möglicherweise nicht akzeptabel ist.
  • Andernfalls, wenn ein Operand ein signierter Ganzzahltyp ist und der andere ein nicht signierter Ganzzahltyp ist und wenn alle möglichen Werte des Typs des Operanden mit dem nicht signierten Ganzzahltyp durch den signierten Ganzzahltyp dargestellt werden können, wird der nicht signierte Ganzzahltyp in den Typ des Operanden des signierten Ganzzahltyps konvertiert werden.
  • Andernfalls würden die beiden Operanden (z.

Schwimmende Punktförderung

Schwimmpunkttypen umfassen "Float", "Double" und "Long Double".”Ein Floating-Punkt-Typ sollte mindestens die gleiche Präzision wie sein Vorgänger haben. Die Gleitkomma-Promotion ermöglicht die Konvertierung von Float zu doppelt oder von doppelt zum langen Doppel.

Zeigerkonvertierungen

Ein Zeiger eines Objekttyps kann einem Zeiger eines anderen Objekttyps nicht zugewiesen werden. Der folgende Code wird nicht kompilieren:

int id = 6;
int* intptr = &id;
float idf = 2.5;
float* floatptr = &idf;
intptr = floatptr; // Fehler hier

Ein Nullzeiger ist ein Zeiger, dessen Adresswert Null ist. Ein Nullzeiger eines Objekttyps kann einem Nullzeiger eines anderen Objekttyps nicht zugewiesen werden. Der folgende Code wird nicht kompilieren:

int id = 6;
int* intptr = &id;
intptr = 0;
float idf = 2.5;
float* floatptr = &idf;
floatptr = 0;
intptr = floatptr; // Fehler hier

Ein Nullzeigerkonst von einem Objekttyp kann nicht einem Nullzeiger -Konst von einem anderen Objekttyp zugeordnet werden. Der folgende Code wird nicht kompilieren:

int id = 6;
int* intptr = &id;
int* const intpc = 0;
float idf = 2.5;
float* floatptr = &idf;
float* const floatpc = 0;
intpc = floatpc; // Fehler hier

Ein Nullzeiger kann einen anderen Adresswert für seinen Typ erhalten. Der folgende Code zeigt dies:

float idf = 2.5;
float* floatptr = 0;
floatptr = &idf;
Cout<<*floatPtr<<'\n';

Die Ausgabe ist 2.5.

Wie erwartet kann einer Nullzeigerkonstante keinen Adresswert seines Typs zugewiesen werden. Der folgende Code wird nicht kompilieren:

float idf = 2.5;
float* const floatpc = 0;
floatpc = &idf; // Fehler hier

Eine Nullzeigerkonstante kann jedoch einem gewöhnlichen Zeiger zugeordnet werden, aber vom gleichen Typ (dies ist zu erwarten). Der folgende Code zeigt dies:

float idf = 2.5;
float* const floatpc = 0;
float* floatPter = &idf;
floatPter = floatpc; //OK
Cout << floatPter << '\n';

Die Ausgabe ist 0.

Zwei Nullzeigerwerte desselben Typs vergleiche (==) gleich.

Ein Zeiger auf einen Objekttyp kann einem Zeiger auf void zugeordnet werden. Der folgende Code zeigt dies:

float idf = 2.5;
float* floatptr = &idf;
void* vd;
vd = floatptr;

Der Code erstellt ohne Warnung oder Fehlermeldung.

Funktion zu Zeigerkonvertierungen

Ein Zeiger auf eine Funktion, die keine Ausnahme auswirft, kann einem Zeiger zugewiesen werden, um zu funktionieren. Der folgende Code zeigt dies:

#enthalten
Verwenden von Namespace STD;
void fn1 () noexcept

Cout << "with noexcept" << '\n';

void fn2 ()

// Aussagen

void (*func1) () noexcept;
void (*func2) ();
int main ()

func1 = &fn1;
func2 = &fn2;
func2 = &fn1;
func2 ();
Rückkehr 0;

Die Ausgabe ist ohne Ausnahme.

Boolesche Konvertierungen

In C ++ umfassen Entitäten, die zu False führen können.„Alle anderen Unternehmen führen zu wahr. Der folgende Code zeigt dies:

bool a = 0.0; Cout << a <<'\n';
float* floatptr = 0;
bool b = floatptr; Cout << b <<'\n';
bool c = -2.5; Cout << c <<'\n';
bool d = +2.5; Cout << d <<'\n';

Die Ausgabe ist:

0 // für false
0 // für false
1 // für wahr
1 // für wahr

Lvalue, Prvalue und XValue

Betrachten Sie den folgenden Code:

int id = 35;
int & id1 = id;
Cout << id1 << '\n';

Die Ausgabe ist 35. Im Code sind ID und ID1 LVALUES, da sie einen Ort (Objekt) im Speicher identifizieren. Der Ausgang 35 ist ein PrValue. Jeder buchstäbliche, außer einer Streicherliteral, ist ein Prvalue. Andere Prvalues ​​sind nicht so offensichtlich, wie in den folgenden Beispielen. Betrachten Sie den folgenden Code:

int id = 62;
int* ptr = &id;
int* pter;

PTR ist ein LVALUE, da es einen Ort (Objekt) im Speicher identifiziert. Andererseits ist Pter kein Lvalue. Pter ist ein Zeiger, identifiziert jedoch keinen Speicherort im Speicher (es zeigt nicht auf ein Objekt). Pter ist also ein Prvalue.

Betrachten Sie den folgenden Code:

void fn ()

// Aussagen

void (*func) () = &fn;
float (*functn) ();

FN () und (*func) () sind lvalue -Ausdrücke, weil sie eine Entität (Funktion) im Speicher identifizieren. Andererseits ist (*functn) () kein LVALUE -Ausdruck. (*functn) () ist ein Zeiger auf eine Funktion, identifiziert jedoch keine Entität im Speicher (es zeigt nicht auf eine Funktion im Speicher). Also ist (*functn) () ein prvaler Ausdruck.

Betrachten Sie nun den folgenden Code:

Struktur s

int n;
;
S obj;

S ist eine Klasse und OBJ ist ein Objekt, das aus der Klasse instanziiert ist. OBJ identifiziert ein Objekt im Speicher. Eine Klasse ist eine verallgemeinerte Einheit. S identifiziert also kein Objekt im Speicher wirklich. S soll ein unbenanntes Objekt sein. S ist auch ein prorue -Ausdruck.

Der Schwerpunkt dieses Artikels liegt auf Prvalues. Prvalue bedeutet reines Rvalue.

XVALUE

XValue steht für den Ablaufwert. Temporäre Werte sind ablaufende Werte. Ein Lvalue kann ein XValue werden. Ein Prvalue kann auch ein XValue werden. Der Schwerpunkt dieses Artikels liegt auf Prvalues. Ein XValue ist ein LVALUE oder ein unbenannter RValue -Referenz, dessen Speicher wiederverwendet werden kann (normalerweise, weil er am Ende seiner Lebensdauer ist). Betrachten Sie den folgenden Code, der funktioniert:

Struktur s

int n;
;
int q = s ().N;

Der Ausdruck “int q = s ().N;" Kopiert den Wert n an q. S () ist nur ein Mittel; Es ist kein regelmäßig verwendeter Ausdruck. S () ist ein PrValue, dessen Verwendung es in einen XValue umgewandelt hat.

Lvalue-to-Relevalue-Konvertierungen

Betrachten Sie die folgende Erklärung:

int ii = 70;

70 ist ein PrValue (rvalue) und II ist ein Lvalue. Betrachten Sie nun den folgenden Code:

int ii = 70;
int tt = ii;

In der zweiten Aussage befindet sich II in der Situation eines Prvalue, so dass II dort zu einem Prvalue wird. Mit anderen Worten, der Compiler wandelt II in ein Prvalue implizit um. Das heißt, wenn ein Lvalue in einer Situation verwendet wird, in der die Implementierung von einem PrValue erwartet.

Array-to-Pointer-Konvertierungen

Betrachten Sie den folgenden Code, der funktioniert:

char* p;
char q [] = 'a', 'b', 'c';
p = & q [0];
++P;
Cout<<*p<<'\n';

Die Ausgabe ist B. Die erste Aussage ist Ausdruck und ein Zeiger auf einen Charakter. Aber auf welchen Charakter ist die Aussage zum Zeigen? - Kein Charakter. Es ist also ein Prvalue und kein Lvalue. Die zweite Aussage ist ein Array, in dem q [] ein LVALUE -Ausdruck ist. Die dritte Aussage verwandelt das Prvalue P in einen LVALUE -Ausdruck, der auf das erste Element des Arrays hinweist.

Funktionen zu Zeigerkonvertierungen

Betrachten Sie das folgende Programm:

#enthalten
Verwenden von Namespace STD;
void (*func) ();
void fn ()

// Aussagen

int main ()

func = &fn;
Rückkehr 0;

Der Ausdruck "void (*func) ();" ist ein Zeiger auf eine Funktion. Aber auf welche Funktion ist der Ausdruck, der zeigt? - Keine Funktion. Es ist also ein Prvalue und kein Lvalue. FN () ist eine Funktionsdefinition, wobei FN ein LVALUE -Ausdruck ist. In main (), „func = &fn;Verwandelt das PrValue, den Func, in einen LVALUE -Ausdruck, der auf die Funktion fn () hinweist.

Temporäre Materialisierungsumwandlungen

In C ++ kann ein PrValue in einen XValue desselben Typs umgewandelt werden. Der folgende Code zeigt dies:

Struktur s

int n;
;
int q = s ().N;

Hier wurde der PrValue () in einen XValue umgewandelt. Als XValue würde es nicht lange dauern - siehe mehr Erklärung oben.

Qualifikationsumwandlungen

Ein CV-qualifizierter Typ ist ein Typ, der durch das reservierte Wort „const“ und/oder das reservierte Wort „volatil“ qualifiziert ist.”

CV-Qualifikation ist ebenfalls eingestuft. Keine CV-Qualifikation ist weniger als die Qualifikation „Const“, was weniger als „const volatile“ Qualifikation ist. Keine CV-Qualifikation ist weniger als „flüchtig“ Qualifikation, was weniger als „const volatile“ Qualifikation ist. Es gibt also zwei Ströme von Qualifikationsranking. Ein Typ kann mehr CV-qualifiziert sein als ein anderer.

Ein niedrigerer prValue-CV-qualifizierter Typ kann in einen CV-qualifizierten Prvalue-Typ umgewandelt werden. Beide Typen sollten Zeiger auf CV sein.

Abschluss

C ++ - Entitäten können von einem Typ in einen verwandten Typ implizit oder explizit konvertiert werden. Der Programmierer muss jedoch verstehen, was konvertiert werden kann und was nicht, in welcher Form umgewandelt werden kann. Die Konvertierung kann in den folgenden Domänen stattfinden: Integrale Konvertierungen, Schwimmpunktkonvertierungen, schwebende Integralkonvertierungen, übliche arithmetische Konvertierungen, Zeigerkonvertierungen, Funktionen auf Zeigerkonvertierungen, booleale Konvertierungen, Konvertierungen von Lvalue-to-Relevern, Array-to-Pointer-Konvertierungen , Funktionen zu Zeigerumwandlungen, Konvertierungen der temporären Materialisierung und Qualifikationsumwandlungen.