Forum: Compiler & IDEs 4 Einzelbytes --> 32-bit Variable?


von Norbert (Gast)


Lesenswert?

Hallo C-Gemeinde,
ich habe vier einzelne Bytes, und zwar:

als Highbyte: R4,      unsigned char high_count
als Middel1:  TCNT3H,  unsigned char middel_1_count
als Middel0:  TCNT3L,  unsigned char middel_0_count
als Lowbyte:  PORTC,   unsigned char low_count

Wie wird hieraus eine 32-bit Variable?

Bisher habe ich nur einfache I/O-Operationen in C geschrieben.
Aber jetzt gehts, glaub' ich, ans 'Eingemachte'.

Vielen Dank für eure Hilfe   Norbert

von Peter D. (peda)


Lesenswert?

unsigned long x = R4 | ((unsigned int)TCNT3H << 8) | ((unsigned 
long)TCNT3L << 16) | ((unsigned long)PORTC << 24);


Peter

von Schmittchen (Gast)


Lesenswert?

@Peter: umgekehrt - R4 soll das Highbyte sein usw...

von Norbert (Gast)


Lesenswert?

So, endlich konnte ich eure Diskussionsbeiträge testen.
Es funktioniert wie folgt:

unsigend long x = ((unsigend char) PINC)|((unsigned 
int)TCNTL3<<8)|((unsigned long)TCNT3H<<16)|((unsigned long)R4<<24);

Diese Funktion ist aber nur die halbe Wahrheit. Ich habe die
Counterregister TCNT3L und TCNT3H zuerst in Variablen 
zwischengespeichert, und zwar erst das L-Byte und dann das H-Byte.

Aber dennoch, es gibt Probleme mit dem Auslesen der Counterregister.
Dazu werde ich im Forum 'Allgemein' einen Thread schreiben.

Erstmal vielen Dank für eure Hilfe    Norbert

von Peter (Gast)


Lesenswert?

für sowas ähnliches hab ich das mit einem union gelöst. Ist meiner 
Meinung nach wesentlich eleganter als diese Bitschieberei und zudem 
wesentlich performanter, da ja die 32-Bit Variable und eine Struktur mit 
4 chars den selben Speicherplatz belegen, d.h. nicht mehr umgelagert 
werden müssen.

Also z.B.

struct irgendwas
{
unsigned char highbyte;
unsigned char middle1;
unsigned char middle0;
unsigned char lowbyte;
};

union xyz
{
struct irgendwas wasweissich;
unsigned long lange_variable;
};

usw. usf.
Gruss,

Peter

von Peter (Gast)


Lesenswert?

alternativ ginge es auch so:

struct
{
unsigned long int lowbyte:8;
unsigned long int middle0:8;
unsigned long int middle1:8;
unsigned long int highbyte:8;
} lange_variable;

Gruss,

Peter

von Schmittchen (Gast)


Lesenswert?

Setzt aber leider voraus, daß die Variablen hintereinander im Speicher 
stehen.

von Peter D. (peda)


Lesenswert?

Hallo Peter,

das mit der Union hat aber 2 Nachteile:

1.
Der Code ist langsamer und größer, da Unions immer im RAM abgelegt 
werden.
Ein lokales long kann dagegen in Registern gehalten werden. Auch 
optimieren die meisten Compiler sehr gut. Es kann also durchaus sein, 
daß die ganze Zeile nur aus 4 Befehlen besteht:
in r16, PINC
in r17, TCNTL3
in r18, TCNTH3
mov r19, R4


2.
Eine Union aus verschiedenen Typen ist immer Compilerabhängig.
Jeder Compiler hat die Freiheit, das höchstwertigste oder das 
niederwertigste Byte zuerst zu schreiben.


Peter

von Peter (Gast)


Lesenswert?

@schmittchen

> Setzt aber leider voraus, daß die Variablen hintereinander im Speicher stehen.

von welchen Variablen sprichst du? Ich kann dir leider an diesem Punkt 
nicht folgen, irgendjemand von uns beiden muss hier wohl etwas nicht 
richtig verstanden haben.

@P. Dannegger

> Der Code ist langsamer und größer, da Unions immer im RAM abgelegt werden.

Also ich weiss ja nicht woher du diese Annahme hast, aber da diese Sache 
mich persönlich sehr interessiert habe ich mir die Mühe gemacht und mal 
in meiner eigenen (umfangreichen) Literatur und auch im Internet 
recherchiert, irgendwas zu finden, dass deine Aussage belegen würde, -> 
Fehlanzeige.
Da ich jedoch weiss, dass solche Aussagen dann doch irgendwo bei den 
anderen Lesern im Hinterkopf auf einem gewissen "status quo" 
hängenbleiben, habe ich mir ganz einfach die Mühe gemacht und es an 
einem praktischen Beispiel mit dem avrgcc mal ausgetestet. Natürlich 
kann ich ein union mit dem Attribut register versehen und siehe da, es 
wurden auch entsprechend Register im übersetzten Code verwendet. Das 
Watch-Window des AVR-Studio belegte dies auch mit einer Anzeige des 
ersten Registers "(Reg) 24" und das Register-Window zeigte, dass die 
Register 24-27 bei meinem Versuch auch entsprechend bei einer Zuweisung 
genutzt wurden. Womit diese Behauptung nun wohl eindeutig widerlegt 
wäre.
> Eine Union aus verschiedenen Typen ist immer Compilerabhängig.

Diese Aussage ist so nicht ganz richtig. In einer durch einen C-Compiler 
angelegten Datenstruktur werden entsprechende Fraktionen immer in 
maschinenabhängig aufsteigender Werigkeit gefüllt. D.h. es ist abhängig 
davon, ob man sich auf einem big-endian oder einem little-endian System 
befindet.
Aber unabhängig davon, es dürfte doch wohl das geringste Problem sein, 
solch eine Frage einmalig in einem Versuch zu ermitteln, oder nicht?

Gruss,

Peter

von Schmittchen (Gast)


Lesenswert?

Ich bin von Norberts Angaben ausgegangen. R4, TCNT3H, TCNT3L, PORTC 
liegen nicht im Speicher hintereinander. Deshalb kann keine Union 
darübergestülpt werden.... und extra nochmal eine Struktur (mit weiteren 
4 Bytes) zu verbrauchen, dachte ich zunächst sei auch nicht sinnvoll. 
ABER: Die mittels "|" zusammengestücktelte Variable verbraucht ja auch 
Speicher. Also alles wieder zurück.

> irgendjemand von uns beiden muss hier wohl etwas nicht richtig verstanden haben.
Hier, ich. :)

Schmittchen.

von Peter D. (peda)


Lesenswert?

Hallo Peter,

1.
ich hab das eher allgemein für C-Compiler betrachtet.
Mag sein, daß meiner nicht mehr der neueste ist und da war es so.

Interessant wäre trotzdem, ob die OR-Verknüpfung nicht auch genau so 
optimal ist, wie die Union in Registern.

2.
Mit der Byte-Order bin ich mal mächtig reingefallen und deshalb nehme 
ich das nie mehr. Der, wenn überhaupt, minimal kleinere Code ist es 
nicht wert, sich dafür Fallgruben ins Programm zu nehmen.

Ich will nicht ausschließen, daß alle z.Z. verfügbare AVR-Compiler die 
gleiche Byteorder verwenden, aber vielleicht nimmt man ja Routinen auch 
mal auf anderen Maschinen, getreu dem Motto "C läuft überall".


Peter

von Peter (Gast)


Lesenswert?

Hallo Namensvetter!

> Interessant wäre trotzdem, ob die OR-Verknüpfung nicht auch genau so optimal 
ist, wie die Union in Registern.

ok, aber diesmal bist du an der Reihe, den praktischen Test 
durchzuführen.

> Ich will nicht ausschließen, daß alle z.Z. verfügbare AVR-Compiler die gleiche 
Byteorder verwenden, aber vielleicht nimmt man ja Routinen auch mal auf anderen 
Maschinen, getreu dem Motto "C läuft überall".

Ok, hier hast du Recht, von solchen Konstrukten wird in Lehrbüchern 
abgeraten wenn es auf Portabilität des Codes ankommt.

> Mit der Byte-Order bin ich mal mächtig reingefallen und deshalb nehme ich das 
nie mehr.

Wenn das so ist, dürfte ich mich z.B. nie mehr mit Frauen befassen ;-)

Aber mal ehrlich, die Realität sieht doch etwas anders aus. Man lernt 
etwas dazu und macht halt eben das nächste Mal andere Fehler. Nicht 
wahr?

Mit :-) Gruss

Peter

von Peter D. (peda)


Lesenswert?

Der Vergleich hinkt:

Ich habe ja mit der OR-Verknüpfung eine vollwertige Alternative, die 
Byteorder unabhängig ist.

Aber welche Alternative hast Du zu Frauen ?


Es gibt noch ne Menge Beispiele, wo anderer C-Code vielleicht mal ein 
Byte mehr benötigt aber dafür wesentlich stabiler ist.

Wenn man also in C-Lehrbüchern bestimmte Code-Konstrukte als gefährlich 
eingestuft findet, sollte man das besser ernst nehmen.

Was weiß ich noch über den kritischen Code im Project von vor 2 Jahren.


Peter D.

von Peter (Gast)


Lesenswert?

> Aber welche Alternative hast Du zu Frauen ?

The System has detected an Internal Processing Error in module
REALITY.SYS.  Please shut down your universe and reboot.

Mehr fällt mir dazu nun auch nicht ein.

Gruss,

Peter

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.