Forum: Compiler & IDEs Struct-Typ erkennen


von Walter T. (nicolas)


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)


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)


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)


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)


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)


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)


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)


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)


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


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)


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)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.