Forum: Compiler & IDEs Bitfelder: Zuweisung so zullaessig?


von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Hallo,

ich habe eine Frage zu Bitfeldern und der Zuweisung derer.
1
??=include <stdint.h>
2
3
typedef struct _BITFIELD
4
{
5
  uint8_t eins : 1;
6
  uint8_t zwei : 1;
7
  uint8_t drei : 1;
8
  uint8_t vier : 1;
9
  uint8_t fuenf : 1;
10
  uint8_t sechs : 1;
11
}BITFIELD;
12
13
int main(void)
14
{
15
  BITFIELD feld1, feld2;
16
17
  feld1.eins = 0;
18
  feld1.zwei = 1;
19
  feld1.drei = 1;
20
  feld1.vier = 0;
21
  feld1.fuenf = 1;
22
  feld1.sechs = 0;
23
24
  feld2 = feld1; // <-- ist das zulaessig?
25
26
  return 0;
27
}
Der Kommentar sagt es schon:
Ist diese zuweisung zulaessig?
Ok, der Compiler mekert nicht und es funktioniert, aber das heisst ja 
noch lange nicht, dass das auch zulaessig ist.
Was sagt der C-Standard dazu?
Gibt es bei dieser Zuweisung irgendwelche gefaehrlichen Fallstricke?

Compiler: GCC 4.8.3 (-std=gnu99 -trigraphs -Wno-trigraphs -Wall -Wextra)

Gruesse

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Kaj G. schrieb:
> Ist diese zuweisung zulaessig?

ANSI-C kennt Strukturzuweisungen. Das mochte nur das alte K&R-C (also 
das C vor C89) nicht.

Da ein Bitfeld eine Struktur ist, ist eine Zuweisung davon also 
vollkommen in Ordnung.

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


Lesenswert?

Kaj G. schrieb:
> struct _BITFIELD

Das ist allerdings nicht zulässig:

<Leierkasten>
Namen, die mit einem Unterstrich beginnen, gefolgt von einem Unterstrich
oder Großbuchstaben, sind reserviert für „die Implementierung“, also für
Compiler und Standardbibliothek.
</Leierkasten>

Davon abgesehen, wenn du den Namen der Struktur gar nicht benutzt,
musst du auch nicht erst den Namensraum damit zumüllen.  Am Ende wirst
du ja eh nur dein typedef benutzen.

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

Eines noch.

Es ist eine weltweite Kovention, dass Namen in Grossbuchstaben für 
Makros reserviert sind und auch umgekehrt Makros immer Namen komplett in 
Grossbuchstaben bekommen.

Die Sache ist die, das es an manchen Stellen wichtig ist, zu wissen, 
dass man es mit einem Makro zu tun hat, da Makros ja nur Textersetzungen 
sind. Das kann in manchen Fällen Auswirkungen haben. Daher möchte man 
das an den verendenden Stellen auch sehen, dass man es mit einem Makro 
zu tun hat.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Da ein Bitfeld eine Struktur ist, ist eine Zuweisung davon also
> vollkommen in Ordnung.
Alles klar, danke.

Jörg Wunsch schrieb:
> Kaj G. schrieb:
>> struct _BITFIELD
>
> Das ist allerdings nicht zulässig:
>
> <Leierkasten>
> Namen, die mit einem Unterstrich beginnen, gefolgt von einem Unterstrich
> oder Großbuchstaben, sind reserviert für „die Implementierung“, also für
> Compiler und Standardbibliothek.
> </Leierkasten>
Danke fuer den Hinweis. :)

Jörg Wunsch schrieb:
> Davon abgesehen, wenn du den Namen der Struktur gar nicht benutzt,
> musst du auch nicht erst den Namensraum damit zumüllen.  Am Ende wirst
> du ja eh nur dein typedef benutzen.
Das hab ich gemacht, damit ich sowas machen kann:
1
typedef struct _BITFIELD
2
{
3
  uint8_t eins : 1;
4
  uint8_t zwei : 1;
5
  uint8_t drei : 1;
6
  uint8_t vier : 1;
7
  uint8_t fuenf : 1;
8
  uint8_t sechs : 1;
9
}BITFIELD;
10
11
int main(void))
12
{
13
  BITFIELD myvar =
14
  {
15
    .eins = 0;
16
    .zwei = 0;
17
    .drei = 0;
18
    .vier = 0;
19
    .fuenf = 0;
20
    .sechs = 0;
21
  }
22
23
  ...
24
  return 0;
25
}
Das hab ich mir aus den Atmel-Headerdateien und dem Linux-Kernel 
abgeschaut. Wenn's falsch ist, lass ich mich gerne belehren wie es 
richtig gemacht wird.
(Bei einem Bitfeld vielleicht nicht so wichtig, aber anderen Strukturen 
ist das schon brauchbar wenn man das gleich so initialisieren kann.)

Karl Heinz schrieb:
> Es ist eine weltweite Kovention, dass Namen in Grossbuchstaben für
> Makros reserviert sind und auch umgekehrt Makros immer Namen komplett in
> Grossbuchstaben bekommen.
Ich werd's beruecksichtigen, danke :)

========================

Ok, "Fehler"/Unterschied gefunden.

Schreibe ich
1
typedef struct STRUCT_NAME
2
{
3
  ...
4
}NAME;
kann ich mit
1
NAME myvar;
weiter machen.

Schreibe ich
1
typedef struct STRUCT_NAME
2
{
3
  ...
4
};
muss ich mit
1
struct STRUCT_NAME myvar;
weiter arbeiten und muss immer struct dazu schreiben.

Ob das eine jetzt vorteile dem anderen gegenueber hat, weiss ich dann 
aber nicht mehr :-/

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Kaj G. schrieb:
> typedef struct STRUCT_NAME
> {
>   ...
> }NAME;

Du kannst "STRUCT_NAME" auch einfach weglassen.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Du kannst "STRUCT_NAME" auch einfach weglassen.
Danke. :)

Wie gesagt, hab's mir nur aus den Atmel-Haedern abgeschaut :-/
1
// aus der datei: sam3x8e.h
2
typedef struct _DeviceVectors
3
{
4
  ...
5
} DeviceVectors;
6
7
// aus startup_sam3xa.c
8
const DeviceVectors exception_table =
9
{
10
  .pfnReset_Handler      = (void*) Reset_Handler,
11
  ...
12
};
Ich bin davon ausgegangen, die Jungs bei Atmel werden das schon richtig 
gemacht haben :)

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


Lesenswert?

Kaj G. schrieb:
> Schreibe ichtypedef struct STRUCT_NAME
> {
>   ...
> };

Dann war das "typedef" völlig überflüssig, denn du hast ja gar keinen
Typen definiert.

Du solltest dich für einen Stil entscheiden.  Entweder immer als
1
struct foo
2
{
3
  ...
4
};
5
6
...
7
struct foo foo_var;

oder
1
typedef struct
2
{
3
  ...
4
} foo;
5
6
...
7
8
foo foo_var;

In letzterem Fall muss man der Struktur nur dann noch einen Namen
im struct-Namensraum verpassen, wenn man ihn bereits vor dem
Fertigstellen des typedef benutzen muss.  Typisches Beispiel sind
verkettete Listen, die einen Zeiger auf den eigenen Typ enthalten:
1
typedef struct _foo
2
{
3
  ...
4
  struct _foo *next;
5
} foo;
6
7
...
8
foo foo_var;

(Unterstrich und Kleinbuchstabe sind im struct-Namensraum zulässig.)

Hinweis: das alles bezieht sich auf die Programmiersprache C.  In
der sehr ähnlichen, aber eben doch verschiedenen, Sprache C++
begründet der Name einer Struktur zugleich einen Typnamen, dort geht
also:
1
struct foo
2
{
3
  ...
4
};
5
6
foo foo_object;

von Rolf Magnus (Gast)


Lesenswert?

Ich hätte noch eine andere Anmerkung.
Trigraph-Sequenzen sind schon vor 25 Jahren völlig aus der Mode 
gewesen...
Funktioniert das # auf deiner Tastatur nicht? ;-)

Kaj G. schrieb:
> ??=include <stdint.h>

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


Lesenswert?

Rolf Magnus schrieb:
> Trigraph-Sequenzen sind schon vor 25 Jahren völlig aus der Mode
> gewesen...

Die Diskussion hatten wir doch neulich schon mal … sie scheinen in
einzelnen, elitären Clubs noch wichtig genug zu sein, als dass man
sie nicht aus C99 gestrichen hat.

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Rolf Magnus schrieb:
> Ich hätte noch eine andere Anmerkung.
> Trigraph-Sequenzen sind schon vor 25 Jahren völlig aus der Mode
> gewesen...
> Funktioniert das # auf deiner Tastatur nicht? ;-)
Doch, das Funktioniert. :)
Ich finde Trigraphs nur irgendwie cool, nachdem ich die Dinger durch 
Zufall entdeckt habe (Beitrag "Was hat es mit den 'trigraphs' aufsich"), 
und deswegen baue ich die hin und wieder mal mit ein (nur fuer mich, 
just for fun :) ).
Hab mich gefragt, wie lange es dauert bis die hier einer bemerkt :)
Schoen dass sie dir aufgefallen sind :D

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


Lesenswert?

Kaj G. schrieb:
> Hab mich gefragt, wie lange es dauert bis die hier einer bemerkt :)

Aufgefallen waren sie mir auch, aber die anderen Dinge waren ja
wichtiger. ;-)

von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Jörg Wunsch schrieb:
> Kaj G. schrieb:
>> Hab mich gefragt, wie lange es dauert bis die hier einer bemerkt :)
>
> Aufgefallen waren sie mir auch, aber die anderen Dinge waren ja
> wichtiger. ;-)
Das stimmt. Aber ein bisschen spass darf ja auch nicht fehlen. :)

von Karl H. (kbuchegg)


Lesenswert?

Kaj G. schrieb:

> Ich finde Trigraphs nur irgendwie cool

Notiz an mich selber:
Kaj G. findet es nicht nur nicht der Mühe wert, grundlegende Dinge in 
seiner Programmiersprache ordentlich zu lernen (siehe typedef), sondern 
schmeisst einem auch noch coole Prügel zwischen die Beine. Postings in 
Zukunft ignorieren.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Karl Heinz schrieb:
> Kaj G. findet es nicht nur nicht der Mühe wert, grundlegende Dinge in
> seiner Programmiersprache ordentlich zu lernen (siehe typedef)

Dann reich das doch bitte auch an die Leute von Atmel weiter:
Kaj G. schrieb:
> // aus der datei: sam3x8e.h
> typedef struct _DeviceVectors
> {
>   ...
> } DeviceVectors;
>
> // aus startup_sam3xa.c
> const DeviceVectors exception_table =
> {
>   ...
> };
Die scheinen ja kein deut besser zu sein. Wenn du schon auf andere 
Zeigst, mit der Bemerkung das man sich keine Muehe geben wuerde, dann 
zeig doch bitte auch auf alle, besonders auf die, die es vormachen.

Gleichermassen gilt deine Aussage fuer (wahrscheinlich)90% aller 
Beitraege hier im Forum, seien es die Arduino nutzer, Nutzer die fragen 
wie man einen ADC richtig nutzt, oder oder oder... alle diesen 
Frage-Stellern kann man unterstellen sie wuerden sich keine Muehe geben 
die Programmiersprache zu lernen, keine Muehe geben das Datenblatt zu 
lesen, oder oder oder...

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Kaj G. schrieb:
> Dann reich das doch bitte auch an die Leute von Atmel weiter:

Kindchen, lies doch bitte richtig, bevor Du so einen Schwachsinn 
schreibst.

Atmel verwendet sowohl einen Namen für die Struktur als auch für das 
typdef:
1
typedef struct Horst
2
{
3
  ...
4
} Ewald;

Zwei Namen: Horst und Ewald.

Du aber unterschlägst Ewald.

Den Horst kann man an der Stelle weglassen, nicht aber den Ewald.

: Bearbeitet durch User
von Kaj G. (Firma: RUB) (bloody)


Lesenswert?

Kaj G. schrieb:
> typedef struct _BITFIELD
> {
>   ...
> }BITFIELD;

Rufus Τ. Firefly schrieb:
> typedef struct Horst
> {
>   ...
> } Ewald;

Ich seh da jetzt keinen unterschied...
Ob ich nun _BITFIELD Horst, und BITFIELD Ewald nenne, macht keinen 
unterschied.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Kaj G. schrieb:
> Ich seh da jetzt keinen unterschied...

Lies Dir einfach nochmal genau durch, was Karl Heinz geschrieben hat, 
und wozu er das jeweils geschrieben hat.

Versuch es einfach. Karl Heinz hat nämlich Recht.

von Rolf Magnus (Gast)


Lesenswert?

Hier sagst du was anderes:

Kaj G. schrieb:
> Schreibe ich
> typedef struct STRUCT_NAME
> {
>   ...
> }NAME;
> kann ich mit
> NAME myvar;
> weiter machen.
>
> Schreibe ich
> typedef struct STRUCT_NAME
> {
>   ...
> };
> muss ich mitstruct STRUCT_NAME myvar;
> weiter arbeiten und muss immer struct dazu schreiben.

Und das stimmt nicht. Es zeigt, daß du nicht verstanden hast, wie 
typedef eigentlich funktioniert.
1
typedef struct STRUCT_NAME {};

Das ist ungefähr so, als würdest du schreiben:
1
typedef int;

Es fehlt der Name für den typedef.

Abgesehen davon darf Atmel in seinen Systemheadern durchaus Namen wie 
_DeviceVectors nutzen, denn genau für sowas sind sie reserviert. Du 
darfst das nicht.

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


Lesenswert?

Rolf Magnus schrieb:
> Abgesehen davon darf Atmel in seinen Systemheadern durchaus Namen wie
> _DeviceVectors nutzen, denn genau für sowas sind sie reserviert.

Allerdings ist auch deren
1
typedef struct _DeviceHeaders { ... } DeviceHeaders;

nicht sinnvoll.  Wenn sie schon ein typedef schreiben, dann muss die
struct nicht noch extra einen Namen bekommen, der am Ende gar nicht
genutzt wird.

Während das aber Kaj G. ja vielleicht nach unserer Erklärung begreifen
könnte und in Zukunft sich für eins von beiden entscheidet, fürchte
ich, dass ein entsprechender Bugreport bei Atmel für derartige rein
stilistische Unschönheiten (ist ja nicht einmal ein Bug) einfach
vergebliche Liebesmüh ist, da er irgendwo in Chennai im Sande verlaufen
wird.

von Karl H. (kbuchegg)


Lesenswert?

Kaj G. schrieb:


> Zeigst, mit der Bemerkung das man sich keine Muehe geben wuerde, dann
> zeig doch bitte auch auf alle, besonders auf die, die es vormachen.

Die Leute von Atmel wissen aber wie ein typedef funktioniert, wissen wie 
eine Strukturdefinition funktioniert, das man im Zuge einer 
Strukturdeklaration auch gleich eine Variablendefinition machen kann und 
wie das jetzt alles wieder dem typedef in die Hände spielt. Und ich bin 
mir recht sicher, sie wissen auch, was eine anonyme Struktur ist, auch 
wenn sie sie nicht verwenden, selbst wenn sie könnten.
Im Gegensatz zu dir. Du scheinst von dem alles nichts zu wissen.

> Frage-Stellern kann man unterstellen sie wuerden sich keine Muehe geben
> die Programmiersprache zu lernen, keine Muehe geben das Datenblatt zu
> lesen, oder oder oder...

Kann man.
Bei dir bin ich mir aber recht sicher, dass du noch nie ein C-Buch zur 
Hand genommen hast. Es reicht einfach nicht, zu denken man könne C 
lernen, in dem man anderer Leute Code ansieht. Ja, man kann daraus viel 
lernen, aber die Grundlagen müssen sitzen. Denn wie willst du denn sonst 
bewerten können, was du da liest.

Also:
Grundsätzlich gibt es in C die Deklaration einer Struktur. Mit so einer 
Struktur deklariert man einen neuen Datentyp, der daher einen Namen 
haben muss
1
struct vector
2
{
3
  int x;
4
  int y;
5
};

Das ist eine Struktur. Eine Blaupause, die beschreibt wie ein Objekt von 
diesem Typ aussieht.

Um eine derartige Variable, ein Objekt, zu bekommen schreibt man
1
struct vector abc;
2
struct vector def;

So weit so gut.
Es gibt aber noch eine andere Mögllichkeit.
Man kann bei der Strukturdefinition auch gleich die Namen von Variablen 
angeben, die von diesem Typ sind
1
struct vector
2
{
3
  int x;
4
  int y;
5
} abc, def;

Super. Spart ein wenig Schreibarbeit.
Da man sich jetzt allerdings auf die Struktur gar nicht mehr mit Namen 
beziehen muss (die Variablen sind ja direkt dort aufgeführt), kann man 
den Namen der Struktur auch weglassen und die struct wird zu einer 
anonymen struct
1
struct
2
{
3
  int x;
4
  int y;
5
} abc, def;

Klar soweit?

Gut. Dann wechseln wir zum typedef.
Was macht ein typedef?
Ein typedef definiert einen anderen Namen für einen Datentyp. 
Syntaktisch funktioniert das so, dass man sich die Definition einer 
Variablen vorstellt, zum Beispiel
1
char mystr[20];
und dann ganz einfach typedef davor schreibt.
1
typedef char mystr[20];
Das was vorher der Name einer Variablen war, ist jetzt der Name des 
neuen Datentyps und der Rest der dort steht, beschreibt diesen Datentyp. 
Ein
1
typedef char mystr[20];
definiert also einen Datentyp namens 'mystr', der als Synonym für ein 
Array von 20 char steht. Eine derartige Variable
1
mystr Vorname;
ist also nichts anderes als ein Array von 20 char, weil ja der Datentyp 
mystr genau so vereinbart wurde.

So, und jetzt zählen wir 2 und 2 zusammen.

Auf der einen Seite hatten wir die Definition von Variablen mit einer 
anonymen Struktur
1
struct
2
{
3
  int x;
4
  int y;
5
} abc;

Und auf der anderen Seite haben wir den typedef, der aus der 
syntaktischen Definition einer Variablen einen neuen Datentyp erzeugt.

Was also wird die Kombination aus beidem machen
1
typedef struct
2
{
3
  int x;
4
  int y;
5
} abc;


Wir hätten hier aber auch keine anonyme Struktur bemühen müssen. Denn 
auch
1
struct vector
2
{
3
  int x;
4
  int y;
5
} abc;
ist eine gültige Definition einer Variablen. Hier hat halt die Struktur 
einen Namen bekommen, aber Variablendefinition ist Variablendefinition. 
Schreibt man ein typedef davor
1
typedef struct vector
2
{
3
  int x;
4
  int y;
5
} abc;
wird aus abc der Name des neuen Datentyps.
Es ist also egal ob ich der struct einen Namen gegeben habe oder ob ich 
sie anonym lasse. Den neuen Datentyp krieg ich so und so. Der einzige 
Unterschied ist, dass ich mit dem Strukturnamen einen zusätzlichen Namen 
im Programm habe, der mglw. mit anderen Bezeichnungen kollidieren 
könnte. Einen kleinen Unterschied gibt es noch, der weiter oben schon 
erwähnt wurde. Wenn ich mich innerhalb der struct auf die struct selber 
beziehen muss, dann greift hier der typedef noch nicht. Denn der 
existiert ja erst nachdem er abgeschlossen wurde.



Und genau das mein ich, wenn ich nicht müde werde, dass ihr verdammt 
noch mal eure Programmiersprache von der Pieke auf lernen sollt. Und 
zwar nicht indem ihr fremden Code lest (denn wie willst du denn dieses 
Konstrukt auseinanderklamüsern, wenn du weder anonyme Strukturen kennst 
noch weisst wie ein typedef funktioniert), sondern indem ihr Literatur 
lest (vulgo C Buch), in dem genau diese Dinge und noch 100 andere 
beschrieben sind, ohne die man nicht vernünftig programmieren kann und 
die man wissen muss.


Und keine Angst: die anderen kriegen auch ihr Fett ab, wenn klar und 
deutlich zu sehen ist, dass sie von den banalsten Grundlagen ihrer 
Programmiersprache wieder mal keine Ahnung haben.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Allerdings ist auch deren
> typedef struct _DeviceHeaders { ... } DeviceHeaders;
> nicht sinnvoll.  Wenn sie schon ein typedef schreiben, dann muss die
> struct nicht noch extra einen Namen bekommen, der am Ende gar nicht
> genutzt wird.

Ich finde es nicht so ungewöhnlich, den structs einfach immer Namen zu 
geben und nicht nur selektiv, wenn sie wie oben genannt in einer 
verketteten Liste tatsächlich gebraucht werden. Es scheint mir gängige 
Praxis zu sein. Ich weiß auch nicht, was da jetzt besonders störend dran 
sein soll.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Ich finde es nicht so ungewöhnlich, den structs einfach immer Namen zu
> geben und nicht nur selektiv, [...]

Aber warum soll ich etwas definieren, wenn ich es im weiteren Code nicht 
mehr brauche?

Wie sagte Jörg so schön? "... Namensraum damit zumüllen ..."

von MaWin (Gast)


Lesenswert?

Kaj G. schrieb:
> feld2 = feld1; // <-- ist das zulaessig?

Die Frage ist, WAS macht der Compiler draus?

memcpy(&feld2,&feld1,sizeof(BITFIELD));

?

von (prx) A. K. (prx)


Lesenswert?

MaWin schrieb:
> memcpy(&feld2,&feld1,sizeof(BITFIELD));

Kann er machen. Der Compiler ist aber nicht verpflichtet, Löcher in 
Strukturen mit zu kopieren.

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.