Byte-Zugriff auf 32bit Variable

OP #1614532
Lesenswert?

Hallo, hier mein Problem...

ich habe folgende Variablen

ziel  32 Bit
var1  16 Bit
var2  8  Bit
var3  8  Bit

nun will ich alle Variablen "varX" an eine bestimmte Position in die 
Variable "ziel" kopieren. Dabei ist es wichtig, dass so wenig wie 
möglich Rechenzeit benötigt wird.

so soll die Variable Ziel später aussehen (links Bits, rechts welche 
Variable darin sein soll):

 0-7  var3
 8-15 var2
16-31 var1

billig und langsam:
1
ziel|var1;
2
ziel<<=8;
3
ziel|=var2;
4
ziel<<=8;
5
ziel|=var3;

gibt es eine Möglichkeit, z.B. var2 direkt in das zweite byte von "ziel" 
zu schreiben, oder geht das nur in assembler (inline)?
Villeicht mit Zeigern ein Byte weiter, geht das?

Der Bereich ist sehr zeitkritisch und daher will ich ihn möglichst 
optimieren, als ich gesehen habe was GCC aus den verschiebeoperationen 
macht wird es einem aber ganz anders...
assembler kann ich zwar aber wenn würde ich das mit inline-assembler 
machen, wovon ich keine Ahnung habe - Da ist mir C doch lieber

Hat wer ne Idee? :)

Gruß,
Kjartan
Gast #1614603
Lesenswert?

Doch, sollte gehen:
1
#include <stdio.h>
2
#include <stdlib.h>
3

4
union {
5
 unsigned int ziel;
6
 struct {
7
  unsigned char var3;
8
  unsigned char var2;
9
  unsigned short var1;
10
 } quelle;
11
} daten;
12

13
int main(void) {
14
 daten.quelle.var3=255;
15
 daten.quelle.var2=255;
16
 daten.quelle.var1=65535;
17
 printf("%u\n", daten.ziel);
18
 system("PAUSE");  
19
 return 0;
20
}
Ausgabe ist dann 4294967295.

Musst nur vorher prüfen ob auf deinem Zielsystem die endianess stimmt 
und wie groß die entsprechenden Datentypen sind...
#1614662
Lesenswert?

Kjartan Ferstl schrieb:
>> aber meine "Variablen" var1,var2,var3 sind Register von AVR Modulen -.-
>>
>> Hat noch wer ne Idee, oder sollte ich mir besser inline-assembler zu
>> herzen nehmen?

Du bist offensichtlich ein absoluter Anfänger. Höre bitte auf, 
Schlagwörter zu verwenden und lerne stattdessen!

Bernhard
OP #1614710
Lesenswert?

Bernhard R. schrieb:
> Kjartan Ferstl schrieb:
>>> aber meine "Variablen" var1,var2,var3 sind Register von AVR Modulen -.-
>>>
>>> Hat noch wer ne Idee, oder sollte ich mir besser inline-assembler zu
>>> herzen nehmen?
>
> Du bist offensichtlich ein absoluter Anfänger. Höre bitte auf,
> Schlagwörter zu verwenden und lerne stattdessen!
>
> Bernhard

du bist offensichtlich ein absoluter "Profi". Also hör bitte auf,
Schalgwörter zu verwenden um Leute einzustufen. Wenn du Theads ansiehst 
nur um andere zu kritisieren bist du hier (glaube ich) an der flaschen 
Stelle.

Hättest du meine Angabe gelesen, so müsste dir klar sein, dass ich nicht 
gefragt habe: wie lege ich meine Variablen am besten an um möglichst 
viel Tricksen zu können.
Nein, ich fragte ob es möglich ist, eine 8-byte Variable auf das 2. byte 
einer 32 bit Variablen zu schreiben.
Eine union ist aber alles andere als verschiedene Variablen.

Dass es sich im falle meiner "Variablen" um Speicherbereiche handelt, 
welche für AVR Module reserviert sind sollte dabei keine Rolle spielen.

Aber nachdem du mir nichtmal einen Ansatz einer sinnvollen Version 
spendieren konntest muss ich annehmen, dass du wohl der "Profi" bist.

Ich will hier ja keinen Prollo spielen ... aber nein, ich bin definitiv 
kein Anfänger mehr.

Angesichts der Menge an Antworten werde ich mich wohl mal an die 
Parameterübergabe von AVR GCC inline assembler machen.
#1614719
Lesenswert?

Kjartan Ferstl schrieb:
> super danke (allgemeinbildung++)

++allgemeinbildung wäre sinvoller (erst erhöhen, dann den
erhöhten Wert benutzen), als den alten Stand zu nutzen.

Deshalb macht mich der Name C++ auch immer etwas misstrauisch.
++C fände ich vielversprechender.
Gast #1614724
Lesenswert?

Ich würde mal versuchen, die Quellvariablen, anstatt die Zielvariable zu 
shiften:

quick and dirty:
1
uint8_t *temp = Uuint8_t*) &ziel;
2

3
ziel[0] = var1;
4
ziel[1] = var2;
5
ziel[2] = var3;

Abgesehen davon, dass die Variante mit der Union trotzdem geht. Muss 
halt nur etwas nachdenken, und dann im Hauptprogramm im Beitrag von Tim 
die 255 u. 65535 durch var 1 bis 3 ersetzen.
OP #1614728
Lesenswert?

Klaus Wachtler schrieb:
> Kjartan Ferstl schrieb:
>> super danke (allgemeinbildung++)
>
> ++allgemeinbildung wäre sinvoller (erst erhöhen, dann den
> erhöhten Wert benutzen), als den alten Stand zu nutzen.
>
> Deshalb macht mich der Name C++ auch immer etwas misstrauisch.
> ++C fände ich vielversprechender.

unvorstellbar was hier für sinnvolle themenorientierte Antworten 
autauchen.
#1614739
Lesenswert?

Kjartan Ferstl schrieb:
> Nein, ich fragte ob es möglich ist, eine 8-byte Variable auf das 2. byte
> einer 32 bit Variablen zu schreiben.

Eine Variable mit 8 Byte auf ein Byte zu legen, geht nur mit
guter Kompression.

Aber davon abgesehen: was du m.E. wirklich willst, könnte so
aussehen:
1
    uint32_t   ziel;
2
#define var3 (((uint8_t*)&ziel)[0])
3
#define var2 (((uint8_t*)&ziel)[1])
4
#define var1 (((uint16_t*)&ziel)[1])
5
    ...
6
    var1 = 32000; // schreibt in die oberen 16 Bit von ziel
7
    var2 = 120;   // dto. 2. Byte von Ziel
8
    var3 = 120;   // dto. unterstes Byte von Ziel

Wahlweise int..._t statt uint..._t.

Die genannten Abhängigkeiten von Endianness etc. gelten
hierbei auch.
Moderator Persönliche Seite #1614890
Lesenswert?

Klaus Wachtler schrieb:
> ok, verloren

Nö, da stand ja nur der Compilername (GNU Compiler Collection, aber
das ist ja ohnehin das Thema dieses Teilforums).  GCC kann auch
FORTRAN, also liegst du gar nicht so daneben. ;-)  Aber FORTRAN
bietet dafür noch andere Scheußlichkeiten, beispielsweise die
Benutzung von COMMON-Blöcken in zwei verschiedenen Modulen, die
dann in beiden verschieden definiert werden...  Das kann man übrigens
beim GCC auch in C haben, da nicht initialisierte globale Variablen
standardmäßig ebenfalls in COMMON-Blöcken untergebracht werden (kann
man aber abschalten mit -fno-common).  Das sieht dann ungefähr so
aus:

foo.c:
1
unsigned long myvar;
2

3
unsigned long
4
getmyvar(void)
5
{
6
        return myvar;
7
}

bar.c:
1
unsigned char myvar[4];
2

3
void
4
initmyvar(void)
5
{
6
        myvar[0] = 42;
7
        myvar[2] = 0x42;
8
}

main.c:
1
extern void initmyvar(void);
2
extern unsigned long getmyvar(void);
3

4
int
5
main(void)
6
{
7
        initmyvar();
8
        return (int)getmyvar();
9
}

Hier der disassemblierte Code des Compilats:
1
0000010e <main>:
2
 10e:   0e 94 96 00     call    0x12c   ; 0x12c <initmyvar>
3
 112:   0e 94 8d 00     call    0x11a   ; 0x11a <getmyvar>
4
 116:   cb 01           movw    r24, r22
5
 118:   08 95           ret
6

7
0000011a <getmyvar>:
8
 11a:   60 91 00 02     lds     r22, 0x0200
9
 11e:   70 91 01 02     lds     r23, 0x0201
10
 122:   80 91 02 02     lds     r24, 0x0202
11
 126:   90 91 03 02     lds     r25, 0x0203
12
 12a:   08 95           ret
13

14
0000012c <initmyvar>:
15
 12c:   8a e2           ldi     r24, 0x2A       ; 42
16
 12e:   80 93 00 02     sts     0x0200, r24
17
 132:   82 e4           ldi     r24, 0x42       ; 66
18
 134:   80 93 02 02     sts     0x0202, r24
19
 138:   08 95           ret
#1614905
Lesenswert?

Jörg Wunsch schrieb:
> Nö, da stand ja nur der Compilername (GNU Compiler Collection, aber
> das ist ja ohnehin das Thema dieses Teilforums).

Das hatte ich auch gelesen, aber ich traue mich nicht damit
zu argumentieren.
Schließlich hat er dann ja noch C erwähnt.
Außerdem bin ich mir nicht sicher, ob die Diskussion darüber
entspannt wäre.

> Das kann man übrigens beim GCC auch in C haben...
Stimmt.
Nur ist es in FORTRAN ausdrücklich so vorgesehen, wenn auch
dann nicht portabel.
Also eine explizit geduldete Schweinerei, während man in C nicht
darüber spricht.
Moderator Persönliche Seite #1614911
Lesenswert?

Klaus Wachtler schrieb:
> Also eine explizit geduldete Schweinerei, während man in C nicht
> darüber spricht.

Da der Linker zwischen beiden aber der gleiche ist, ist auch klar,
dass beides auf genau gleiche Weise funktioniert. ;-)

Zugegeben aber, das ist und bleibt eine typische FORTRAN-Lösung,
während die typische C-Antwort darauf halt die union ist.  Die ist
auch nicht wirklich portabel, aber zumindest "portabel genug",
wenn man die durch endianess und alignment verursachten Probleme
im Blick behält.

Btw., diese COMMON-Block-Lösung ist auch in C eine der möglichen
zulässigen Implementierungsvarianten.  Da es historisch so gehandhabt
worden ist, lässt der Standard eine derartige Implementierung nach
wie vor als konform durchgehen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren