Forum: Mikrocontroller und Digitale Elektronik uint16_t in 2 uint8_t umwandeln


von Jakov K. (jackenkoffer)


Lesenswert?

Hallo !
Ich habe folgende Frage :
mit welchem Programmcode ( in C ) kann ich einen beschriebenen uint16_t 
in 2 uint8_t umwandeln ?
außerdem besteht dann die Frage : wie kann ich dann die zwei einzelnen 
bytes ansprechen ? ( ich bräuchte praktisch das high byte und das 
low-byte vom integer)

Danke im Voraus,
Jakov

von Dussel (Gast)


Lesenswert?

1
uint8_t var1=(uint8_t)(var16&0xFF);
2
uint8_t var2=(uint8_t)((var16>>8)&0xFF);
sollte die sichere Variante sein. Probier es mal aus.

von Rolf M. (rmagnus)


Lesenswert?


von Bernhard S. (b_spitzer)


Lesenswert?

Andere Idee: (weiss aber nicht, wie man das beim AVR umsetzen kann)
Man definiert eine 16-Bit Variable und legt die fest auf bestimmte 
RAM-Adressen. Danach definiert man 2 8-Bit Variablen und legt die auf 
die selben (!) 8-Bit Adressen. Der Compiler spuckt zwar eine Warning 
aus, aber die Aufteilung ist sehr elegant. Schreibe den 16-Bit Wert und 
lese 2x die 8-Bit Werte. Beim 8051 mit RIDE sieht das dann so aus:

data at 0x10 unsigned int Ergebnis; // belegt Adressen 0x10 und 0x11
data at 0x10 unsigned char HighByte, LowByte; // Reihenfolge muss man 
bei seinem Compiler testen...

// jetzt kann man zum Beispiel die Timer-Reloads so machen:
Ergebnis = (65536-Zeit_in_us);   // nur die Subtraktion wird gerechnet
TL0 = LowByte;                   // dauert nur 2us
TH0 = HighByte;                  // und es wird nix gerechnet

tschuessle
Bernhard

von Dussel (Gast)


Lesenswert?

Bernhard Spitzer schrieb:
> (weiss aber nicht, wie man das beim AVR umsetzen kann)
Wie kann man das in irgendeinem Controller in C umsetzen? Eine 
Möglichkeit wäre, dass man Zeiger erzeugt und denen die entsprechenden 
Adresse zuweist. Da ist die Gefahr aber groß, dass der Compiler die 
Adresse überschreibt.

>data at 0x10 unsigned int Ergebnis;
Ich würde zwar von mir sagen, dass ich C gut kann, aber 'data' und 'at' 
ist mir da nie untergekommen. Was soll das machen?

von Blub (Gast)


Lesenswert?

Naja, dann lieber gleich eine Union, die ist für sowas gedacht und der 
Compiler meckert nicht.

von Dussel (Gast)


Lesenswert?

1
uint16_t *zvar16=&var16;
2
uint8_t *zvar1=(uint8_t *)var16;
3
uint8_t *zvar2=(uint8_t *)(var16+1);
4
var1=*zvar1;
5
var2=*zvar2;
oder so Ähnlich. Sowas habe ich gerade mit XCode ausprobiert und es 
funktioniert. Ist ziemlich getrickst, aber wahrscheinlich schneller als 
meine erste Variante.

von Oliver J. (skriptkiddy)


Lesenswert?

Blub schrieb:
> Naja, dann lieber gleich eine Union, die ist für sowas gedacht und der
> Compiler meckert nicht.
Und wenn du dann beim Portieren des mal die Endianess gewechselt wird?
Das Einzige was gleichermaßen auf Little und Big Endian funktionieren 
wird ist:

Dussel schrieb:
> uint8_t var1=(uint8_t)(var16&0xFF);
> uint8_t var2=(uint8_t)((var16>>8)&0xFF);

Gruß Oliver

von Oliver J. (skriptkiddy)


Lesenswert?

Dussel schrieb:
> uint16_t *zvar16=&var16;
> uint8_t *zvar1=(uint8_t *)var16;
> uint8_t *zvar2=(uint8_t *)(var16+1);
> var1=*zvar1;
> var2=*zvar2;

Das ist falsch. Du solltest schon richtigen Code posten. So ein Müll 
nützt Niemandem.

So könnte man es über deine Idee auf Little Endian machen:
1
uint16_t var16  = 0xBaBe;
2
uint8_t  var8l  = *((uint8_t*)&var16 + 0);
3
uint8_t  var8h  = *((uint8_t*)&var16 + 1);

Gruß Oliver

von Dussel (Gast)


Lesenswert?

Oliver J. schrieb:
> So könnte man es über deine Idee auf Little Endian machen:
Erstens ist es nicht meine Idee, sondern die von Bernhard Spitzer. Ich 
habe sie nur Standard-C tauglich aufgeschrieben.

Oliver J. schrieb:
> Das ist falsch. Du solltest schon richtigen Code posten. So ein Müll
> nützt Niemandem.
Zweitens: Dann schreib mal, was daran falsch ist, wenn es so 
funktioniert.
Drittens: Was ist in deinem Beispiel anders, als in meinem, außer dass 
ich es ausführlicher mache?

von Marcus O. (marcus6100)


Lesenswert?

Dussel schrieb:

> Oliver J. schrieb:
>> Das ist falsch. Du solltest schon richtigen Code posten. So ein Müll
>> nützt Niemandem.
> Zweitens: Dann schreib mal, was daran falsch ist, wenn es so
> funktioniert.

Das funktioniert so nicht. zvar16 ist ein pointer vom typ uint16_t *, 
dieser + 1 zeigt nicht auf das nächste Byte.

von Dussel (Gast)


Angehängte Dateien:

Lesenswert?

1
uint16_t *zvar16=&var16;
2
uint8_t *zvar1=(uint8_t *)zvar16;        //Natürlich nicht var16
3
uint8_t *zvar2=(uint8_t *)(zvar16+1);
4
var1=*zvar1;
5
var2=*zvar2;
Mein Fehler. In der zweiten und dritten Zeile soll es natürlich zvar16 
heißen. Wenn ich nicht noch einen Schreibfehler drinhabe, müsste es aber 
so funktionieren.
Mit dem +1 funktioniert es bei mir. AVRStudio habe ich nicht hier, aber 
mit XCode geht es, wenn ich statt uint16_t ein unsigned short int und 
statt uint8_t ein unsigned char nehme.
Den funktionierenden Code habe ich mal angehängt. Vielleicht habe ich 
mich beim Abschreiben ja nochmal vertan.

von Oliver J. (skriptkiddy)


Lesenswert?

Mit zvar16+1 erwischst du niemals das High-Byte.

Schau dir mal diese beiden Pointer an:
1
printf("%p %p", (uint8_t *)(zvar16 + 1), (uint8_t*)zvar16 + 1)

Gruß Oliver

von Dussel (Gast)


Lesenswert?

Oliver J. schrieb:
> Mit zvar16+1 erwischst du niemals das High-Byte.
Das kann sein. C hat so viele 'Gemeinheiten'. Trotzdem wüsste ich noch 
gerne, warum es funktioniert.

von Oliver J. (skriptkiddy)


Lesenswert?

Dussel schrieb:
> Trotzdem wüsste ich noch
> gerne, warum es funktioniert.
Ich kann mir beim besten Willen nicht vorstellen, dass es mit deinem 
Code wirklich funktioniert.

Gruß Oliver

von Dussel (Gast)


Lesenswert?

Tut mir leid für das Durcheinander, aber jetzt habe ich es: Die Klammern 
um das zvar16+1 sind zu viel. Die hatte ich in der ersten Version 
fälschlicherweise drumgemacht und dann übernommen.
Danke.
Wenigstens war mein gedachter Ansatz richtig. Im Programm habe ich es ja 
auch automatisch richtig gemacht, nur dann falsch übertragen.

von Sven P. (Gast)


Lesenswert?

Blub schrieb:
> Naja, dann lieber gleich eine Union, die ist für sowas gedacht und der
> Compiler meckert nicht.
Nein, es ist in Standard-C ausdrücklick verboten, einen anderen 
Eintrag aus einer Union zu lesen, als derjenige, der zuletzt einen Wert 
erhalten hat.

Die Zeigerrechnerei ist insofern fatal, als dass Zeiger keine Adressen 
im Speicher sind. Das predige ich etliche Jahre und kriege dafür immer 
nur müdes Lächeln. Und zwar meistens genau so lange, bis der Betroffene 
auf die Fresse gefallen ist und stundenlang debuggt hat.
Es gibt außer Big- und Little-Endian dann auch noch Middle-Endian. 
Könnte außerdem mit Alignment einen Unfall geben.

Der richtige Weg ist ja schon genannt worden: Simple Arithmetik mit 
Schieben. Das optimiert jeder brauchbare Compiler ohnehin auf 
irgendeinen Speicherversatz. Und das funktioniert sogar portabel.

von Dussel (Gast)


Lesenswert?

Oliver J. schrieb:
> Ich kann mir beim besten Willen nicht vorstellen, dass es mit deinem
> Code wirklich funktioniert.
Probier es doch aus. Du hast doch sicher einen C(++)-Compiler rumliegen. 
Für C musst du eben iostream.h einfügen und die Dateiendung in .C 
ändern.

von Oliver J. (skriptkiddy)


Lesenswert?

Dussel schrieb:
> Oliver J. schrieb:
>> Ich kann mir beim besten Willen nicht vorstellen, dass es mit deinem
>> Code wirklich funktioniert.
> Probier es doch aus. Du hast doch sicher einen C(++)-Compiler rumliegen.
> Für C musst du eben iostream.h einfügen und die Dateiendung in .C
> ändern.

lol

von Dosmo (Gast)


Lesenswert?

Sven P. schrieb:
> Blub schrieb:
>> Naja, dann lieber gleich eine Union, die ist für sowas gedacht und der
>> Compiler meckert nicht.
> Nein, es ist in Standard-C ausdrücklick verboten, einen /anderen/
> Eintrag aus einer Union zu lesen, als derjenige, der zuletzt einen Wert
> erhalten hat.
Aber was ist denn dann die Anwendung für eine Union?

> Die Zeigerrechnerei ist insofern fatal, als dass Zeiger keine Adressen
> im Speicher sind. Das predige ich etliche Jahre und kriege dafür immer
> nur müdes Lächeln.
Das interessiert mich jetzt aber doch: Was meinst Du damit und worauf 
willst Du  damit hinaus?

von Oliver J. (skriptkiddy)


Lesenswert?

Dosmo schrieb:
> Aber was ist denn dann die Anwendung für eine Union?
Wie wäre es mit Speicher sparen?

von Oliver J. (skriptkiddy)


Lesenswert?

Dosmo schrieb:
> Das interessiert mich jetzt aber doch: Was meinst Du damit und worauf
> willst Du  damit hinaus?
Zeiger enthalten im Prinzip eine Adresse. Wenn man aber Integer-Werte zu 
ihnen hinzu addiert oder von ihnen subtrahiert, dann ist der neue Wert 
des Zeigers von der Breite des Datentyps abhängig, auf den der Zeiger 
zeigt.

Bsp:
1
/*Annahme Adressen sind 32-Bit */
2
uint8_t*  u8p  =  (uint8_t*)0x00000020;
3
uint16_t* u16p = (uint16_t*)0x00000020;
4
uint32_t* u32p = (uint32_t*)0x00000020;
5
6
u8p  = u8p  + 1;  // neuer wert 0x00000021
7
u16p = u16p + 1;  // neuer wert 0x00000022
8
u32p = u32p + 1;  // neuer wert 0x00000024

Gruß Oliver

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

> Blub schrieb:
>> Naja, dann lieber gleich eine Union, die ist für sowas gedacht und der
>> Compiler meckert nicht.
> Nein, es ist in Standard-C ausdrücklick verboten, einen /anderen/
> Eintrag aus einer Union zu lesen, als derjenige, der zuletzt einen Wert
> erhalten hat.
Na denn erzähl mir mal wie man sonst an die Bits eines floats rankommt 
um sie zB über I2C zu senden.

von Oliver J. (skriptkiddy)


Lesenswert?

Martin Wende schrieb:
> Na denn erzähl mir mal wie man sonst an die Bits eines floats rankommt
> um sie zB über I2C zu senden.
Über einen Pointer-Cast.
http://snipplr.com/view/8806/

Gruß Oliver

von Sven P. (Gast)


Lesenswert?

Martin Wende schrieb:
> Na denn erzähl mir mal wie man sonst an die Bits eines floats rankommt
> um sie zB über I2C zu senden.
In Standard-C theoretisch garnicht.
Wenn du allerdings solcherlei Low-Level-Programmierung anstellen 
möchtest, musst du ja ohnehin in das Handbuch deines Compilers gucken. 
Insofern erübrigt sich die Portabilität dann. Das geht soweit, dass z.B. 
im C-Standard von 1999 nichtmal spezifiziert ist, wie genau ein 'long 
double' auszusehen hat.

Bei Unions kommt erschwerend dazu, dass es bei Bitfeldern keinerlei 
Garantie hast, wie die Bits im Speicher angeordnet sind (Endianness, 
Überlappung). Bei Strukturen kann am Ende aufgefüllt werden. Um das zu 
umgehen, kann man z.B. ein Compiler-spezifisches 'packed'-Attribut 
mitgeben. Da wären wir dann wieder beim Compiler-Handbuch...


Thomas:
Grammatikalisch in die Hose gegangen, da hast du Recht. Also nochmal:
>> Nein, es ist in Standard-C ausdrücklick verboten, einen /anderen/
>> Eintrag aus einer Union zu lesen, als denjenigen, der zuletzt einen Wert
>> erhalten hat.
Ansonsten habe ich natürlich alles falsch verstanden, meine Erfahrungen 
sind Einbildung und du bist der einzig wahre Heilsbringer. In Ordnung? 
Gut.


Dosmo:
Ein Beispiel hast du gesehen, nämlich dass Feldweise adressiert wird. 
Wenn ein Feld vier Zeichen lang ist, rückt man im Speicher halt vier 
Zeichen weiter, wenn man '1' zum Zeiger addiert.
Weiterhin ist die Zuordnung zwischen Zeiger und Ganzzahl nicht 
spezifiziert. Es ist zwar erlaubt, einen Zeiger in eine Ganzzahl zu 
wandeln und wieder zurück, es ist aber nicht festgelegt, dass diese 
Ganzzahl in irgendeinen der Integer-Typen passen muss (ISO/IEC 9899:TC2 
6.3.2.3  Abs. 6)!

Wenn du eine Ganzzahl in einen Zeiger umwandelst, muss dieser Zeiger 
weder richtig ausgerichtet sein, noch auf ein gültiges Objekt zeigen. 
Das insbesondere ist relevant, wenn man einen uint8_t* auf einen 
uint16_t* umbiegt. Möglicherweise zeigt der dann nämlich garnicht mehr 
auf den gewünschten uint16_t.

Auch da hilft das Compiler-Handbuch weiter.


Im übrigen, und das habe ich ja angedeutet, erschließt sich mir der Sinn 
hinter der Pfriemelei nicht. Der arithmetische Weg mit dem 
Schiebe-Operator ist gut lesbar, portabel und wird praktisch von jedem 
Compiler optimiert.

von Dussel (Gast)


Lesenswert?

Sven P. schrieb:
> Im übrigen, und das habe ich ja angedeutet, erschließt sich mir der Sinn
> hinter der Pfriemelei nicht. Der arithmetische Weg mit dem
> Schiebe-Operator ist gut lesbar, portabel und wird praktisch von jedem
> Compiler optimiert.
Den habe ich ja direkt geschrieben. Danach ging es nur noch um die 
philosophische Überlegung, ob man es auch anders machen kann.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sven P. schrieb:

> Der arithmetische Weg mit dem Schiebe-Operator ist gut lesbar,
> portabel und wird praktisch von jedem Compiler optimiert.

Und selbst wenn es nicht zu 100% optimal übersetzt wird und ne 
überflüssige Instruktion bleibt, ist es allemal besser als die Adresse 
einer lokalen Variablen zu nehemn!

von Sven P. (Gast)


Lesenswert?

Johann L. schrieb:
> Und selbst wenn es nicht zu 100% optimal übersetzt wird und ne
> überflüssige Instruktion bleibt, ist es allemal besser als die Adresse
> einer lokalen Variablen zu nehemn!
Bei dir weiß ich ja, dass du ein Fachmann bist (sic) -- betonst du 
absichtlich die lokale Variable?

Falls dem so ist, dann weil deren Adresse nicht konstant ist und es 
dadurch zu Zeigerrechnerei kommt, wohingegen bei einer globalen Variable 
meistens direkt lds/sts erzeugt werden kann?

von Dussel-Fänger (Gast)


Lesenswert?

Dussel schrieb:
> Danach ging es nur noch um die
> philosophische Überlegung, ob man es auch anders machen kann.

Wie kommt es, dass es Dir notwendig erscheint, die "Überlegung" als eine 
"philosophische" zu bezeichnen, Dussel?

von Dosmo (Gast)


Lesenswert?

Sven P. schrieb:
>
> Dosmo:
> Ein Beispiel hast du gesehen, nämlich dass Feldweise adressiert wird.
> Wenn ein Feld vier Zeichen lang ist, rückt man im Speicher halt vier
> Zeichen weiter, wenn man '1' zum Zeiger addiert.
Okay, das meinst Du.

Dazu am Rande:
Eine Sache, über die ich mal gestolpert bin, war auf dem MSP430 ein 
UART-Empfangs-Ringpuffer, aus dem ich ein uint16_t auslesen wollte. Da 
hab ich auch die Adresse des Datums feucht-fröhlich als uint16_t* 
gecastet, ausgelesen und prompt folgt ein Address-Trap!
Was war passiert? Das Telegram lag an einer ungerade Adresse im Puffer, 
daher war das auszulesende uint16 auch an einer ungeraden Adresse, und 
auf diese kann man (beim MSP430 und vielen anderen) keinen 16Bit-Zugriff 
machen.
Es half nur byteweise lesen und Bits schieben.

von Sven P. (Gast)


Lesenswert?

Dosmo schrieb:
> [...]
Schau doch mal im Assembler-Listing, was dann tatsächlich draus wurde. 
Sicherlich auch wieder nur Speicherversatz, also ein Byte-Zugriff oder 
ein Byte-Swap im Arbeitsregister.

Aber du siehst ja auch, manche Leute wollen das einfach nicht verstehen.

von Dosmo (Gast)


Lesenswert?

Sven P. schrieb:
> Johann L. schrieb:
>> Und selbst wenn es nicht zu 100% optimal übersetzt wird und ne
>> überflüssige Instruktion bleibt, ist es allemal besser als die Adresse
>> einer lokalen Variablen zu nehemn!
> Bei dir weiß ich ja, dass du ein Fachmann bist (sic) -- betonst du
> absichtlich die lokale Variable?
>
> Falls dem so ist, dann weil deren Adresse nicht konstant ist und es
> dadurch zu Zeigerrechnerei kommt, wohingegen bei einer globalen Variable
> meistens direkt lds/sts erzeugt werden kann?

Vielleicht sollte man an der Stelle noch folgendes erwähnen:
Es gibt Architekturen, bei denen die CPU-Register nicht im 
Speicheradressraum liegen, d.h. nicht per Adresse/Zeiger (bzw. 
load/store) zugreifbar sind. Lokale Variablen liegen aber gerne in 
Registern. Wenn man eine lokale Variablen dann mit 
Adresse/Register-Zugriff benutzt, muß diese dazu dann auf den Stack 
ausgelagert werden, was den Code auch nicht gerade effizienter macht.

von Sven P. (Gast)


Lesenswert?

Dosmo schrieb:
> Vielleicht sollte man an der Stelle noch folgendes erwähnen:
> Es gibt Architekturen, bei denen die CPU-Register nicht im
> Speicheradressraum liegen, d.h. nicht per Adresse/Zeiger (bzw.
> load/store) zugreifbar sind. Lokale Variablen liegen aber gerne in
> Registern. Wenn man eine lokale Variablen dann mit
> Adresse/Register-Zugriff benutzt, muß diese dazu dann auf den Stack
> ausgelagert werden, was den Code auch nicht gerade effizienter macht.

Ja, das ist im C-Standard berücksichtigt, sodass man von einer 
Variablen, die mit 'register' vereinbart wurde, keine Adresse erhalten 
kann.

An Johann L. dachte ich dann, weil er knietief im AVR-GCC steckt.

von funky (Gast)


Lesenswert?

Dussel schrieb:
> uint16_t *zvar16=&var16;
> uint8_t *zvar1=(uint8_t *)var16;
> uint8_t *zvar2=(uint8_t *)(var16+1);
> var1=*zvar1;
> var2=*zvar2;

und wenn du dir deinen code 3 monate später anschaust, weisst du sofort 
was du da geamcht hast? so einfach wie möglich, so kompliziert wie 
nötig...

von Dussel (Gast)


Lesenswert?

Dussel-Fänger schrieb:
> Dussel schrieb:
>> Danach ging es nur noch um die
>> philosophische Überlegung, ob man es auch anders machen kann.
>
> Wie kommt es, dass es Dir notwendig erscheint, die "Überlegung" als eine
> "philosophische" zu bezeichnen, Dussel?
Philosophisch als Ausdruck dafür, dass es nicht um die praktische 
Anwendung geht, sondern nur um das 'wissen Wollen'. Was natürlich auch 
nicht uninteressant ist.

funky schrieb:
> Dussel schrieb:
>> uint16_t *zvar16=&var16;
>> uint8_t *zvar1=(uint8_t *)var16;
>> uint8_t *zvar2=(uint8_t *)(var16+1);
>> var1=*zvar1;
>> var2=*zvar2;
>
> und wenn du dir deinen code 3 monate später anschaust, weisst du sofort
> was du da geamcht hast? so einfach wie möglich, so kompliziert wie
> nötig...
Der Code ist ja sowieso falsch, aber ich denke, dass ich mir die 
Funktion des Codes rekonstruieren könnte. Davon abgesehen würde ich 
ausnahmsweise mal einen Kommentar setzen, wenn ich sowas verwenden 
würde…

von Philosoph (Gast)


Lesenswert?

Dussel schrieb:
> Dussel-Fänger schrieb:
>
>> Dussel schrieb:
>>> Danach ging es nur noch um die
>>> philosophische Überlegung, ob man es auch anders machen kann.
>
>> Wie kommt es, dass es Dir notwendig erscheint, die "Überlegung" als eine
>> "philosophische" zu bezeichnen, Dussel?

> Philosophisch als Ausdruck dafür, dass es nicht um die praktische
> Anwendung geht, sondern nur um das 'wissen Wollen'. Was natürlich auch
> nicht uninteressant ist.

In diesem Zusammenhang ist die Verwendung des Attributs "philosophisch" 
gänzlich unangebracht, ja sogar falsch.

Avanti Dilettanti, Dussel!

von Dussel (Gast)


Lesenswert?

Tut mir leid, ich verstehe kein Italienisch.
Davon abgesehen gibt es die umgangssprachliche Bezeichnung philosophisch 
für 'einfach nur des Wissens wegen' oder 'aus der Freude am Wissen'. 
Jetzt übersetze die Wörter, aus denen Philosophie gebildet ist noch und 
du wirst feststellen, dass man das hier durchaus gebrauchen kann.
Davon abgesehen: Wenn es dir solche (geistigen) Schmerzen bereitet, 
kannst du es auf deine Verantwortung auch durch ein beliebiges anderes 
Wort ersetzen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dosmo schrieb:
> Sven P. schrieb:
>> Johann L. schrieb:
>>> Und selbst wenn es nicht zu 100% optimal übersetzt wird und ne
>>> überflüssige Instruktion bleibt, ist es allemal besser als die Adresse
>>> einer lokalen Variablen zu nehemn!
>> Bei dir weiß ich ja, dass du ein Fachmann bist (sic) -- betonst du
>> absichtlich die lokale Variable?

Lokal ist eigentlich nicht zutreffend. Es geht um auto-Variablen bzw. 
um solche, die nicht im Static Storage liegen.

In den meisten Anwendungen ist lokal Deckungsgleich mit auto, aber 
dass ist natürlich nicht zwingend.

>> Falls dem so ist, dann weil deren Adresse nicht konstant ist und es
>> dadurch zu Zeigerrechnerei kommt, wohingegen bei einer globalen Variable
>> meistens direkt lds/sts erzeugt werden kann?

Über Adressen von Variablen im Static Storage weiß der Compiler, daß sie 
unveränderlich sind. Zur Adressberechnung kann er also — bei AVR — 
einfach LDI Instruktionen verwenden. Das geht, obwohl er selbst die 
Adresse nicht kennt: Die Adresse wird erst vom Linker/Locator 
festgelegt.
1
void put (char*);
2
3
void foo_static (void)
4
{
5
    static char x[] = "AB";
6
    put (x+1);
7
}
 
kann von avr-gcc also so übersetzt werden:
 
1
foo_static:
2
  ldi r24,lo8(x.1327+1)
3
  ldi r25,hi8(x.1327+1)
4
  rjmp put
 
Ist x hingegen auto:
 
1
void foo_auto (void)
2
{
3
    char x[] = "AB";
4
    put (x+1);
5
}
 
dann sieht der Code etwas anders aus:
 
1
foo_auto:
2
  push r28
3
  push r29
4
  rcall .
5
  push __zero_reg__
6
  in r28,__SP_L__
7
  in r29,__SP_H__
8
  ldi r24,lo8(65)
9
  ldi r25,lo8(66)
10
  ldi r26,0
11
  std Y+1,r24
12
  std Y+2,r25
13
  std Y+3,r26
14
  movw r24,r28
15
  adiw r24,2
16
  rcall put
17
  pop __tmp_reg__
18
  pop __tmp_reg__
19
  pop __tmp_reg__
20
  pop r29
21
  pop r28
22
  ret

Beide Funktionen wurden mit

% avr-gcc -S -Os -mmcu=atmega8

übersetzt, und es ist nix mehr daran zu optimieren bis auf eine unnötige 
Instruktion im zweiten Listing.

Natürlich sind die beiden Funktionen nicht gleichwertig; die erste ist 
zum Beispiel nicht reentrant. Aber das Beispiel zeigt, daß man sich mit 
der Adressbildung von auto-Variablen schnell den Code aufpusten kann — 
insbesondere, wenn es ohne Not geschieht wie oben; und dann 
möglicherweise noch verstreut über den ganzen Code und abgesehen von 
bereits genannten Problemen wie Endianess, Alignment, Aliasing Rules, 
Type Punning, Wart- und Lesbarkeit, Portierbarkeit, etc.

Um die Adresse zu nehmen, muss der Compiler das Auto auf dem Stack (im 
Frame der Funktion) anlegen.

Um es im Frame anlegen zu können, muss ein Frame angelegt werden.

Um einen Frame anzulegen, wird ein Framepointer benötigt.

Um einen Framepointer zu haben, muss er einen initialisieren.

Um einen initialisieren zu können, muss er die betreffenden Register 
(hier Y) sichern und wieder herstellen.

Y ist im weiteren Verlauf der Funktion an den Framepointer gebunden und 
kann nicht mehr für andere Zwecke verwendet werden. Bei AVR mit seiner 
üppigen Ausstattung an Zeigerregistern ist das besonders ungünstig.


> Vielleicht sollte man an der Stelle noch folgendes erwähnen:
> Es gibt Architekturen, bei denen die CPU-Register nicht im
> Speicheradressraum liegen, d.h. nicht per Adresse/Zeiger (bzw.
> load/store) zugreifbar sind.

Ein Compiler wird niemals nicht die Adressberechnung einer Variablen auf 
die Speicheradresse eines GPRs abbilden oder umgekehrt.

Das wäre absoluter Hack und ein schwerer technischer Fehler.

GCC wird niemals auf ein Register über dessen Speicheradresse zugreifen 
oder Adressberechnung eines Auto entsprechend abbilden.

Greift man dennoch über (*((unsigned char volatile*) 10)) auf R10 zu, so 
weiß der Compiler nicht, daß es um einen Zugriff aus R10 geht. Das 
Verhalten ist komplett undefiniert.

von Dussel-Ex (Gast)


Lesenswert?

Dussel schrieb:
> gibt es die umgangssprachliche Bezeichnung philosophisch
> für 'einfach nur des Wissens wegen' oder 'aus der Freude am Wissen'.

Dieser angeblich umgangssprachliche Gebrauch von 'philosphisch' ist frei 
erfunden.

Nomen est omen, Dussel!

von Dussel (Gast)


Lesenswert?

Dussel-Ex schrieb:
> Dussel schrieb:
>> gibt es die umgangssprachliche Bezeichnung philosophisch
>> für 'einfach nur des Wissens wegen' oder 'aus der Freude am Wissen'.
>
> Dieser angeblich umgangssprachliche Gebrauch von 'philosphisch' ist frei
> erfunden.
Ich habe eine Quelle gefunden (es gibt mindestens eine), wo das auch so 
verstanden wird. Trotzdem habe ich es falsch verstanden (nicht 
erfunden). Also muss ich zugeben, dass ich es falsch benutzt habe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sag doch einfach "aus Wissbegier" oder "aus rein akademischem 
Interesse".

von Dussel (Gast)


Lesenswert?

Johann L. schrieb:
> Sag doch einfach "aus Wissbegier" oder "aus rein akademischem
> Interesse".
Ja, danke. Sowas meinte ich und hatte halt dafür 'philosophisch' im 
Kopf. 'Aus Freundschaft zum Wissen'. Es sollte aber längst jedem klar 
sein, was gemeint war.

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.