Ist es sicher, einen Zeiger in typisierte / Größe enum in einen Zeiger auf den zugrunde liegenden Typ zu konvertieren?

8

Der folgende Code:

%Vor%

Erzeugt den folgenden Fehler:

%Vor%

Das ist überraschend für mich, weil ich dachte, dass die : uint8_t in der enum Definition bedeuten würde, dass sie notwendigerweise mit diesem zugrunde liegenden Typ dargestellt werden. Ich kann das mit einem Cast leicht umgehen:

%Vor%

was mich nicht zu sehr stört, aber da es ein Fehler ist, es wegzulassen, ist das immer sicher oder liefert die : uint8_t nicht die Garantien, die ich denke, dass es ist?

    
Luis 22.05.2017, 21:39
quelle

2 Antworten

5

Es ist in der Tat sicher (obwohl ich kein Sprachanwalt bin): Was im Speicher gespeichert ist, ist uint8_t , und darauf zielen Sie hin. Wenn f() jedoch einen Zeiger auf eine nicht konstante uint8_t verwenden soll, kann der Wert möglicherweise in einen Wert geändert werden, der nicht explizit als einer der E enum-Werte definiert ist. (Bearbeiten:) Obwohl dies vom C ++ - Standard scheinbar erlaubt ist, ist es für viele überraschend (siehe die Diskussion in den Kommentaren zu diesem Punkt), und ich würde Sie ermutigen, dafür zu sorgen, dass dies nicht geschieht.

... aber wie andere vorschlagen, erhalten Sie den Fehler nicht wegen Ihrer Sicherheitsvorstellung, sondern weil implizite Konvertierungen zwischen spitzen Typen nicht durchgeführt werden. Sie können eine E an eine Funktion übergeben, die eine uint8_t , aber keine E * an eine Funktion mit uint8_t * ; das wäre - nach Meinung des Sprachkomitees und meiner Meinung nach - eine allzu rücksichtslose Haltung gegenüber den Zeigertypen.

    
einpoklum 22.05.2017, 22:07
quelle
3

Afaik das ist nur durch Zufall erlaubt:

Was Sie tun, ist eine reinterpret_cast durchzuführen und ich nehme an, dass f diesen Zeiger intern dereferenziert. Dies ist nur in einer sehr eingeschränkten Menge von Fällen legal und obwohl nicht normativ, cppreference.com gibt ein gutes Überblick über diese:

  

Wenn ein Zeiger oder eine Referenz auf ein Objekt, dessen dynamischer Typ DynamicType ist, in einen Zeiger oder eine Referenz auf ein Objekt eines anderen Typs AliasedType reinterpret_cast (oder C-artige Umwandlung) uminterpretiert wird, ist die Umwandlung immer erfolgreich, aber der resultierende Zeiger oder Verweis kann nur Wird verwendet, um auf das Objekt zuzugreifen, wenn eine der folgenden Bedingungen zutrifft:

     
  • AliasedType ist (möglicherweise cv-qualifiziert) DynamicType

  •   
  • AliasedType und DynamicType sind beide (möglicherweise mehrere Ebenen, möglicherweise cv-qualifiziert auf jeder Ebene) Zeiger auf den gleichen Typ T (seit C ++ 11)

  •   
  • AliasedType ist die (möglicherweise cv-qualifizierte) signierte oder unsignierte Variante von DynamicType

  •   
  • AliasedType ist ein Aggregat-Typ oder ein Union-Typ, der einen der oben genannten Typen als Element oder nicht-statisches Member enthält (einschließlich rekursiv Elemente von Unteraggregaten und nicht-statische Datenmitglieder der enthaltenen Vereinigungen): Dies macht es möglich, einen verwendbaren Zeiger auf eine Struktur oder Vereinigung zu erhalten, wenn ein Zeiger auf sein nicht-statisches Element oder Element gegeben wird.

  •   
  • AliasedType ist eine (möglicherweise cv-qualifizierte) Basisklasse von DynamicType und DynamicType ist eine Standardlayout-Klasse, die keine nicht statischen Datenelemente hat und AliasedType ist ihre erste Basisklasse.

  •   
  • AliasedType ist char, unsigned char oder std :: byte: Dies ermöglicht die Untersuchung der Objektrepräsentation jedes Objekts als ein Array von Bytes.

  •   

Wenn AliasedType diese Anforderungen nicht erfüllt, ruft das Zugreifen auf das Objekt über den neuen Zeiger oder die Referenz ein nicht definiertes Verhalten auf. Dies wird als strenge Aliasing-Regel bezeichnet und gilt für C ++ - und C-Programmiersprachen.

In keinem dieser Fälle wird ein Zeiger auf den zugrunde liegenden Typ eines Enums übergeben!

Allerdings:
Das Umwandeln in einen Zeiger auf unsigned char* und Dereferenzieren ist immer legal und auf den meisten Plattformen ist uint8_t nur ein typedef dafür. So ist es in diesem Fall in Ordnung, wäre aber nicht, wenn der zugrunde liegende Typ z.B. wäre ein uint16_t .

Davon abgesehen wäre ich nicht überrascht zu hören, dass die meisten Compiler eine solche Verwendung zulassen würden, selbst wenn der Standard dies nicht tut.

    
MikeMB 23.05.2017 05:49
quelle