Struct-Typ erkennen

#5344705
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.
Gast #5344735
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.
#5344754
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.
Gast #5344774
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.
Gast #5344787
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.
#5345147
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
}
Moderator Persönliche Seite #5345246
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.
#5345562
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren