Forum: Compiler & IDEs Struct-Typ erkennen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Walter T. (nicolas)


Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich habe zwei structs. Der Typ des einen structs liegt fest - ich kann 
ihn nicht ändern. Den Typ des zweiten Structs kann ich frei festlegen. 
Ich will einen Zeiger auf einen dieser beiden Structs durch den Inhalt 
unterscheidbar machen.

Im C-Quelltext also ungefähr so:
1
// Struct mit unveraenderlicher Struktur
2
typedef struct fixedstruct_s
3
{
4
    uint8_t  *a;
5
    int       someweirdcontent;
6
    uint8_t   b, c, d;
7
}
8
Fixedstruct_t;
9
10
// Struct frei waehlbarer Struktur (solange alles hineinpasst)
11
typedef struct
12
{
13
    uint8_t *null;
14
    uint32_t lala;
15
    uint16_t lili;
16
    uint16_t lulu;
17
}
18
StructOfFreeChoice_t;
19
20
typedef struct
21
{
22
    uint8_t *discriminator;
23
}
24
Genericstruct_t;
25
26
27
28
bool discriminateStructPointer(void *S)
29
{
30
    Genericstruct_t *G = (Genericstruct_t *) S;
31
    if( G->discriminator == NULL )
32
        return 1;
33
    else
34
        return 0;
35
}
36
37
38
/* Funktion zum Testen von Sachen. */
39
int main(void)
40
{
41
    uint8_t somethingToPointTo;
42
    Fixedstruct_t A = {.a = &somethingToPointTo}; // Rest nicht initialisiert
43
    StructOfFreeChoice_t B = {.null = NULL,}; // Wird bei Typ A nie vorkommen
44
45
46
    int a = discriminateStructPointer(&A);
47
    int b = discriminateStructPointer(&B);
48
    printf("typeA=%i, typeB=%i", a, b);
49
50
    return EXIT_SUCCESS;
51
}
Das Beispiel läuft.

Ich finde im C-Standard nichts zur Dereferenzierung eines void-Zeigers 
auf einen unpassenden struct-Typ. Deshalb die Frage:

 - Ist diese Methode zulässig?
 - Oder gibt es eine bessere Methode?

Viele Grüße
W.T.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Typischerweise macht man so etwas als "Tagged Union" oder "TLV"-Typ. In 
C++ gibt's std::variant genau dafür.

von Walter T. (nicolas)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Typischerweise macht man so etwas als "Tagged Union

Könnte ich beide structs ändern, machte ich es so. Das eine struct ist 
allerdings unveränderlich.

von Dr. Sommer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Walter T. schrieb:
> Das eine struct ist allerdings unveränderlich.

Dann packe es so wie es ist in die Tagged Union hinein.

von mh (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Du hast mindestens an zwei Stellen undefined behaviour.
-Du dereferenzierst einen NULL-Pointer.
-Du greifst auf ein Objekt über einen nicht zulässigen Typ zu (strict 
aliasing rule).

Ohne dein fixedstruct_t zu "verändern", wird es nicht gehen. Es gibt nen 
Grund warum C++ nen vptr braucht.

von Walter T. (nicolas)


Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Dann packe es so wie es ist in die Tagged Union hinein.

Ginge dieser einfache Weg, ginge ich ihn.

mh schrieb:
> -Du dereferenzierst einen NULL-Pointer.

Wo?

mh schrieb:
> -Du greifst auf ein Objekt über einen nicht zulässigen Typ zu (strict
> aliasing rule).

Das ist implementation defined, aber ja: Das ist der Knackpunkt. Ich 
suche eine saubere Unterscheidung. Gerne auch auf eine komplett andere 
Art und Weise.

: Bearbeitet durch User
von mh (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
> mh schrieb:
>> -Du dereferenzierst einen NULL-Pointer.
>
> Wo?
1
StructOfFreeChoice_t B = {.null = NULL,};
2
int b = discriminateStructPointer(&B);
3
bool discriminateStructPointer(void *S)
4
Genericstruct_t *G = (Genericstruct_t *) S;
5
if( G->discriminator == NULL )

Walter T. schrieb:
> mh schrieb:
>> -Du greifst auf ein Objekt über einen nicht zulässigen Typ zu (strict
>> aliasing rule).
>
> Das ist implementation defined, aber ja: Das ist der Knackpunkt. Ich
> suche eine saubere Unterscheidung.

Nein es ist undefined.

von mh (Gast)


Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
> Walter T. schrieb:
>> mh schrieb:
>>> -Du dereferenzierst einen NULL-Pointer.
>>
>> Wo?StructOfFreeChoice_t B = {.null = NULL,};
> int b = discriminateStructPointer(&B);
> bool discriminateStructPointer(void *S)
> Genericstruct_t *G = (Genericstruct_t *) S;
> if( G->discriminator == NULL )

Ok das war unsinn von mir.

von Walter T. (nicolas)


Bewertung
0 lesenswert
nicht lesenswert
So, ich habe es. Nicht schön, aber sollte gehen: Ich kann mir magische 
Zahlen für den Inhalt von "Fixedstruct_t" ausdenken, die bei einem 
echten Struct nie vorkommen könnten. Wenn ich mein zweit-Struct 
entsprechend konstruiere, dass jeder Datentyp gleich ist, sollte auch 
keine Alias-Regel verletzt werden.
1
// Struct mit unveraenderlicher Struktur
2
typedef struct fixedstruct_s
3
{
4
    uint8_t  *a;
5
    int       someweirdcontent;
6
    uint8_t   b, c, d;
7
}
8
Fixedstruct_t;
9
10
typedef struct
11
{
12
    uint32_t lala;
13
    uint16_t lili;
14
    uint16_t lulu;
15
}
16
Payload_t;
17
18
// Gleiche Structur wie Fixedstruct_t
19
typedef struct
20
{
21
    void *Payload;
22
    int       magic0;
23
    uint8_t   magic1, magic2, magic3;
24
}
25
Piggyback_t;
26
27
28
typedef union
29
{
30
    Fixedstruct_t Fixed;
31
    Piggyback_t Piggyback;
32
}
33
Genericunion_t;
34
35
//static_assert( sizeof(Fixedstruct_t) == sizeof(Piggyback_t) );
36
//static_assert( sizeof(Fixedstruct_t) == sizeof(Genericunion_t) );
37
38
enum
39
{
40
    magic0 = INT_MIN,
41
    magic1 = 0xFF,
42
    magic2 = 0xFF,
43
    magic3 = 0xFF,
44
};
45
46
47
bool discriminateStructPointer(void *S)
48
{
49
    Genericunion_t *G = (Genericunion_t *) S;
50
    Piggyback_t *P = &(G->Piggyback);
51
    if( P->magic0 == magic0 &&
52
       P->magic1 == magic1 &&
53
       P->magic2 == magic2 &&
54
       P->magic3 == magic3 )
55
        return 1;
56
    else
57
        return 0;
58
}
59
60
61
int main(void)
62
{
63
    assert( sizeof(Fixedstruct_t) >= sizeof(Piggyback_t) );
64
    assert( sizeof(Fixedstruct_t) == sizeof(Genericunion_t) );
65
66
    uint8_t somethingToPointTo;
67
    Payload_t Pl;
68
    Fixedstruct_t A = {.a = &somethingToPointTo}; // Rest nicht initialisiert
69
    Genericunion_t B = {.Piggyback =
70
        {
71
            .Payload = &Pl,
72
            .magic0 = magic0,
73
            .magic1 = magic1,
74
            .magic2 = magic2,
75
            .magic3 = magic3,
76
        }}; // Wird bei Typ A nie vorkommen
77
78
79
    int a = discriminateStructPointer(&A);
80
    int b = discriminateStructPointer(&B);
81
    printf("typeA=%i, typeB=%i", a, b);
82
83
84
    return EXIT_SUCCESS;
85
}

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht lesenswert
Deine "Lösung" mit Magics gefällt mir überhaupt nicht.

Warum übergibst Du Deiner Funktion discriminateStructPointer(void *S) 
nicht einfach einen 2. Parameter is_fixed, in welchem notiert ist, um 
welche Struct es sich handelt? Die aufrufende Funktion weiß das doch!

Wenn diese das jedoch auch nicht weiß, weil Du den Pointer über mehrere 
Funktionsebenen heruntergibst, dann machs so:
1
typedef struct
2
{
3
    union
4
    {
5
        Fixedstruct_t Fixed;
6
        Piggyback_t Piggyback;
7
    } u;
8
    int is_fixed;
9
}
10
Genericstruct_t;

Dann kannt Du die Information, um welche struct es sich handelt, direkt 
in den Daten mitschleppen. Das funktioniert auch noch, wenn Du später 
die eine oder andere Structs erweiterst und Deine Magics dadurch auch 
mal fehlinterpretiert werden könnten.

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Bewertung
0 lesenswert
nicht lesenswert
Frank M. schrieb:
> Die aufrufende Funktion weiß das doch!

Leider nein. Sie ist nicht von mir und kennt nur Fixedstruct_t.

von Bernd K. (prof7bit)


Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
> -Du greifst auf ein Objekt über einen nicht zulässigen Typ zu (strict
> aliasing rule).

Ja, das gute alte strict aliasing. Zum Glück kann man diese unnütze 
Elfenbeinturm-Regel bei allen gängigen Compilern ausschalten, dann 
erzeugt er den Code den man hinschreibt, so wie es eigentlich von Anfang 
an hätte sein sollen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.