Forum: Compiler & IDEs gcc Conversion-Warnung vermeiden bei Struct-Member mit Größenangabe in Bits


von FOp (Gast)


Lesenswert?

Bei Strukturen in C kann ich ja die Größe von Mitgliedern auf eine 
konkrete Anzahl Bits einschränken.
1
struct {
2
unsigned int genau : 1;
3
unsigned int mitjein : 2;
4
} antwort;

Wie weise ich jetzt so einem Mitglied der Struktur den Inhalt einer 
Variable zu, ohne dass der gcc beim Compilieren schon warnt, dass 8 Bits 
nicht in 3 passen (may change value [-Wconversion]) ?

Dass der Wert in der Variable klein genug ist, wird vorher im Programm 
schon sicher gestellt.

Ich will auch nicht die Warnungen für problematische Zuweisungen ganz 
ausschalten. In anderen Fällen hilft ja ein expliziter Typecast, um dem 
Compiler zu sagen : ich weiß, was ich hier tue.

von foobar (Gast)


Lesenswert?

Probier das mal:
1
   antwort.genau = x & 1u;
2
   antwort.mitjein = x & 3u;

Generell: wenn du nicht wirklich Speichersorgen hast, vermeide 
Bitfields.

von Markus F. (mfro)


Lesenswert?

foobar schrieb:
> Generell: wenn du nicht wirklich Speichersorgen hast, vermeide
> Bitfields.

Nein.
Nutze Bitfelder, wenn sie Sinn machen.
Vermeide sie, wenn nicht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Beispiel-Code:
1
typedef struct
2
{
3
    unsigned a : 1;
4
    unsigned b : 2;
5
} S;
6
7
S func1 (S s, unsigned x)
8
{
9
    s.b = x;
10
    return s;
11
}

1. Was nicht funktioniert: Den Wert mit einer entsprechenden Maske zu 
verUNDen löst das Problem nicht, die Warnung bleibt.
1
    x &= 0b11;

2. Was nicht funktioniert: Dem Compiler mitteilen, das die Wert in den 
Grenzen liegt. Aber die Warnung bleibt:
1
    if (x > 0b11)
2
        __builtin_unreachable();
oder
1
    if (x > 0b11)
2
        __builtin_unreachable();
3
    else
4
        s.b = x;

3. Was nicht funktioniert: Cast auf den Typ von s.b mit 
"__typeof__(s.b)".  Ergibt einen Fehler vom Compiler weil das Argument 
von typeof ein Bitfeld ist.


Das Problem mit -Wconversion scheint schon recht alt zu sein:

https://gcc.gnu.org/PR39170

was 2009 gegen v4.3 reportiert wurde; aber v8 und master (v13) zeigen 
immer noch das gleiche Verhalten. Und der PR hat Status "assigned", 
nicht etwa "resolved" oder "fixed".

Stattdessen wird -Wtraditional-conversion vorgeschlagen, was jedoch

>> -Wtraditional-conversion
>>     Warn of prototypes causing type conversions different
>>     from what would happen in the absence of prototype.

ist also was anderes...

: Bearbeitet durch User
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

struct {
unit8_t
 genau   : 1,
 mitjein : 2,
         : 5;
}  __attribute__((_packed_)) antwort_t;

antwort_t anwort;

... so gehts bei avr gcc, klar der type kann auch einzeln wiederholt
werden und dann wie bekannt antwort.genau.

du kannst auch auf unit16_t/uint32_t mappen

e.g. dann
#define ANTWORT_FLAG(genau) (*(antwort_t*) &antwort).b

also cast ptr auf type struct

: Bearbeitet durch User
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

#define ANTWORT_FLAG(b) (*(antwort_t*) &antwort).b
und dann e.g. ANTWORT_FLAG(genau)

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Apollo M. schrieb:
> [...]

Es geht um GCC-Warnings bei Zuweisungen und -Wconversion.  Und nicht 
darum, wie man Strict-Aliasing Regeln verletzt.

von Klaus H. (klummel69)


Lesenswert?


von Peter D. (peda)


Lesenswert?

FOp schrieb:
> Bei Strukturen in C kann ich ja die Größe von Mitgliedern auf eine
> konkrete Anzahl Bits einschränken.

Besser nicht.
Wenn Du so eine verquere Struct brauchst, z.B. für einen IC oder ein 
Protokoll, dann schreibe besser eine Funktion, die Dir das erst für die 
Ausgabe so hinbastelt bzw. eine Eingabe wieder in einzelne Variablen 
aufdröselt.
Das spart dann auch Code und Rechenzeit, wenn der Compiler sich nicht 
bei jedem Rechenschritt extra verrenken muß (AND, OR, SHIFT usw).

von FOp (Gast)


Lesenswert?

Danke für die ganzen Antworten.

Was ich in der Tat nicht gesucht habe sind Lösungen, die darauf 
vertrauen, wie der Compiler etwas umsetzt, z.B. Typecasts über Zeiger. 
Wenn das ganze MISRA konform bleiben muss sind ja schon Unions verpönt, 
wenn sie ausnutzen, wie der Compiler die Daten im Speicher anordnet.

Das mit dem & 1 hatte ich auch schon ausprobiert. Es führt beim 
arm-none-eabi-gcc (GNU Tools for STM32 10.3-2021.10.20211105-1100) 
10.3.1 20210824 (release) wenn er für einen cortex-m4 compiliert dazu, 
dass wirklich ein zusätzlicher Assembler Befehl eingebaut wird. Ok, 
vermutlich kaum nennenswert im Vergleich zu dem Zusatzaufwand die Bits 
zusammen und wieder auseinander zu pfriemeln.

Die Pragmas helfen natürlich auch, wenn man sich sicher ist, warum die 
Warnungen kommen. Ich hatte erwartet, dass es da auch was im C-Standard 
gibt, um auszudrücken, was der Programmierer will. Für so abgespaced 
hatte ich die Vorgehensweise nicht gehalten, dass nicht schon einige vor 
mir darauf gestossen wären.

So lange es beim gcc bleibt, sind keine Änderungen an den Pragmas 
fällig. Sobald das zum ersten Mal auf einen Compiler portiert werden 
soll, der die Pragmas nicht kennt, kommen die & rein.

von Rolf M. (rmagnus)


Lesenswert?

FOp schrieb:
> Danke für die ganzen Antworten.
>
> Was ich in der Tat nicht gesucht habe sind Lösungen, die darauf
> vertrauen, wie der Compiler etwas umsetzt, z.B. Typecasts über Zeiger.

Dann sind Bitfelder aber das falsche, denn die sind abhängig davon, wie 
der Compiler sie umsetzt. Gerade deshalb sollte man sie dafür nicht 
verwenden.

von FOp (Gast)


Lesenswert?

Rolf M. schrieb:
> Dann sind Bitfelder aber das falsche, denn die sind abhängig davon, wie
> der Compiler sie umsetzt. Gerade deshalb sollte man sie dafür nicht
> verwenden.

Äh nee. Compilerabhängig wird es erst, wenn Du einerseits normal darauf 
zugreifst und andererseits wilde Typecasts, gar noch über Pointer 
machst, Unions oder andere Tricks verwendest, damit sie sich mit einem 
SFR oder einer Variable eines anderen Typs den Speicherplatz teilen, und 
dann darauf zugreifst. Wenn Du sie auf dem vorgesehenen Weg befüllst und 
wieder ausliest ist alles in Butter. Oder es ist zumindest sehr 
schwierig zu scheitern, denn der Compiler weiß ja, was der Compiler tut.

von Rolf M. (rmagnus)


Lesenswert?

FOp schrieb:
> Wenn Du sie auf dem vorgesehenen Weg befüllst und wieder ausliest ist
> alles in Butter.

Ah, ok. Ich dachte, du willst die befüllen, um die Daten in irgendwelche 
Hardware-Register zu stecken oder irgendein Protokoll umzusetzen. Wenn 
du die tatsächlich nur einfach in die Struktur schreibst und später 
wieder ausliest, dann ist es natürlich kein Problem. Dann ergibt sich 
aber tatsächlich die Frage, ob dein Speicher wirklich so knapp ist, dass 
du darauf zurückgreifen musst.

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.