Forum: Mikrocontroller und Digitale Elektronik Byte-Vektor aus Array extrahieren


von Martin K. (mkohler)


Lesenswert?

Hallo,
Ich habe ein Array mit 8 Werten, z.B.:
BYTE value[8] = { 2, 5, 0, 0, 1, 7, 3, 0 };

Aus diesem Array möchte ich nun einen Byte-Vektor generieren, für 
welchen gilt:
- Bit0 ist 0 wenn value[0] = 0, sonst 1
- ...
- Bit7 ist 0 wenn value[7] = 0, sonst 1

Ich möchte also im Vektor anzeigen, ob die Array-Elemente 0 sind oder 
nicht.

Wie codiere ich das am effizientesten in C?

von Johannes M. (johnny-m)


Lesenswert?

Das könnte funktionieren (ohne Gewähr und ungetestet):
1
uint8_t vector = 0;
2
for(int8_t i=7; i>=0; i++)
3
{
4
    vector |= value[i] && 1;
5
    vector <<= 1;
6
}
In "vector" sollte dann das stehen, was Du oben haben wolltest.

von Niels H. (monarch35)


Lesenswert?

Johannes M. wrote:
>
1
>     vector |= value[i] && 1;
2
>

Nein, das stimmt nicht. Das prüft nur das LSB in value[x].
Es müsste
1
 vector|= (value[i]?1:0);

heissen. Auch sollte zuerst geshifted werden, sonst ist das vector-LSB 
immer low....

von Simon K. (simon) Benutzerseite


Lesenswert?

Niels Hüsken wrote:
> Johannes M. wrote:
>>
1
>>     vector |= value[i] && 1;
2
>>
>
> Nein, das stimmt nicht. Das prüft nur das LSB in value[x].
> Es müsste

Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist). 
Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".

>
1
>  vector|= (value[i]?1:0);
2
>

Das macht das Gleiche (oder das Selbe? ;))

von Martin K. (mkohler)


Lesenswert?

Simon K. wrote:
> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".

Der Ausdruck evaluiert zu nicht 0, richtig?
Obs eine 1 oder sonst was wird, ist doch compilerabhängig?

von Johannes M. (johnny-m)


Lesenswert?

Simon K. wrote:
> Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist).
> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".
Genau so war es gedacht...

>>
1
>>  vector|= (value[i]?1:0);
2
>>
>
> Das macht das Gleiche (oder das Selbe? ;))
Im Prinzip ja. Und was der Compiler draus macht, dürfte auch ungefähr 
auf das selbe hinauslaufen...

Man muss nur dafür sorgen (wie auch immer), dass aus dem Wert in 
value[i] ein Wahrheitswert wird. Und der kann nunmal nur 0 oder 1 sein.

@Niels:
Du verwechselst "&&" mit "&"! Das was Du meinst, ist "&", das steht aber 
nicht da...

von Johannes M. (johnny-m)


Lesenswert?

Martin Kohler wrote:
> Der Ausdruck evaluiert zu nicht 0, richtig?
Und "nicht 0" ist in C standardmäßig "1" (als Ausgabewert).

> Obs eine 1 oder sonst was wird, ist doch compilerabhängig?
Nö. ANSI sagt, dass das Ergebnis einer logischen Verknüpfung 0 oder 1 
ist. Als *Eingabe*-Wert stimmt Deine Annahme, dass alles, was nicht 0 
ist, als "true" interpretiert wird. Aber auch nur da.

von Simon K. (simon) Benutzerseite


Lesenswert?

Rein theoretisch müsste auch
1
vector |= !(!(value[i]));

Gehen. Es gibt bestimmt was kürzeres, aber da müsste dann mal einer der 
Profis ran ;)

von Kai G. (runtimeterror)


Lesenswert?

C ist jetzt nicht so meine Domäne, aber:
1
uint8_t vector = 0;
2
for(int8_t i=7; i>=0; i++)
3
{
4
    vector |= value[i] && 1;
5
    vector <<= 1;
6
}

bewirkt, dass das Endergebnis eins zu weit nach links geshiftet wird.

&& ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt 
sollte Folgendes gehen:
1
uint8_t vector = 0;
2
for(int8_t i = 7; i >= 0; i++)
3
{
4
    vector = (value[i] << 1) | (value[i] != 0);
5
}

oder?

von Niels H. (monarch35)


Lesenswert?

Simon K. wrote:
> Nein, das stimmt nicht. Es überprüft ob i!=0 (Also logisch wahr ist).

F*ck! Sorry..hab das doppelte "&&" überlesen...mir war, da wäre nur ein 
einfaches "&". Naja, wer Augen hat wie ein Maulwurf.....

> Falls ja, evaluiert der Ausdruck zu "1" ansonsten zu "0".

Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der 
AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit 
ich weiss.

IMO wäre die x?1:0 Lösung vorzuziehen.

von Johannes M. (johnny-m)


Lesenswert?

Kai Giebeler wrote:
> C ist jetzt nicht so meine Domäne, aber:
> [...]
> bewirkt, dass das Endergebnis eins zu weit nach links geshiftet wird.
Stimmt. Müsste sich vermeiden lassen, indem man die beiden Zeilen 
vertauscht. Beim ersten mal ist es egal, dass einmal geschoben wird, 
steht eh ne Null drin...

> && ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt
> sollte Folgendes gehen:
>
> [...]
>
> oder?
Nein, das geht nicht. value[i] ist kein Wahrheitswert. Genau deshalb 
muss ja erst mit irgendeiner logischen Verknüpfung einer draus gemacht 
werden. Und "&& 1" macht genau das. "|" ist hingegen wieder kein 
logischer, sondern ein bitweiser Operator. Das haut nicht hin.

von Martin K. (mkohler)


Lesenswert?

Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle 
"i++" heissen muss ;-)

Ich fasse dann mal zusammen:
1
uint8_t vector = 0;
2
for(int8_t i=7; i>=0; i--)
3
{
4
    vector <<= 1;
5
    vector |= value[i] ? 1 : 0;
6
}

Das Zielsystem ist übrigens ein Cypress Controller, mit KEIL 
programmiert.

von Benedikt K. (benedikt)


Lesenswert?

Oder ganz sicher und für jeden verständlich:
1
uint8_t vector;
2
for(uint8_t i=0; i<8; i++)
3
{
4
    vector >>= 1;
5
    if (value[i])
6
       vector |= 128;
7
}

von Johannes M. (johnny-m)


Lesenswert?

Martin Kohler wrote:
> Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle
> "i++" heissen muss ;-)
Schön, dass Du das selber rausgefunden hast. Wie sagte mein Mathe-Lehrer 
immer, wenn er was falsches an die Tafel geschrieben hatte und ein 
Schüler ihn drauf aufmerksam machte: "Wollte nur sehen, ob Sie auch 
aufpassen..."

von Niels H. (monarch35)


Lesenswert?

Martin Kohler wrote:
> Interessanterweise hat noch keiner bemängelt, dass es "i--" anstelle
> "i++" heissen muss ;-)

Nein, muss es nicht, aber da ist trozdem ein fehler. Intention war es 
wohl, i von 7 bis 0 zählen zu lassen. Da UINT8_T niemals <0 werden kann, 
haben wir eine endloschleife....

Insofern hast du schon recht....

von Johannes M. (johnny-m)


Lesenswert?

Niels Hüsken wrote:
> Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der
> AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit
> ich weiss.
Doch, ist es. Wäre sonst ein wenig chaotisch, wenn alles Mögliche 
rauskommen könnte.

von Simon K. (simon) Benutzerseite


Lesenswert?

Benedikt K. wrote:
> Oder ganz sicher und für jeden verständlich:
>
>
1
> uint8_t vector;
2
> for(uint8_t i=0; i<8; i++)
3
> {
4
>     vector >>= 1;
5
>     if (value[i])
6
>        vector |= 128;
7
> }
8
>

Ja, diese Version würde ich auch vorziehen. Obige ist zwar schön 
geschrieben, aber für Anfänger bspw. unverständlich, was da jetzt ein 
Logischer Operator soll. Der Code dürfte aber in beiden Fällen gleich 
sein.

von Martin K. (mkohler)


Lesenswert?

Uiuiui... gleich mehrere sich überschneidende Beiträge...!

Die Variante von Benedikt K. ist m.E. die einfachste und auch lesbarste 
--> wird so gewählt.

Danke für die Antworten!

von Johannes M. (johnny-m)


Lesenswert?

Niels Hüsken wrote:
> Nein, muss es nicht, aber da ist trozdem ein fehler. Intention war es
> wohl, i von 7 bis 0 zählen zu lassen. Da UINT8_T niemals <0 werden kann,
> haben wir eine endloschleife....
Doch, muss es. Genau deshalb steht da auch kein uint8_t, sondern ein 
int8_t, und der kann kleiner Null werden! War ein Tippfehler, weil ich 
das ganze andersrum angefangen habe (so, wie Benedikt es gemacht hat), 
und danach beim Ändern vergessen hab, aus dem "++" ein "--" zu machen...

von Kai G. (runtimeterror)


Lesenswert?

>IMO wäre die x?1:0 Lösung vorzuziehen.
Ist auf jeden Fall die sauberste Lösung, wenn die casts nicht sauber 
definiert sind.

Unter folgendem Aspekt
>Wie codiere ich das am effizientesten in C?
muss der Autor zwischen Geschwindigkeit und Portabilität abwägen. Der 
ternäre Operator ist wahrscheinlich nicht so gut zu optimieren wie ein 
type cast.

Bin jetzt einfach mal von Effizienz = Geschwindigkeit ausgegangen...

>> && ist doch nur für booleans oder? Wenn C eh implizite Typecasts zulässt
>> sollte Folgendes gehen:
>>
>> [...]
>>
>> oder?
>Nein, das geht nicht. value[i] ist kein Wahrheitswert. Genau deshalb
>muss ja erst mit irgendeiner logischen Verknüpfung einer draus gemacht

Deshalb mache ich ja auch einen Integer-Vergleich um die Konversion 
vorzunehmen.

>werden. Und "&& 1" macht genau das. "|" ist hingegen wieder kein
>logischer, sondern ein bitweiser Operator. Das haut nicht hin.

Beim bitweisen Operator verlasse ich mich auf den impliziten Cast zum 
Integer (wie ihr oben auch mit dem Operator |= ). Sollte in meinen Augen 
also gut funktionieren.

von Niels H. (monarch35)


Lesenswert?

Johannes M. wrote:
>> Das Logisch True "1" ergibt, ist Compiler/ Platform abhängig. Wenn der
>> AVR-GCC das so macht, ist das OK, ist aber nicht standardisiert, soweit
>> ich weiss.

> Doch, ist es. Wäre sonst ein wenig chaotisch, wenn alles Mögliche
> rauskommen könnte.

Nein, alles was nicht 0 ist, ist logisch "true". Und nein, es ist nicht 
Standardisiert. Auch da bin ich mir ziemlich sicher, weil ich das Thema 
schon öfter hatte.

Ich weiss z.B. das der Borland-C Compiler, mit dem ich mich beschäftigt 
habe, True immer als "-1" evaluiert hat....

von Johannes M. (johnny-m)


Lesenswert?

@Kai:
> Deshalb mache ich ja auch einen Integer-Vergleich um die Konversion
> vorzunehmen.
Hast recht. Ist im Prinzip genau das, was ich geschrieben hatte, nur 
eben in einer Zeile. Kam mir nur zuerst ein bisschen komisch vor...

von Johannes M. (johnny-m)


Lesenswert?

Niels Hüsken wrote:
> Nein, alles was nicht 0 ist, ist logisch "true". Und nein, es ist nicht
> Standardisiert. Auch da bin ich mir ziemlich sicher, weil ich das Thema
> schon öfter hatte.
Dass alles, was nicht Null ist, true ist, ist mir klar, aber i.m.E. nur 
als Eingabewert. Dass das Ergebnis einer logischen Operation ebenfalls 
alles sein kann, wäre mir neu (und ich meine auch, mich erinnern zu 
können, dass wir das schon öfter hier hatten). Aber das dürfte sich 
überprüfen lassen.

von Martin K. (mkohler)


Lesenswert?

Bei diesem Code-Segment würde ich ja jetzt erwarten, dass am Schluss 
0x04 im Vektor steht:
1
  char value[8] = { 0, 0, 1, 0, 0, 0, 0, 0 };
2
  char vector = 0;
3
  count=0;
4
5
  for(i=0; i<8; i++)
6
  {
7
    vector >>= 1;
8
    if (value[i])
9
       vector |= 128;
10
  }

Aber was macht VisualStudio daraus? (jaaa, wer arbeitet schon damit... 
nur zu Testzwecken!)
Resultat: 0xFC  !!!!
Sobald eine 1 im MSB steht, werden nur noch 1 nachgeschoben, keine 0 
mehr?!?!

Ist das ein Bug oder ein Feature?

von Kai G. (runtimeterror)


Lesenswert?

hehe ... die Schiebeoperation kopiert das oberste Bit. Einfach vector 
auf 16 bits Breite deklarieren, dann geht's.

von Niels H. (monarch35)


Lesenswert?

Johannes M. wrote:
> Aber das dürfte sich überprüfen lassen.

Ja, das findest du irgendwo in den Dokumenten zum ISO/IEC 9899:1999 
Standard. Aber ich habs gerade mal überprüft. Der AVR-GCC evaluiert 
"True" auf jedenfall zu "1", also funktioniert value[x]&&1 auch 
richtig...

von Martin K. (mkohler)


Lesenswert?

Kai Giebeler wrote:
> hehe ... die Schiebeoperation kopiert das oberste Bit.

Sicher?
1
  char value[8] = { 0, 0, 1, 0, 0, 0, 0, 0 };
2
  char vector = 0;
3
  count=0;
4
5
  for(i=8; i>0; i++)
6
  {
7
    vector <<= 1;
8
    if (value[i])
9
       vector |= 0x01;
10
  }
11
12
  count <<=1;
13
  count <<=1;
14
15
  count++;
16
17
  count <<=1;
18
  count <<=1;
Warum kommt dann hier vector=0xfe und count=0x04 raus??

Bei vector wird das unterste Bit kopiert (diesmal shift-right!), bei 
count jedoch nicht ???

von Johannes M. (johnny-m)


Lesenswert?

@Martin:
Vorsicht! Keine Schiebeoperationen mit vorzeichenbehafteten Werten 
machen! Da kann nämlich tatsächlich alles Mögliche rauskommen. Ich hab 
schon mit Absicht einen uint8_t für vector genommen! Mach es wenn 
überhaupt dann mit einem unsigned char.

von Karl H. (kbuchegg)


Lesenswert?

Martin Kohler wrote:
> Kai Giebeler wrote:
>> hehe ... die Schiebeoperation kopiert das oberste Bit.
>
> Sicher?

Nein.
Es hängt davon ab, ob für den Compiler ein plain vanilla 'char'
ein 'signed char' oder ein 'unsigned char' ist.

Du willst in diesem Fall auf jeden Fall, dass sich der
Compiler aus allem raushält und die Bits so nimmt wie
sie sind. Daher solltest du in diesem Fall explizit
sein und es nicht dem Compiler überlassen, was er nimmt.
Du willst unsigned char

von Johannes M. (johnny-m)


Lesenswert?

So, ich hab grad mal im K&R (2. Auflage, ANSI C) nachgesehen und wurde 
zumindest von dieser Quelle bestätigt:

Zitat:
"By definition, the numeric value of a relational or logical expression 
is 1 if the relation is true, and 0 if the relation is false.
The unary negation operator ! converts a non-zero operand into 0, and a 
zero operand into 1."

Wenn der Borland-Compiler etwas anderes fabriziert, ist er entweder kein 
ANSI-C-Compiler oder der K&R lügt...

von Martin K. (mkohler)


Lesenswert?

Kai Giebeler wrote:
> hehe ... die Schiebeoperation kopiert das oberste Bit. Einfach vector
> auf 16 bits Breite deklarieren, dann geht's.

Nachtrag: Die Schiebeoperation kopiert nur beim Datentyp "char" das 
oberste Bit, d.h. bei einer negativen Zahl werden 1 reingeschoben, bei 
einer positiven 0.

Bei unsigned char werden immer 0 reingeschoben.

von Karl H. (kbuchegg)


Lesenswert?

Martin Kohler wrote:
> Nachtrag: Die Schiebeoperation kopiert nur beim Datentyp "char" das
> oberste Bit, d.h. bei einer negativen Zahl werden 1 reingeschoben, bei
> einer positiven 0.

Vorsicht mit dieser Aussage.
Ob char als 'signed char' oder als 'unsigned char' angesehen wird,
entscheidet der Compilerbauer. Der C-Standard lässt ihm diese
Freiheit.
Man kann also nicht generell sage, dass dieses Verhalten beim
Datentyp 'char' auftritt. Wenn der Compilerbauer eine 'char'
als 'unsigned char' aufgefasst hat, dann stimmt die Aussage
so nicht.

Viele Compiler haben auch Command Line switches mit der sich
die Vorgabe des Compilerbauers überschreiben lässt.

von Martin K. (mkohler)


Lesenswert?

Karl heinz Buchegger wrote:
> Vorsicht mit dieser Aussage.
> Ob char als 'signed char' oder als 'unsigned char' angesehen wird,
> entscheidet der Compilerbauer. Der C-Standard lässt ihm diese
> Freiheit.

Präzisierung:
hier kommt 0x10 raus:
1
  {
2
    unsigned char count = 0x80;
3
    count >>=1;
4
    count >>=1;
5
    count >>=1;
6
  }
und hier kommt 0xf0 raus:
1
  {
2
    signed char count = 0x80;
3
    count >>=1;
4
    count >>=1;
5
    count >>=1;
6
  }

Wo finde ich die Angabe des ANSI-Standards, dass bei signed char bei 
shift-right das MSB kopiert wird?

von Karl H. (kbuchegg)


Lesenswert?

Martin Kohler wrote:
> Wo finde ich die Angabe des ANSI-Standards, dass bei signed char bei
> shift-right das MSB kopiert wird?

Im Grunde nirgends, das folgt aus der Verwendung des 2-er Komplements
für Arithmetik. Welches Verhalten >> und << bei signed Datentypen
haben ist meines Wissens so nicht eindeutig geregelt (genauso
wie beispielsweise das Overflow/Underflow Verhalten bei signed
Datentypen nicht geregelt ist). Da man aber Konsistenz haben will,
wird man das bei Verwendung von 2-er Komplement Arithmetik so machen,
damit der Zusammenhang zwischen Schiebeoperatio und Multiplikation
bzw. Division erhalten bleibt.

Aber auch hier Achtung: Im Grunde ist auch nirgends festgeschrieben,
dass 2-er Komplement verwendet werden muss. Kann mich aber nicht daran
erinnern je von einem Compiler gehört zu haben, der das nicht tut.

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.