Forum: Compiler & IDEs überprüfen ob ein struct mit einsen beschrieben ist


von Peter (Gast)


Lesenswert?

Hi,
ich habe einen struct erstellt.
Ist es möglich, diesen einfach abzufragen, ob einige inhalte mit einer 
eins beschrieben sind?
1
typedef struct{
2
  unsigned error_1 : 1;
3
  unsigned error_2 : 1;
4
  unsigned error_3 : 1;
5
  unsigned error_4 : 1;
6
  unsigned error_5 : 1;
7
} s_Fehler;
8
9
s_Fehler Fehler;

bei einem bestimmten Fehler beschreibe ich das Feld mit einer eins. 
Jetzt möchte ich im prinzip nicht immer alle durchgehen, sondern nur, 
wenn wirklcih ein fehler vorliegt.
Im prinzip möchte ich so etwas wie
1
if(Fehler){
2
  if(Fehler.error_1){
3
  ....
4
  }
5
  ..usw.
6
}

bekomme aber dabei die Fehlermeldung, dass ein scalar erfordert ist, 
aber ein struct typ gegeben ist.

Peter

: Verschoben durch User
von tux (Gast)


Lesenswert?

Stichwort: union

von Ralf G. (ralg)


Lesenswert?

Peter schrieb:
> ich habe einen struct erstellt.

Falls die Reaktion nicht ist: "Mein Ausbilder hat aber gesagt, das muss 
so sein!", dann würde ich sagen, der Ansatz ist Mist. Wozu brauchst du 
hier ein Bitfeld?

von Martin (Gast)


Lesenswert?

If(Fehler) wird nicht funkionieren, da "Fehler" kein Scalar ist

von Cyblord -. (cyblord)


Lesenswert?

Martin schrieb:
> If(Fehler) wird nicht funkionieren, da "Fehler" kein Scalar ist

Ach. Genau DAS ist das Problem des TE. Schlaubischlumpf.

Aber Ralf hat recht, der struct muss da weg. Ein reines Bitfeld ist in 
einer normalen Variable besser aufgehoben.

von Ulrich F. (Gast)


Lesenswert?

Das Stichwort union wurde schon genannt!
Damit die sowohl die Anforderung nach einem Bitfeld und auch nach einem 
Skalar erfüllbar.

von Programmierer (Gast)


Lesenswert?

Ulrich F. schrieb:
> Damit die sowohl die Anforderung nach einem Bitfeld und auch nach einem
> Skalar erfüllbar.
Leider aber nicht mehr die nach korrektem C Code, denn nach Beschreiben 
des Bitfields darf man den anderen Member des union nicht auslesen bzw. 
man erhält ein undefiniertes Ergebnis. union darf halt nur zum Speicher 
sparen verwendet werden, nicht zum Konvertieren.
Lösung: Bitmasken mit Konstanten verwenden statt Bitfields

von Ulrich F. (Gast)


Lesenswert?

Programmierer schrieb:
> union darf halt nur zum Speicher
> sparen verwendet werden, nicht zum Konvertieren.

Häää.....
Ob man denselben Speicherplatz als Sammlung von 8Bit betrachtet, oder 
als Byte ....
Was soll da anbrennen?
Der Kompiler nimmts ohne Murren, und tut das richtige.

von Programmierer (Gast)


Lesenswert?

Ulrich F. schrieb:
> äää.....
> Ob man denselben Speicherplatz als Sammlung von 8Bit betrachtet, oder
> als Byte ....
> Was soll da anbrennen?
Wer sagt dass char 8 Bits sind? Was ist mit dem obersten Bit bei signed 
Typen? Was ist mit Padding? Was mit Byte-Reihenfolge bei Typen mit mehr 
als 1 Byte? C lässt das mit Recht komplett offen (undefiniert) , d.h. es 
hängt komplett vom Compiler/Plattform ab.
> Der Kompiler nimmts ohne Murren, und tut das richtige.

Na und, nur weil das bei deiner Compiler Version und deinem OS und 
deinem Prozessor funktioniert, heißt das noch lange nicht dass es 
"richtig" ist.

von tux (Gast)


Lesenswert?

Programmierer schrieb:
> Wer sagt dass char 8 Bits sind?
>
wer hat wo hier geschrieben, dass mit einem Typ char das Bitfeld 
überlagert werden soll?

Programmierer schrieb:
> Was ist mit dem obersten Bit bei signed
> Typen? Was ist mit Padding?
>
sollte bei der oben skizzierten Aufgabenstellung reichlich uninteressant 
sein (vorausgesetzt, dass 8 Ein-Bit-Variablen definiert werden/gesetzt 
werden).

von Der Andere (Gast)


Lesenswert?

Dann schu dir mal das an:
"if(Fehler){"

was willst du damit ausdrücken?
Welche Typen sind in der if Bedingung erlaubt? Das sagt dir dein C Buch.

von Steffen R. (steffen_rose)


Lesenswert?

Programmierer schrieb:
> union darf halt nur zum Speicher
> sparen verwendet werden, nicht zum Konvertieren.

Ernsthaft?
Gerade bei registern ist es doch üblich. Wüßte nicht, dass es 
undefiniert ist. Undefiniert ist, wie ein bestimmter Compiler das 
Bitfeld anordnet. Insofern ist es nicht portabel.

Programmierer schrieb:
> C lässt das mit Recht komplett offen (undefiniert) , d.h. es
> hängt komplett vom Compiler/Plattform ab.

Ja, im Standard ist es (ggf.) nicht definiert. In den 
Compilerbeschreibungen taucht es regelmäßig auf. Insofern hat es auf dem 
jeweiligen System ein definiertes Verhalten. Undefiniertes Verhalten ist 
es aus meiner Sicht, wenn es auch im Compilerhandbuch nicht beschrieben 
ist und man sich auf das momentane Verhalten verläßt, was sich aber 
ändern kann.

Bitfelder sind generell nicht portabel.

von Programmierer (Gast)


Lesenswert?

Steffen R. schrieb:
> Ernsthaft?
> Gerade bei registern ist es doch üblich.
Ja. Davon wird es noch lange nicht korrekt. Da allerdings Register 
Definitionen sowieso plattformabhängig sind, macht es nichts dass es, 
abhängig von der Plattform, eben doch funktioniert.

> Wüßte nicht, dass es
> undefiniert ist. Undefiniert ist, wie ein bestimmter Compiler das
> Bitfeld anordnet. Insofern ist es nicht portabel.
Undefiniert ist der Zugriff auf "falsche" Member des union.

Steffen R. schrieb:
> Undefiniertes Verhalten ist es aus meiner Sicht, wenn es auch im 
Compilerhandbuch
> nicht beschrieben ist und man sich auf das momentane Verhalten verläßt,
> was sich aber ändern kann.

Schön dass das deine Sicht ist, aber die Sicht des C Standards ist dass 
alles undefiniert ist, was im Standort nicht definiert ist. Und der 
Zugriff auf "falsche" union Member ist undefiniert. Normalerweise will 
man ja auch auf Korrektheit und Portabilität abzielen, und nicht dass es 
ein bestimmter Compiler kann.

Steffen R. schrieb:
> Bitfelder sind generell nicht portabel.
Doch, nur das Mapping auf Bytes - und damit anderer union Member - 
nicht.

von Steffen R. (steffen_rose)


Lesenswert?

Programmierer schrieb:
>> Wüßte nicht, dass es
>> undefiniert ist. Undefiniert ist, wie ein bestimmter Compiler das
>> Bitfeld anordnet. Insofern ist es nicht portabel.
> Undefiniert ist der Zugriff auf "falsche" Member des union.

Kann ich nicht im Standard finden. Kannst Du hier etwas nachhelfen?

Ich finde nur, dass speziell die Anordnung der Bitfelder 
Compilerspezifisch ist.

"The order of allocation of bit-fields within a unit (high-order to
low-order or low-order to high-order) is implementation-defined."

von Ralf G. (ralg)


Lesenswert?

Ulrich F. schrieb:
> Häää.....
> Ob man denselben Speicherplatz als Sammlung von 8Bit betrachtet, oder
> als Byte ....
> Was soll da anbrennen?
> Der Kompiler nimmts ohne Murren, und tut das richtige.

Da kann schon was anbrennen! Aber: in diesem Fall sollte es astrein 
sein, da ja nur mal geprüft werden soll, ob irgendwo ein Bit gesetzt 
ist. Falls der Compiler umsortiert oder irgendwas erweitert, sollte 
eigentlich in der union immer noch '0' stehen, wenn kein Bit gesetzt 
ist.
1
if (Fehler.AlleBits)
2
{
3
  if (Fehler.error_1)
4
  {
5
    // ...
6
  }
7
  // ...
8
}
(Wobei ich's einfacher ohne Bitfeld finde.)

von Karl H. (kbuchegg)


Lesenswert?

Steffen R. schrieb:
> Programmierer schrieb:
>>> Wüßte nicht, dass es
>>> undefiniert ist. Undefiniert ist, wie ein bestimmter Compiler das
>>> Bitfeld anordnet. Insofern ist es nicht portabel.
>> Undefiniert ist der Zugriff auf "falsche" Member des union.
>
> Kann ich nicht im Standard finden. Kannst Du hier etwas nachhelfen?

Es ist tatsächlich undefiniert.
Der C Standard kann so etwas nicht definieren, weil er auch nicht 
definiert,in welcher Endianess abgespeichert werden muss, bzw. ob 2-er 
Komplement benutzt werden muss oder nicht. Der Standard kann auch nicht 
definieren, was passieren soll, wenn du in einer union einen long mit 
einem double übereinander legst, an den long zuweist und vom double 
ausliest.
Aus diesem und ähnlichen Gründen KANN der C Standard hier nichts 
definieren.

Aber es gibt einen legalen Ausweg.
Auch wenn es undefiniertes Verhalten nach sich zieht, wenn man in einer 
union Dinge übereinander lagert, so gibt es eine legale Ausnahme:
Man darf unsigned char darüber legen.
Hier wurde eine der üblichen Vorgehensweisen legitimiert, die seit 
Anbeginn der C Welt funktionieren, indem man in einer union einen 
beliebigen Datentyp mit einem Array-of-unsigned_char überlagert um so an 
die Bytes der anderen Variablen zu kommen.

: Bearbeitet durch User
von Steffen R. (steffen_rose)


Lesenswert?

Karl H. schrieb:
> Steffen R. schrieb:
>> Programmierer schrieb:
>>>> Wüßte nicht, dass es
>>>> undefiniert ist. Undefiniert ist, wie ein bestimmter Compiler das
>>>> Bitfeld anordnet. Insofern ist es nicht portabel.
>>> Undefiniert ist der Zugriff auf "falsche" Member des union.
>>
>> Kann ich nicht im Standard finden. Kannst Du hier etwas nachhelfen?
>
> Es ist tatsächlich undefiniert.

Der Standard unterscheidet zwischen undefiniert und 
Implementationsabhängig, was ich mit Compilerabhängig meine.

> Aus diesem und ähnlichen Gründen KANN der C Standard hier nichts
> definieren.

Die Ablage der Daten im Speicher in 'implementation-defined'.
Daher schließe ich daraus (fälschlicherweise?), dass ich bei einem 
Auslesen über einen anderen Member die Daten 'implementation-defined' 
bekomme.

> Aber es gibt einen legalen Ausweg.
> Auch wenn es undefiniertes Verhalten nach sich zieht, wenn man in einer
> union Dinge übereinander lagert, so gibt es eine legale Ausnahme:
> Man darf unsigned char darüber legen.
> Hier wurde eine der üblichen Vorgehensweisen legitimiert, die seit
> Anbeginn der C Welt funktionieren, indem man in einer union einen
> beliebigen Datentyp mit einem Array-of-unsigned_char überlagert um so an
> die Bytes der anderen Variablen zu kommen.

OK. Es ist somit erlaubt wurden auf diese Weise die 
'implementation-defined' abgelegten Daten auszulesen?
In deiner Beschreibung scheint mir das Array of char nicht Teil der 
union zu sein.

von Ulrich F. (Gast)


Lesenswert?

Nach nochmaligem aufmerksamen lesen der Aufgabenstellung, finde ich nix 
mit "Portabel".
Also ist alles, was mit "implementation-defined" zu tun hat irrelevant.

von Karl H. (kbuchegg)


Lesenswert?

Steffen R. schrieb:

> OK. Es ist somit erlaubt wurden auf diese Weise die
> 'implementation-defined' abgelegten Daten auszulesen?
> In deiner Beschreibung scheint mir das Array of char nicht Teil der
> union zu sein.
1
union
2
{
3
  long data;
4
  unsigned char bytes[sizeof(long)];
5
};

ist offiziell erlaubt.
Derartige unions werden verwendet, seit es C gibt. Da kein Compiler 
bekannt war oder ist, bei dem eine derartige union nicht das erwartete 
Verhalten zeigt(e) und es auch keinen vernünftigen Grund gibt, warum 
sich das auch in Zukunft nicht korrekt einrichten lässt, wurde es 
offiziell zugelassen.

von Steffen R. (steffen_rose)


Lesenswert?

Danke

von Ralf G. (ralg)


Lesenswert?

Karl H. schrieb:
> bei dem eine derartige union nicht das erwartete Verhalten zeigt(e)

Bedeutet das jetzt, dass das 'erwartete Varhalten' auf verschiedenen 
Maschinen unterschiedlich sein kann?

long-Wert   -> -123456789
uchar-Array -> 0xF8A832EB
bzw.
            -> 0xEB32A8F8

von Ulrich F. (Gast)


Lesenswert?

Ralf G. schrieb:
> Bedeutet das jetzt, dass das 'erwartete Varhalten' auf verschiedenen
> Maschinen unterschiedlich sein kann?
Ja!

Beispiel:
Programmierer schrieb:
> Was mit Byte-Reihenfolge bei Typen mit mehr
> als 1 Byte?
Bei long ist diese u.U. unterschiedlich.



Solche Strukturen sind ohne besondere Vorkehrungen nicht portabel.

von Ralf G. (ralg)


Lesenswert?

Ulrich F. schrieb:
> Ja!

Dann ist ja gut! :) Ich dachte, ich hätte mal wieder was verpasst...

So dachte ich mir das auch.
Ralf G. schrieb:
> long-Wert   -> -123456789
> uchar-Array -> 0xF8A832EB
> bzw.
>             -> 0xEB32A8F8

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Programmierer schrieb:
> Leider aber nicht mehr die nach korrektem C Code, denn nach Beschreiben
> des Bitfields darf man den anderen Member des union nicht auslesen bzw.
> man erhält ein undefiniertes Ergebnis.

Die Unportabilität eines solchen Konstrukts hält sich in ziemlich
engen Grenzen:
1
union error {
2
  struct {
3
    unsigned char e1: 1;
4
    unsigned char e2: 1;
5
    unsigned char e3: 1;
6
  } bits;
7
  unsigned char byte;
8
};
9
10
// …
11
12
   union error e;
13
   e.byte = 0;
14
15
   // …
16
   if (something) {
17
      e.bits.e1 = 1;
18
   }
19
   // …
20
   if (e.byte != 0) {
21
      handle_error(e);
22
   }

Der Standard spezifiziert, dass aufeinanderfolgende Bitfields auch
hintereinander platziert werden müssen.  Padding darf es nur geben,
wenn die gewünschte Bitanzahl nicht mehr in die aktuell adressierbare
Einheit passt, was bei einem Bitfield der Größe 1 nie der Fall sein
kann.  Padding darf es am Ende geben (und gibt es hier in Form der
5 nicht deklarierten Bits), aber wenn man initial das ganze Byte
ausnullt, dann können die Padding-Bits später nicht magisch eine 1
rein bekommen.

Die Unportabilität beschränkt sich daher darauf, dass die 0 auch
als Bitmuster 0b00000000 implementiert sein muss.  Ich kann mich
nicht dran erinnern, je etwas von einer Maschine gehört zu haben,
auf diese Bedingung nicht zuträfe.

von Tom (Gast)


Lesenswert?

Jörg W. schrieb:
> dann können die Padding-Bits später nicht magisch eine 1
> rein bekommen.

Dürfte der Compiler laut Standard ein
1
uint8_t foo;
2
// ...
3
e.bits.e1 = (foo >> 0);
4
e.bits.e2 = (foo >> 1);
5
e.bits.e3 = (foo >> 2);
durch ein einfaches
1
memcpy(&e, &foo, 1);
bzw. dessen ASM-Äquivalent ersetzen?
Das macht natürlich kein Compiler, weil dann ca. 154 Milliarden 
Programme nicht mehr liefen, aber dürfte er?


Die Union-Lösung würde ich irgendwo in einer "Dinge, deren 
Implementierung im Compiler bei Portierung (inkl. Compilerwechsel) zu 
überprüfen sind"-Liste dokumentieren.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tom schrieb:
> Dürfte der Compiler laut Standard ein

So gut bin ich im Interpretieren des Standards nun auch nicht. ;-)

Aus dem hier:
1
union error {
2
  struct {
3
    unsigned char e1: 1;
4
    unsigned char e2: 1;
5
    unsigned char e3: 1;
6
  } bits;
7
  unsigned char byte;
8
};
9
10
unsigned char bar(unsigned char foo)
11
{
12
  union error e;
13
14
  e.bits.e1 = (foo >> 0);
15
  e.bits.e2 = (foo >> 1);
16
  e.bits.e3 = (foo >> 2);
17
18
  return e.byte;
19
}

macht mein Clang jedenfalls (mal im 32-bit-Modus):
1
        .file   "bitfield.c"
2
        .text
3
        .globl  bar
4
        .type   bar,@function
5
bar:                                    # @bar
6
# BB#0:
7
        pushl   %ebp
8
        movl    %esp, %ebp
9
        movzbl  8(%ebp), %eax
10
        andl    $7, %eax
11
        popl    %ebp
12
        ret
13
.Ltmp0:
14
        .size   bar, .Ltmp0-bar
15
16
17
        .ident  "FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512"
18
        .section        ".note.GNU-stack","",@progbits

GCC 4.8 sieht das genauso:
1
        .file   "bitfield.c"
2
        .text
3
        .globl  bar
4
        .type   bar, @function
5
bar:
6
.LFB0:
7
        .cfi_startproc
8
        pushl   %ebp
9
        .cfi_def_cfa_offset 8
10
        .cfi_offset 5, -8
11
        movl    %esp, %ebp
12
        .cfi_def_cfa_register 5
13
        movb    8(%ebp), %al
14
        popl    %ebp
15
        .cfi_restore 5
16
        .cfi_def_cfa 4, 4
17
        andl    $7, %eax
18
        ret
19
        .cfi_endproc
20
.LFE0:
21
        .size   bar, .-bar
22
        .ident  "GCC: (FreeBSD Ports Collection) 4.8.3"
23
        .section        .note.GNU-stack,"",@progbits

d. h. im Prinzip das, was du gefragt hast, nur dass sie auf Nummer
sicher gehen und die oberen 5 Bits ausmaskieren.  Müssten sie aber
nicht, denn der Code hat die lokale Variable e ja nicht initialisiert.

AVR-GCC (4.8.3) hingegen befummelt die Bits wirklich einzeln.

: Bearbeitet durch Moderator
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.