Forum: Compiler & IDEs Aufbau von Structs im Speicher


von G. B. (garyb)


Lesenswert?

Hallo,

ich suche derzeit eine eindeutige Antwort/Quelle auf folgende Frage.
Wird der Aufbau von Strukturen in C 1:1 im Speicher übernommen - heist
werden die einzelnen Elemente einer Struktur in der gleichen Reihenfolge 
im Speicher abgelegt, wie sie in der Struktur definiert wurden.

Beispiel:
1
struct adam{
2
  char c_var;
3
  char c_var_2;
4
  char c_var_3;
5
... };

wird das dann auch im Speicher so abgelegt?
Anahme: Ram mit Ausrichtung Byte
RAM:  Byteaddresse 1: c_var
      +1Byte          c_var_2
      +2Byte          c_var_3

Grund für meine Frage ist ein Programmbeispiel welches über Pointer auf 
die Elemente einer Struktur zugegriffen hat. Bisher hielt ich das für
heikel... aber anscheinend gehts ... Zufall?

mfg
Gary

von Peter (Gast)


Lesenswert?

wenn du es so wie im beispiel hinschreibst dann ist es nicht festgelegt. 
Der Compiler kann es machen wie er will.

Wenn man das Schlüsselwort packet verwendet, dann ordnet er es so wie du 
willst - kann aber zu einer schlechten Performance kommen.

von Daniel G. (motello)


Lesenswert?

Die Reihenfolge bleibt bestehen aber der compiler wird die einzelnen 
variablen an speichergrenzen ausrichten (alignment). Bei 32bit 
prozessoren beispielsweise 4B.

Deine 3 chars werden schon hintereinander liegen aber folgende 
konstellation nicht mehr:
1
struct {
2
  char a;
3
  long b;
4
}

Hier wird hinter dem char 3B freigelassen (padding) damit der 4-byte 
long auch an einer 4-byte grenze ausgerichtet wird. Insgesamt nimmt das 
struct also 8B in Anspruch.

Du kannst aber _attribute_ ((packed)) nutzen (siehe "using GCC") um 
den compiler zu zwingen, die variablen ohne padding anzuordnen. wenn 
dann allerdings ein long unaligned im speicher liegt, wird der zugriff 
darauf vermutlich mehr Zeit in anspruch nehmen, weil der prozessor nur 
auf an 4B Grenzen ausgerichtete 4B integer zugreifen kann.

Das zugreifen mit pointern auf struct-member ist problematisch, wenn es 
ein pointer typ ungleich des struct pointers ist, also z.B. char*. Bei 
Optimierung höher O1 wird gcc keinen funktionierenden code mehr 
produzieren (Stichworte: pointer aliasing, strict aliasing rules).

nötig ist das aber manchmal, beispielsweise um netzwerk-packete oder 
genauer dessen header-felder, die zusammenhängend im speicher liegen zu 
lesen oder zu beschreiben. die protokoll header sind aber zum glück so 
aufgebaut, dass der zugriff immer aligned ist. zumindest bei wenigen mir 
bekannten protokollen wie UDP, IP, ethernet II.

hier was zum thema aliasing:
http://mail-index.netbsd.org/tech-kern/2003/08/11/0001.html

und compiler spezifisches hier:
http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc.pdf

von G. B. (garyb)


Lesenswert?

Danke Peter, Danke Daniel für die schnelle Antwort ...

Daniel G. schrieb:
> Die Reihenfolge bleibt bestehen aber der compiler wird die einzelnen
> variablen an speichergrenzen ausrichten (alignment). Bei 32bit
> prozessoren beispielsweise 4B.
>
> Deine 3 chars werden schon hintereinander liegen aber folgende
> konstellation nicht mehr:
>
>
1
struct {
2
>   char a;
3
>   long b;
4
> }
>
> Hier wird hinter dem char 3B freigelassen (padding) damit der 4-byte
> long auch an einer 4-byte grenze ausgerichtet wird. Insgesamt nimmt das
> struct also 8B in Anspruch.

Das hatte ich auch so verstanden - deswegen fügte ich den Hinweis 
Ausrichtung Byte ein.

> Du kannst aber _attribute_ ((packed)) nutzen (siehe "using GCC") um
> den compiler zu zwingen, die variablen ohne padding anzuordnen. wenn
> dann allerdings ein long unaligned im speicher liegt, wird der zugriff
> darauf vermutlich mehr Zeit in anspruch nehmen, weil der prozessor nur
> auf an 4B Grenzen ausgerichtete 4B integer zugreifen kann.

Genau und damit würde ich beim Zugriff über Pointer ohne (packed) immer 
wieder ins Leere greifen - sollte der Pointer (vom Typ char) einfach nur 
inkrementiert werden.
Aber mal angenommen es ist dann nur ein Datentyp verwendet ... könnte 
das mit dem (packed) ja funktionieren. Das ist dann aber GCC spezifisch 
... haben andere Compiler ähnliche "Anweisungen"?

> hier was zum thema aliasing:
> http://mail-index.netbsd.org/tech-kern/2003/08/11/0001.html

Beim schnellen Überfliegen, wird hier über den Zugriff auf 
unterschiedliche Datentypen durch einen Pointer (eines bestimmten 
Datentyps) gesprochen... aber ich verste das erste Beispiel schon nicht
>> so a write through a pointer may change any variable in a program:
>>
>>    int i = 23;
>>    *f = 5;
>>    /* We don't know what value i has at this point. */
>>
>> We cannot know what value i has, since the pointers &i and f may point
>> at the same address (that is what ISO C means when it say that &i and f
>> may alias).
das Beispiel ist mir zu knapp - um es zu verstehen, weil ich nichts von 
f weiss ... wieso sollte er i beeinflussen?

Zusammenfassung wäre für mich ...
Zugriff (nicht gecastet) auf Inhalte unterschiedlicher Datentypen über 
einen Pointer können schief gehen, da der Compiler/Linker, die Addressen
je nach Datentyp (Länge) anders ausrichten kann -> siehe (packed).

>
> und compiler spezifisches hier:
> http://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc.pdf
danke ;O) .... lies sich nicht so schnell lesen ;O)


Wäre das nicht ein paar Zeilen im GCC-Tutorial wert?
Dort werden Zeiger eh nur kurz gestreift.
Vermutlich bekomme ich gleich nen Kommentar, dass dies definitiv kein 
"Einsteiger" interessieren würde ;)

Gruss,
Gary

von Oliver (Gast)


Lesenswert?

>Zusammenfassung wäre für mich ...
>Zugriff (nicht gecastet) auf Inhalte unterschiedlicher Datentypen über
>einen Pointer können schief gehen, da der Compiler/Linker, die Addressen
>je nach Datentyp (Länge) anders ausrichten kann -> siehe (packed).

Wobei ein Zugriff über Pointer auf EINEN Member eines structs 
grundsätzlich problemlos ist. Problematisch ist nur der Zugriff auf 
andere Member über diesen einen Pointer mittels Pointerarithmetik. Und 
da stellt sich dann schon die Frage, ob das dann noch sauberer 
Programmierstil ist, und in ein Tutorial gehört.

Oliver

von Mmmh (Gast)


Lesenswert?

G. B. schrieb:
> Vermutlich bekomme ich gleich nen Kommentar, dass dies definitiv kein
> "Einsteiger" interessieren würde ;

Im Gegenteil. Der belesene Einsteiger (Kernighan & Ritchie) kommt aus so 
eine Idee garnicht:

> Zugriff (nicht gecastet) auf Inhalte unterschiedlicher Datentypen über
> einen Pointer

Deine "Erwartungshaltung" ist hier unangebracht.
Nur weil Du denkst etwas müsse so und so sein, heisst das noch nicht 
das es so ist.

von Mmmh (Gast)


Lesenswert?

Oliver schrieb:
> ob das dann noch sauberer
> Programmierstil ist, und in ein Tutorial gehört.

ACK!

von Daniel G. (motello)


Lesenswert?

Oliver schrieb:
> Wobei ein Zugriff über Pointer auf EINEN Member eines structs
> grundsätzlich problemlos ist. Problematisch ist nur der Zugriff auf
> andere Member über diesen einen Pointer mittels Pointerarithmetik. Und
> da stellt sich dann schon die Frage, ob das dann noch sauberer
> Programmierstil ist, und in ein Tutorial gehört.

Wie schon beschrieben habe ich bei eingehenden Paketen, die in einem 
char-array landen (RX-buffer), einen struct-pointer auf jeden der Header 
gesetzt. Die (packed) structs entsprechen den headern, so dass ich 
direkt auf die felder zugreifen kann. das ist sehr übersichtlich und 
effizient.

Wie sieht denn so etwas in "sauberem" programmierstil aus?

von Hc Z. (mizch)


Lesenswert?

Deine Frage:

>>>    int i = 23;
>>>    *f = 5;
>>>    /* We don't know what value i has at this point. */
>>>
>>> We cannot know what value i has, since the pointers &i and f may point
>>> at the same address (that is what ISO C means when it say that &i and f
>>> may alias).
> das Beispiel ist mir zu knapp - um es zu verstehen, weil ich nichts von
> f weiss ... wieso sollte er i beeinflussen?

betrifft eine ganz andere Baustelle.  Eben weil nichts über f bekannt 
ist, kann der Compiler nach *f = 5 nichts mehr über den Inahlt von i 
wissen[1].  Er kann also ein nachfolgendes
1
int j = 10 * i;
nicht mehr einfach durch
1
int j = 230;
ersetzen, da i durch das Schreiben nach *f verändert worden sein kann. 
Ohne das *f dürfte der Compiler das.

__
[1] Um genau zu sein: Er kann wissen, dass i entweder 5 oder 23 ist. 
Das nützt für Optimierungen aber nichts.

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


Lesenswert?

Daniel G. schrieb:

> Die (packed) structs entsprechen den headern, so dass ich
> direkt auf die felder zugreifen kann. das ist sehr übersichtlich und
> effizient.

Es gibt Architekturen, bei denen du sowas einfach nicht mehr
machen kannst, weil du mit einem unaligned trap rausfliegst (statt
nur ineffektiv zu werden wie auf einem IA32 oder IA64).  Dafür
genügt schon eine Portierung auf einen ARM.  Ganz finster wird's
dann, wenn du auch noch byte-order-Probleme bei einer Portierung
bekommen kannst, beispielsweise bei einer Portierung auf UltraSPARC
oder sowas.

> Wie sieht denn so etwas in "sauberem" programmierstil aus?

Einzeln zusammenbauen, auch wenn's drei CPU-Takte mehr kostet.

von Karl H. (kbuchegg)


Lesenswert?

Daniel G. schrieb:
> Oliver schrieb:
>> Wobei ein Zugriff über Pointer auf EINEN Member eines structs
>> grundsätzlich problemlos ist. Problematisch ist nur der Zugriff auf
>> andere Member über diesen einen Pointer mittels Pointerarithmetik. Und
>> da stellt sich dann schon die Frage, ob das dann noch sauberer
>> Programmierstil ist, und in ein Tutorial gehört.
>
> Wie schon beschrieben habe ich bei eingehenden Paketen, die in einem
> char-array landen (RX-buffer), einen struct-pointer auf jeden der Header
> gesetzt. Die (packed) structs entsprechen den headern, so dass ich
> direkt auf die felder zugreifen kann. das ist sehr übersichtlich und
> effizient.
>
> Wie sieht denn so etwas in "sauberem" programmierstil aus?

Bei dir ist das schon ok so.
Oliver spricht von ganz anderen, völlig unsauberen Dingen. Das Problem: 
Mit einem Cast (um den du hier im Grunde nicht rumkommst) kannst du 
einen Compiler zu allem zwingen. Noch problematischer wird die 
Situation, wenn man den Cast an die falsche Stelle setzt und sich zuerst 
den Struct-Member mittels Pointer-Arithmetik bestimmt und dann erst von 
dort wegcastet. Dein Fall sieht aber anders aus. Du hast einen unsigned 
char Buffer und castest dir eine Startadresse in diesem Buffer auf einen 
'Pointer zu Header' um und greifst dann ganz normal auf den Header zu.

    unsigned char Buffer[...];

    struct Header* pPtr = (struct Header* )Buffer[x];

    Length = pPtr->Length;  // wenn zb ein Length Feld im Header 
existiert

ausser auf das Padding(und ev. die Byte Order) musst du dabei eigentlich 
auf nicht viel aufpassen. Allerdings ist Padding immer 
Compilerspezifisch. Von daher kann man hier keine allgemeinen Regeln 
angeben ausser: Sieh im Compilermanual unter Padding nach und wie man es 
auf einen bestimmten Wert zwingt bzw. abschaltet. Dazu kommt natürlich 
noch, dass man sich hier eigentlich um das Padding auf 2 Seiten kümmern 
muss. Auf der einen Seite könnte der Sender bereits ein Padding in den 
gesendeten Daten haben; auf der anderen Seite könnte der Compiler auf 
der Empfangsseite in die struct Header ein Padding eingearbeitet haben.



(Mir ist auch nicht klar, warum hier das Thema 'Aliasing' aufgebracht 
wurde. IMHO hat das mit der eigentlichen Fragestellung nichts zu tun. 
Bei Aliasing geht es darum, was der Compiler annehmen darf und was er 
nicht annehmen darf, wenn über einen Pointer zugegriffen wird)

von Mmmh (Gast)


Lesenswert?

Daniel G. schrieb:
> Wie schon beschrieben habe ich bei eingehenden Paketen, die in einem
> char-array landen (RX-buffer), einen struct-pointer auf jeden der Header
> gesetzt. Die (packed) structs entsprechen den headern, so dass ich
> direkt auf die felder zugreifen kann. das ist sehr übersichtlich und
> effizient.
>
> Wie sieht denn so etwas in "sauberem" programmierstil aus?

Es wäre besser wenn Du Code zeigst. Im Prinzip habe ich das auch schon 
so wie Du gemacht. Aber die Frage ist wie Du den Zeiger auf die Elemente 
bildest.

von G. B. (garyb)


Lesenswert?

Mmmh schrieb:
> G. B. schrieb:
>> Vermutlich bekomme ich gleich nen Kommentar, dass dies definitiv kein
>> "Einsteiger" interessieren würde ;
>
> Im Gegenteil. Der belesene Einsteiger (Kernighan & Ritchie) kommt aus so
> eine Idee garnicht:
>
>> Zugriff (nicht gecastet) auf Inhalte unterschiedlicher Datentypen über
>> einen Pointer
>
> Deine "Erwartungshaltung" ist hier unangebracht.
> Nur weil Du denkst etwas müsse so und so sein, heisst das noch nicht
> das es so ist.


Sorry - wie du in meinem Beitrag oben lesen kannst... ist das nicht 
meine Erwartungshaltung... drauf gekommen bin ich über ein Beispiel 
darauf gekommen.
nämlich das Beispiel zum DCF77 von Peter Dannegger (erstes Posting)
Beitrag "DCF77 Uhr in C mit ATtiny26"

Auserdem ging es mir dabei nicht um die structs sondern um das Thema 
Allingment... sorry da war ich nicht präzise.

von Daniel G. (motello)


Lesenswert?

Jörg Wunsch schrieb:
> Es gibt Architekturen, bei denen du sowas einfach nicht mehr
> machen kannst, weil du mit einem unaligned trap rausfliegst (statt
> nur ineffektiv zu werden wie auf einem IA32 oder IA64).  Dafür
> genügt schon eine Portierung auf einen ARM.  Ganz finster wird's
> dann, wenn du auch noch byte-order-Probleme bei einer Portierung
> bekommen kannst, beispielsweise bei einer Portierung auf UltraSPARC
> oder sowas.

Hier zitiere ich mich selbst:

Daniel G. schrieb:
> die protokoll header sind aber zum glück so
> aufgebaut, dass der zugriff immer aligned ist. zumindest bei wenigen mir
> bekannten protokollen wie UDP, IP, ethernet II.

Es ist Software für einen ARM9.

Hier das Struct für den IP header:
1
/* IP header */
2
struct ip_typ {
3
  unsigned char version_header;
4
  unsigned char TOS;
5
  unsigned short length;
6
  unsigned short id;
7
  unsigned short flags_offset;
8
  unsigned char TTL;
9
  unsigned char prot;
10
  unsigned short chksum;
11
  unsigned int srcIP;
12
  unsigned int destIP;
13
} __attribute__ ((packed));

Es gibt einen einheitlichen Packet descriptor für RX und TX:
1
struct PD_typ {
2
  
3
  /* buffer location */
4
  unsigned int StartOfFrame;
5
  unsigned int EndOfFrame;
6
7
  /* header pointer */
8
  struct eth_typ *eth_header;
9
  struct ip_typ *ip_header;
10
  struct udp_typ *udp_header;
11
12
  /* PMI communication protocol */
13
  struct PMI_prot_typ *PMI_prot;
14
15
  /* PMI data length */
16
  unsigned int data_length;
17
18
};

Wenn ein Paket empfangen wurde, werden hier die pointer gesetzt:
1
void eth_rx_header (struct PD_typ *RxPd)
2
{
3
    
4
  void *ptr;
5
6
  ptr = (void*) RxB[RxPd->StartOfFrame];
7
8
  /* Ethernet II header */
9
  RxPd->eth_header = ptr;
10
11
  /* IP header */
12
  RxPd->ip_header = ptr + 14;
13
14
  /* UDP header */
15
  RxPd->udp_header = ptr + 34;
16
17
  /* PMI protocol */
18
  RxPd->PMI_prot = ptr + 42;
19
20
}

Ab jetzt kann ich einfach zugreifen.

von Mmmh (Gast)


Lesenswert?

G. B. schrieb:
> Sorry - wie du in meinem Beitrag oben lesen kannst... ist das nicht
> meine Erwartungshaltung... drauf gekommen bin ich über ein Beispiel
> darauf gekommen.

Es ist doch für eine Erwartungshaltung völlig irrelevant ob Du sie 
selbst entwickelt hast oder durch einen Anderen darauf gebracht wurdest, 
Meister.

Postest Du bitte Deinen Code?

von Daniel G. (motello)


Lesenswert?

Der Vollständigkeit halber, selbst wenn es nicht zur Frage gehört, hier 
noch ein Link in dem es auch um Aliasing issues geht (unter "non-bugs" 
und dann unter "C"):
http://gcc.gnu.org/bugs/

Überhaupt eine gute Seite, auch die anderen vermeintlichen compiler bugs 
sollte man kennen :-)

von G. B. (garyb)


Lesenswert?

Leider ist das Thema nun ein bischen ausgeufert...
DANKE für die vielen Antworten... ich hoffe nun aber die Antwort auf 
meine Frage korrekt zu formulieren....

Beim Anlegen von structs im Speicher durch den Compiler/Linker kann 
nicht von einer Anordnung der Struct-Elemente analog zur Deklaration 
ausgegangen werden. Durch Optimierungen, wie z.B. Allignment, können die 
Variablen an nicht aufeinanderfolgenden Adressen liegen. Das heist bei 
einem Pointer-Zugriff über Offset (wie im Beispiel der DCF77-Uhr) können 
fehlerhafte Zugriffsaddressen erzeugt werden.

Kann man das so zusammen fassen?


Gruss,
Gary

PS: an Mmmh (Gast)
Bevor du hier jemanden abfällig mit "Meister" betitulierst würde ich 
vorschlagen das du dich hier im Forum anmeldest und vor allem das du die 
Postings liest, denn dann wüsstest du das es hier um eine grundsätzliche 
Frage geht und nicht um einen Code den ich geschrieben habe.

von Daniel G. (motello)


Lesenswert?

G. B. schrieb:
> Kann man das so zusammen fassen?

So würde ich es formulieren:
Reihenfolge bleibt, aber evtl. werden durch den compiler Leerräume 
eingefügt "padding" um durch korrektes alignment einen schnellen zugriff 
zu gewährleisten. Das padding kann durch __attribute__((packed)) 
aufgehoben werden.

Eine andere Möglichkeit ohne Castings auf gleiche Speicherbereiche mit 
unterschiedlichen Datentypen zuzugreifen ist übrigens ein Union zu 
nutzen:
1
union {
2
  struct {
3
    int a;
4
    int b;
5
  } access_words;
6
  char access_bytes[8];
7
}

Und das ist ISO-C konform.

von Mmmh (Gast)


Lesenswert?

G. B. schrieb:
> würde ich
> vorschlagen das du dich hier im Forum anmeldest

Das ist garnicht nötig. Ich kann mich auch ohne Anmeldung abfällig 
äussern.

von Karl H. (kbuchegg)


Lesenswert?

Daniel G. schrieb:

> Und das ist ISO-C konform.

Nicht wirklich :-)
Das zeigt, bei der angestrebten Verwendungsform, genauso undefiniertes 
Verhalten, wie das Umcasten eines Pointers.

Eine union hat nur dann definiertes Verhalten, wenn man über denselben 
Member die Daten wieder herausliest, über den sie auch hineingeschrieben 
wurden. Über access_bytes schreiben und über access_words lesen verletzt 
genau dieses Prinzip und man landet damit automatisch bei undefiniertem 
Verhalten.

von G. B. (garyb)


Lesenswert?

@Daniel: Danke dir.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

G. B. schrieb:
> Beim Anlegen von structs im Speicher durch den Compiler/Linker kann
> nicht von einer Anordnung der Struct-Elemente analog zur Deklaration
> ausgegangen werden. Durch Optimierungen, wie z.B. Allignment, können die
> Variablen an nicht aufeinanderfolgenden Adressen liegen. Das heist bei
> einem Pointer-Zugriff über Offset (wie im Beispiel der DCF77-Uhr) können
> fehlerhafte Zugriffsaddressen erzeugt werden.

Ja. Der C Standard lässt einige Details (bewusst) offen, die aber
damit nicht zwangsläufig vom Compiler abhängig sind (gibt's auch),
sondern von der Zielarchitektur.

Setzt man sich mit dem ABI der Zielarchitektur auseinander, kann man
dieses Verfahren (ptr+offset) durchaus sinnvoll einsetzen.

Kurz: Wisse was Du tust.

Gruß
Marcus

von Mmmh (Gast)


Lesenswert?

@ G. B.
Das hast Du wirklich in den falschen Hals bekommen. Weder meine 
Bemerkung zu Deiner Erwartungshaltung noch die Anrede "Meister" war 
abfällig gemeint, wenn auch letzteres natürlich nicht ernst gemeint war.

Um ein wenig meinen guten Willen zu zeigen:

> Beim Anlegen von structs im Speicher durch den Compiler/Linker kann
> nicht von einer Anordnung der Struct-Elemente analog zur Deklaration
> ausgegangen werden.
Nein. Man kann davon ausgehen das die Adressen der Elemente in der 
Reihenfolge aufsteigen, wie sie in der Deklaration angegeben sind. 
Darüber hinaus ist der Zeiger auf die Struktur identisch mit dem Zeiger 
auf das erste Element. Siehe K&R, 2. Auflage, S. 209

> Durch Optimierungen, wie z.B. Allignment, können die
> Variablen an nicht aufeinanderfolgenden Adressen liegen.
"Alignment" ist eine Eigenschaft, kein Vorgang. Der Vorgang heisst 
"Padding" und kann durch das pragma "packed" oder andere Optionen 
verhindert werden.

> Das heist bei
> einem Pointer-Zugriff über Offset (wie im Beispiel der DCF77-Uhr) können
> fehlerhafte Zugriffsaddressen erzeugt werden.
Nicht grundsätzlich. Aber dazu müsste man (verd... nochmal) den Code 
sehen, auf den Du Dich beziehst.

Mit gutem Stil ist hier gemeint, sich in der Programmformulierung 
nicht auf eine bestimmte Anordnung zu verlassen soweit sie nicht 
definiert ist.
D.h in dem konkreten Fall, das Du den Zeiger von einem Strukturelement 
nicht als Ausgangspunkt für die Berechnung des Zeigers auf das nächste 
Element nimmst, sondern wieder auf den ursprünglichen Zeiger auf die 
Struktur zurückgreifst.

von Daniel G. (motello)


Lesenswert?

Karl heinz Buchegger schrieb:
> Nicht wirklich :-)
> Das zeigt, bei der angestrebten Verwendungsform, genauso undefiniertes
> Verhalten, wie das Umcasten eines Pointers.
>
> Eine union hat nur dann definiertes Verhalten, wenn man über denselben
> Member die Daten wieder herausliest, über den sie auch hineingeschrieben
> wurden. Über access_bytes schreiben und über access_words lesen verletzt
> genau dieses Prinzip und man landet damit automatisch bei undefiniertem
> Verhalten.

Mist!
Danke für die Verbesserung!

Aber immerhin läuft diese Methode mit Optimierung >O1, also mit 
-fstrict-aliasing:
Aus "using gcc":
1
The practice of reading from a different union member than the one
2
most recently written to (called “type-punning”) is common. Even with
3
‘-fstrict-aliasing’, type-punning is allowed, provided the memory is
4
accessed through the union type. So, the code above will work as expected.

von G. B. (garyb)


Lesenswert?

@Mmmh ... geht also doch auch normal ;)
 um es konkret zu machen hatte ich den Link angehängt....

Wobei ich jetzt aber das Thema auf die Speicherablage verdichtet hatte.

Aber weil ich deinen guten Willen würdige
- das klingt vielleicht ;O) -
hier den kopierte Code.

Zusammenfassung aus dem Beispiel
Deklaration in clock.h
1
struct time {
2
  u8 second;
3
  u8 minute;
4
  u8 hour;
5
  u8 day;
6
  u8 wday;
7
  u8 month;
8
  u8 year;
9
};
Verwendung und Zugriff in dcf77.c
1
struct time newtime;
2
...
3
void decode_dcf77( u8 pulse )
4
{
5
  u8 *d;
6
....
7
  d = (u8 *)&newtime.minute + (i >> 4);  // byte address
8
  i &= 0x0F;        // bit number
9
  if( i == 0 )
10
    *d = 0;        // clear all, if lsb
11
  if( pulse )
12
    *d += LPM(&BMASK[i]);      // set bit
13
....

Und  d = (u8 *)&newtime.minute + (i >> 4);
kann hat mich eben stutzig gemacht, bzw. kann auch daneben gehen.

von Mmmh (Gast)


Lesenswert?

G. B. schrieb:
> @Mmmh ... geht also doch auch normal ;)
> Aber weil ich deinen guten Willen würdige

Seufz. Kaum gibt man den kleinen Finger...

Dann sieh mal selbst zu wenn Du meinst Dir diesen Ton erlauben zu 
können.

von Karl H. (kbuchegg)


Lesenswert?

G. B. schrieb:

>
> Und  d = (u8 *)&newtime.minute + (i >> 4);
> kann hat mich eben stutzig gemacht, bzw. kann auch daneben gehen.

Im Prinzip schon.
Allerdings wissen wir, dass es auf dem AVR kein Padding gibt.
Aber im Grunde hast du recht. Die (softwaretechnisch etwas bessere) 
Alternative ist allerdings gleich um ein gutes Stück aufwändiger, so 
dass man mit der Einschränkung 'ist auf einem AVR mit avr-gcc ok' leben 
kann.
1
  ...
2
3
  switch( i >> 4 ) {
4
    case 0: d = &newtime.minute; break;
5
    case 1: d = &newtime.hour; break;
6
    case 2: d = &newtime.day; break;
7
    case 3: d = &newtime.wday; break;
8
    case 4: d = &newtime.month; break;
9
    case 5: d = &newtime.year; break;
10
    default: d = NULL;
11
  }
12
13
  if( d ) {
14
    i &= 0x0F;        // bit number
15
    if( i == 0 )
16
      *d = 0;        // clear all, if lsb
17
    if( pulse )
18
      *d += LPM(&BMASK[i]);      // set bit
19
20
    ...

von G. B. (garyb)


Lesenswert?

@Karl-Heinz:
Danke - stimmt - das hab ich auch so verstanden.

@Mmmh:
Irgendwie scheine wir grundsätzlich andere Wahrnehmungen zu haben.
Ton kam von dir. Meine Antwort war aber nicht "böse" gemeint, schon gar 
nicht wollte ich die angebotene Hand ausschlagen
Meine Antwort war - zugegeben - etwas sarkastisch (siehe smilys), was 
jemand der selber holzt aber verstehen sollte.
Egal - Gott sei Dank - gibt es Leute die sich im Forum ANMELDEN und 
Beiträge schreiben die fair und sachlich sind.

von Daniel G. (motello)


Lesenswert?

Daniel G. schrieb:
> die protokoll header sind aber zum glück so
> aufgebaut, dass der zugriff immer aligned ist. zumindest bei wenigen mir
> bekannten protokollen wie UDP, IP, ethernet II.

Von wegen! Da will ich mich mal eben korrigieren, nicht dass da einer 
stolpert:
Der Ethernet II header besteht aus 14B und damit ist alles was danach 
kommt (IP, UDP...) für 4B nicht mehr aligned. :-(

sorry!

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.