Warum kann ich kein static_cast zwischen char * und unsigned char * durchführen?

Lesezeit: 9 Minuten

Anscheinend betrachtet der Compiler sie als nicht verwandte Typen und daher reinterpret_cast Wird benötigt. Warum ist das die Regel?

  • Ich nehme den SHA-1-Hash eines Strings. c_str() gibt a . zurück const char * und die SHA-1-Funktion nimmt a const unsigned char * als Argument.

    – Nick

    14. April ’12 um 7:36


  • Und was erwarten Sie, wenn diese Zeichenfolge negative Zeichenwerte enthält?

    – Pubby

    14. April ’12 um 7:41

  • Ich erwarte jeden negativen Wert c werden c + 256, wie es beim Konvertieren eines vorzeichenbehafteten Bytes in ein vorzeichenloses üblich ist. Ehrlich gesagt mache ich nur die Konvertierung, um einen Hash-Wert zu berechnen. Es ist mir egal, wie sie konvertiert werden, solange sie jedes Mal auf die gleiche Weise konvertiert werden.

    – Nick

    14. April ’12 um 7:46

  • @Nick: Konvertieren eines char zu einem unsigned char ist eine Konvertierung. Konvertieren char * zu unsigned char* und dann die Elemente lesen vorausgesetzt dass sie konvertiert wurden, wenn sie es nicht getan haben, ist sehr unterschiedlich. Es funktioniert auf einem System, bei dem die Konvertierung nicht wirklich eine Änderung der Darstellung erfordert (zB auf einem Zweier-Komplement-System), aber da dies eine implementierungsspezifische Annahme ist, ist es angemessen, dass eine explizite reinterpret_cast Wird benötigt.

    – CB Bailey

    14. April ’12 um 10:38

Warum kann ich kein static cast zwischen char und unsigned
EdChum

Sie sind völlig unterschiedliche Typen siehe Standard:

3.9.1 Grundtypen [basic.fundamental]

1 Als Zeichen char deklarierte Objekte müssen groß genug sein, um jedes Mitglied des grundlegenden Zeichensatzes der Implementierung zu speichern. Wenn ein Zeichen aus diesem Satz in einem Zeichenobjekt gespeichert wird, ist der ganzzahlige Wert dieses Zeichenobjekts gleich dem Wert der Einzelzeichen-Literalform dieses Zeichens. Es wird durch die Implementierung definiert, ob ein char-Objekt negative Werte enthalten kann. Zeichen können explizit ohne Vorzeichen deklariert werden oder
unterzeichnet. Einfaches Zeichen, Zeichen mit Vorzeichen und Zeichen ohne Vorzeichen sind drei verschiedene Typen. Ein Zeichen, ein Zeichen mit Vorzeichen und ein Zeichen ohne Vorzeichen belegen den gleichen Speicherplatz und haben die gleichen Ausrichtungsanforderungen (Grundtypen); das heißt, sie haben dieselbe Objektdarstellung. Bei Zeichentypen alle Bits des Objekts
Repräsentation an der Wertrepräsentation teilnehmen. Bei vorzeichenlosen Zeichentypen stellen alle möglichen Bitmuster der Wertdarstellung Zahlen dar. Diese Anforderungen gelten nicht für andere Typen. In einer bestimmten Implementierung kann ein einfaches char-Objekt entweder die gleichen Werte wie ein vorzeichenbehaftetes oder ein vorzeichenloses Zeichen annehmen; welche ist implementierungsdefiniert.

Analog dazu schlägt auch folgendes fehl:

unsigned int* a = new unsigned int(10);
int* b = static_cast<int*>(a); // error different types

a und b sind völlig unterschiedliche Typen, Sie fragen sich wirklich, warum static_cast so restriktiv ist, wenn es die folgenden problemlos ausführen kann

unsigned int a = new unsigned int(10);
int b = static_cast<int>(a); // OK but may result in loss of precision

und warum kann daraus nicht abgeleitet werden, dass die Zieltypen die gleiche Bitfeldbreite haben und dargestellt werden können? Dies ist für skalare Typen möglich, aber für Zeiger, es sei denn, das Ziel wird von der Quelle abgeleitet und Sie möchten einen Downcast durchführen, dann funktioniert das Umwandeln zwischen Zeigern nicht.

Bjarne Stroustrop erklärt warum static_cast‘s sind in diesem Link nützlich: http://www.stroustrup.com/bs_faq2.html#static-cast aber in abgekürzter Form ist es Sache des Benutzers, seine Absichten klar zu formulieren und dem Compiler die Möglichkeit zu geben, zu überprüfen, ob das von Ihnen beabsichtigte erreicht werden kann, da static_cast unterstützt das Casting zwischen verschiedenen Zeigertypen nicht, dann kann der Compiler diesen Fehler abfangen, um den Benutzer zu warnen, und wenn er diese Konvertierung wirklich durchführen möchte, sollte er verwenden reinterpret_cast.

  • thx für die Angabe des Standards hier. Ich habe es nicht verfügbar.

    – Tobias Langner

    14. April ’12 um 8:27

  • Aus dem gleichen Grund können Sie static_cast von doubles zu ints und umgekehrt, was Sie nicht tun können, ist static_cast double* zu int* ein Verlust an Präzision

    – EdChum

    14. April ’12 um 9:02

  • @Nick: Auch mit int und float. Sie können die gleiche Größe haben, aber wenn Sie eine int, dann versuche es wie a . zu lesen float, was du bekommst, hängt davon ab, wie genau float wird im Speicher abgelegt. Und da die Spezifikation dies nicht tut sagen wie float wird im Speicher abgelegt, die Spezifikation kann nicht definieren, was die int sieht aus wie. Denken Sie daran: Der C++-Standard existiert, um Garantien für das zu bieten, was Sie erhalten. Um dieses Verhalten zu definieren, müsste der Standard detailliert beschreiben, wie float im Gedächtnis angelegt ist, sowie wie int ist im Gedächtnis angelegt.

    – Nicol Bolas

    14. April ’12 um 20:22

  • @Nick: Kurz gesagt: Sie verwechseln das Konzept “was auf echten Maschinen passiert” mit “was die Spezifikation erfordert”.

    – Nicol Bolas

    14. April ’12 um 20:23

  • Die Spezifikation sagt nicht, dass das höchstwertige Bit das Vorzeichenbit sein muss, daher könnte eine Implementierung das niedrigstwertige Bit haben. Wenn dies der Fall wäre, würde der Compiler, wenn er ein unsigned char mit dem Wert 1 in ein signed char umwandelt, wissen, dass er die Bits nach links um 1 verschieben muss, um das Vorzeichenbit zu berücksichtigen. Aber bei einem unsigned char* und char* ist es nicht die Aufgabe des Casts, die Werte an der Position des Pointers anzupassen. Es wüsste sowieso nicht, wie viele Zeichen angepasst werden müssen.

    – Cemafor

    12. März ’15 um 22:59

1641781071 270 Warum kann ich kein static cast zwischen char und unsigned
Tobias Langner

Sie versuchen, nicht verwandte Zeiger mit einem static_cast zu konvertieren. Dafür ist static_cast nicht da. Hier sieht man: Typ Gießen.

Mit static_cast können Sie numerische Daten (zB char in unsigned char sollte funktionieren) oder Zeiger auf verwandte Klassen (durch eine Vererbung verwandt) konvertieren. Dies ist beides nicht der Fall. Sie möchten einen unabhängigen Zeiger in einen anderen konvertieren, daher müssen Sie reinterpret_cast verwenden.

Im Grunde ist das, was Sie versuchen, für den Compiler dasselbe wie der Versuch, ein char * in ein void * umzuwandeln.


Ok, hier noch ein paar zusätzliche Gedanken, warum es grundsätzlich falsch ist, dies zuzulassen. Mit static_cast können numerische Typen ineinander umgewandelt werden. Es ist also völlig legal, Folgendes zu schreiben:

char x = 5;
unsigned char y = static_cast<unsigned char>(x);

was ist auch möglich:

double d = 1.2;
int i = static_cast<int>(d);

Wenn Sie sich diesen Code in Assembler ansehen, werden Sie feststellen, dass der zweite Cast keine bloße Neuinterpretation des Bitmusters von d ist, sondern hier einige Assembler-Anweisungen für Konvertierungen eingefügt werden.

Wenn wir dieses Verhalten nun auf Arrays ausdehnen, könnte es in dem Fall funktionieren, in dem einfach eine andere Art der Interpretation des Bitmusters ausreicht. Aber was ist mit der Umwandlung von Arrays von Doubles in Arrays von Ints? Hier müssen Sie entweder erklären, dass Sie einfach eine Neuinterpretation der Bitmuster wünschen – dafür gibt es einen Mechanismus namens reinterpret_cast, oder Sie müssen zusätzliche Arbeit leisten. Wie Sie sehen, reicht eine einfache Erweiterung des static_cast für Zeiger / Arrays nicht aus, da es sich ähnlich verhalten muss wie das statische_casting einzelner Werte der Typen. Dies erfordert manchmal zusätzlichen Code, und es ist nicht klar, wie dies für Arrays erfolgen soll. In Ihrem Fall – aufhören bei – weil es die Konvention ist? Dies ist für Nicht-String-Fälle (Zahl) nicht ausreichend. Was passiert, wenn sich die Größe des Datentyps ändert (zB int vs. double auf x86-32bit)?

Das gewünschte Verhalten kann nicht für alle Anwendungsfälle richtig definiert werden, deshalb ist es nicht im C++-Standard enthalten. Andernfalls müssten Sie sich Dinge merken wie: “Ich kann diesen Typ in den anderen umwandeln, solange sie vom Typ Integer sind, die gleiche Breite haben und …”. Auf diese Weise ist es völlig klar – entweder sind es verwandte KLASSEN – dann können Sie die Zeiger umwandeln, oder es handelt sich um numerische Typen – dann können Sie die Werte umwandeln.

  • Ich habe in meinem Eröffnungspost bestätigt, dass der Compiler sagt, dass es sich um unabhängige Zeiger handelt. Was ich wissen möchte ist warum. Es scheint mir, wenn T1 bezieht sich auf T2, dann T1 * sollte “verwandt” sein mit T2 *. Warum ist diese Eingaberegel nicht korrekt (für primitive Typen)?

    – Nick

    14. April ’12 um 7:49


  • @Nick es ist nicht “irgendwie verwandt”, sondern “verwandte Klassen”. Wie Sie sagten, sind char und unsigned char Primitive – keine Klassen. Das ist der Grund – und das habe ich gesagt, wenn Sie genau lesen. Sie haben Recht – wenn Klasse T1 mit Klasse T2 verwandt ist, können Sie static_cast verwenden, um T1* in T2* zu konvertieren. Dies ist nicht das, was Sie tun. char steht nicht im Zusammenhang mit unsigned char im Sinne der vom C++-Standard geforderten Relation.

    – Tobias Langner

    14. April ’12 um 8:24

  • Wenn Sie jedoch den Zeiger fallen lassen, hat der Compiler keine Probleme beim Casting zwischen primitiven Typen, z unsigned char a = 255; char b = static_cast<char>(a); Es scheint ein bisschen seltsam, denn wenn T1 und T2 Klassen sind, ist die Umwandlung zwischen Zeigern nicht korrekt, da Sie Folgendes tun könnten: class A; class B : public A; B *b = new B[4]; b[0] = B(); A *a = static_cast<A *>(b); a[1] = A(); B b1 = b[1]; // oops Es scheint, als ob nur Die Zeit, zu der die Besetzung sicher sein sollte, liegt zwischen primitiven Typen.

    – Nick

    14. April ’12 um 8:37

  • ja – und das Verhalten dafür ist klar und gut definiert. Sie haben einige Regeln, wie Sie einen numerischen Typ in den anderen umwandeln. Dies kann so einfach sein wie das Kopieren des Bitmusters in den neuen Speicher oder so kompliziert wie das Umwandeln eines Doubles in ein Int. Trotzdem – es ist nur für einen Wert und daher leicht definierbar. Siehe meine Erklärung oben. Der static_cast hat 2 völlig unterschiedliche Verwendungen, je nachdem, ob der Typ ein Zeigertyp ist oder nicht. Sie hätten es für die 2 Anwendungsfälle anders benennen sollen (zB static_cast & numerische_cast).

    – Tobias Langner

    14. April ’12 um 10:59

Abgesehen davon, dass sie Hinweise sind, unsigned char * und char * nichts gemeinsam haben (EdChum hat bereits erwähnt, dass char, signed char und unsigned char sind drei verschiedene Typen). Das gleiche könnte man auch sagen Foo * und Bar * Zeigertypen auf unterschiedliche Strukturen.

static_cast bedeutet, dass ein Zeiger des Quelltyps als Zeiger des Zieltyps verwendet werden kann, was eine Untertypbeziehung erfordert. Daher kann es nicht im Kontext Ihrer Frage verwendet werden; was du brauchst ist entweder reinterpret_cast die genau das tut, was Sie wollen, oder eine Besetzung im C-Stil.

.

262800cookie-checkWarum kann ich kein static_cast zwischen char * und unsigned char * durchführen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy