Forum: Mikrocontroller und Digitale Elektronik c - 2x unsigned char to int16_t


von conf (Gast)


Lesenswert?

Hallo,

ich bekomme zwei Bytes die einen int16_t repräsentieren als unsigend 
char (high und low.)
Wie bekomme ich die Daten bit-weise (also ohne unsigend to signed 
conversation) in den int16_t?
1
int16_t data;
2
unsigned char high, low;
3
4
....
5
6
// Ohne Cast
7
// 8 bit high nach << = 0? 
8
data = low + (high << 8); 
9
10
// Mit Cast zu signed
11
// Data korruption durch cast? 
12
data = (int16_t)low + ( (int16_t)high << 8);
13
14
// Mit cast to unsigend 
15
// Data korruption durh impliziten cast, unsigend to sigend? 
16
data = (uint16_t)low + ( (uint16_t)high << 8);

Wie macht man es richtig?

von Daniel S. (daniel_s49)


Lesenswert?

Einentlich brauchst du da nicht casten und persönlich benutze ich da 
immer das OR:
1
data = low | (high << 8);

von Peter II (Gast)


Lesenswert?

conf schrieb:
> Wie bekomme ich die Daten bit-weise (also ohne unsigend to signed
> conversation) in den int16_t?

im Zweifelsfall so
1
int16_t data;
2
unsigned char high, low;
3
4
char* tmp = (char*)&data;
5
memcpy( tmp, high );
6
memcpy( tmp+1, low );

von Peter II (Gast)


Lesenswert?

Daniel S. schrieb:
> Einentlich brauchst du da nicht casten und persönlich benutze ich da
> immer das OR:data = low | (high << 8);

da bin ich mir nicht sicher. Wenn low und high char sind und char signed 
ist, kommt da Unsinn raus.

Denn das low wird auf int erweitert und dann ist das höchste bit gesetzt 
wenn es low negativ war.

von Peter II (Gast)


Lesenswert?

Peter II schrieb:
> da bin ich mir nicht sicher. Wenn low und high char sind und char signed
> ist, kommt da Unsinn raus.

ok, er hat ja unsigned char geschrieben. dann müsste es gehen.

von Curby23523 N. (Gast)


Lesenswert?

Peter II schrieb:
> conf schrieb:
>> Wie bekomme ich die Daten bit-weise (also ohne unsigend to signed
>> conversation) in den int16_t?
>
> im Zweifelsfall so
>
>
1
> int16_t data;
2
> unsigned char high, low;
3
> 
4
> char* tmp = (char*)&data;
5
> memcpy( tmp, high );
6
> memcpy( tmp+1, low );
7
>

mempcy?? ok vermutlich macht ein kluger compiler alles richtig ich würde 
das aber anders machen:
1
uint16_t * tmp = (uint16_t *)&variable;
2
*tmp++ = low;
3
*tmp = high;

Oder gleich ein union nehmen:
1
union{
2
  uint8_t _Byte[2];
3
  uint16_t _Word;
4
} tmp;
5
6
tmp._Byte[0] = low;
7
tmp._Byte[1] = high;

von Peter II (Gast)


Lesenswert?

Nils H. schrieb:
> mempcy?? ok vermutlich macht ein kluger compiler alles richtig ich würde
> das aber anders machen:
> uint16_t * tmp = (uint16_t *)&variable;
> *tmp++ = low;
> *tmp = high;

das kann nicht funktionieren. du meinst vermutlich uint8_t

von Curby23523 N. (Gast)


Lesenswert?

Ja natürlich, danach dann als uint16_t * interpretieren.

von Dr. Sommer (Gast)


Lesenswert?

Nicht schon wieder dieses Thema... Kurzfassung:

Daniel S. schrieb:
> immer das OR:data = low | (high << 8);
ist korrekt, falls deine Plattform 2er-Komplement nutzt (tun fast alle).

Peter II schrieb:
> im Zweifelsfall so
und
Nils H. schrieb:
> Oder gleich ein union nehmen:

Sind "falsch" und plattform-abhängig, funktioniert nur auf Little-Endian 
Prozessoren.

Nils H. schrieb:
> uint8_t _Byte[2];
>   uint16_t _Word;
Solche Bezeichner sind verboten und für die Standard-Library reserviert 
(alles was __ am Anfang/Ende enthält oder mit _ + Großbuchstabe 
beginnt).

conf schrieb:
> // Data korruption durh impliziten cast, unsigend to sigend?
Implizite Casts gibt es nicht. Casts sind immer explizit, 
Konvertierungen immer implizit.

von conf (Gast)


Lesenswert?

Vielen Dank für die Unterstützung.

ich denke ich werd aufgrund der Lesbarkeit den Union verwenden.

Danke!

von conf (Gast)


Lesenswert?

Dr. Sommer schrieb:
>> // Data korruption durh impliziten cast, unsigend to sigend?
> Implizite Casts gibt es nicht. Casts sind immer explizit,
> Konvertierungen immer implizit.

Hätte ich jetzt als Synonym gesehen.
Worin liegt der Unterschied?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

conf schrieb:
> ich denke ich werd aufgrund der Lesbarkeit den Union verwenden.

Das ist die schlechteste Begründung überhaupt. Die union ist nicht 
portabel und muss zum Endianess der CPU passen. Schön, aber falsch.

Verwende den Shift und gut ist. Das ist lesbar. Bedenke dabei auch, ob 
ein int16_t überhaupt der richtige Datentyp ist. Vermutlich ergibt sich 
aus den beiden uint8_t-Variablen nur sinnvoll eine uint16_t-Variable, 
also eine vorzeichenlose.

von Dr. Sommer (Gast)


Lesenswert?

conf schrieb:
> ich denke ich werd aufgrund der Lesbarkeit den Union verwenden.
Lieber lesbar als funktionsfähig/korrekt?

z.B. hier wurde das alles schon lang und breit diskutiert:
Beitrag "union mit Struktur und Array gleicher Größe?"
Beitrag "Endianess beim Kopieren automatisieren"

conf schrieb:
> Hätte ich jetzt als Synonym gesehen.
Okay die Begriffe werden nicht wirklich konsistent genutzt:
http://en.cppreference.com/w/cpp/language/implicit_conversion
http://en.cppreference.com/w/cpp/language/explicit_cast

von Peter II (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Sind "falsch" und plattform-abhängig, funktioniert nur auf Little-Endian
> Prozessoren.

aber:
1
data = low | (high << 8);


3 Otherwise, the new type is signed and the value cannot be represented 
in it; either the result is implementation-defined or an 
implementation-defined signal is raised.

von Dr. Sommer (Gast)


Lesenswert?

Peter II schrieb:
> 3 Otherwise, the new type is signed and the value cannot be represented
> in it;
Korrekt. Das Ergebnis hängt von der Signed-Darstellung ab. Aber fast 
alle Prozessoren nutzen 2er-Komplement, und bei denen ist das Ergebnis 
daher gleich. Das könnte man durch etwas Bitfrickelei reparieren, ist 
aber immer noch viel besser als memcpy/union/pointer umcasten, denn 
LE/BE Probleme treten viel häufiger auf, und das Signed-Problem hat man 
da genauso.

von Peter II (Gast)


Lesenswert?

Dr. Sommer schrieb:
>> 3 Otherwise, the new type is signed and the value cannot be represented
>> in it;
> Korrekt. Das Ergebnis hängt von der Signed-Darstellung ab. Aber fast
> alle Prozessoren nutzen 2er-Komplement, und bei denen ist das Ergebnis
> daher gleich.

darum geht es doch nicht. Ein wert von 50000 lässt sich nicht in int16_t 
darstellen, damit ist das Ergebnis undefiniert. (auch wenn vermutlich 
immer das richtige rauskommt)

von Curby23523 N. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Solche Bezeichner sind verboten und für die Standard-Library reserviert
> (alles was __ am Anfang/Ende enthält oder mit _ + Großbuchstabe
> beginnt).

Dann benenne sie um und fertig ist, dein Compiler wird schon meckern, 
wenn ihm das nicht gefällt, meine Güte erbsenzählerei hier 
Kopfschüttel ...

von Dr. Sommer (Gast)


Lesenswert?

Peter II schrieb:
> Ein wert von 50000 lässt sich nicht in int16_t
> darstellen, damit ist das Ergebnis undefiniert. (auch wenn vermutlich
> immer das richtige rauskommt)
Ja das sag ich doch... Reden wir aneinander vorbei?

Nils H. schrieb:
> Dann benenne sie um und fertig ist
Wäre halt schlau Anfängern korrekte Code-Beispiele zu geben-

Nils H. schrieb:
> dein Compiler wird schon meckern
Die meisten Compiler meckern leider nicht. Oft funktioniert es, aber 
vielleicht funktioniert es später (neue Compiler/Standard Library 
Version) aufgrund einer Überschneidung nicht mehr; vielleicht sogar so 
dass der Compiler nicht meckert es aber trotzdem irgendwelche Probleme 
gibt.

Nils H. schrieb:
> meine Güte erbsenzählerei hier Kopfschüttel ...
Als Programmierer, insb. bei C und C++, muss man Erbsen sehr genau 
zählen damit man nicht in eine der unzähligen Fallen gerät...

von Mikro 7. (mikro77)


Lesenswert?

Die "richtige" Lösung steht ja schon oben. Der Vollständigkeit halber...

conf schrieb:
> ich bekomme zwei Bytes die einen int16_t repräsentieren als unsigend
> char (high und low.)

Wie wurde denn der ursprüngliche int16_t zu den zwei unsigned char 
gemacht!?

von conf (Gast)


Lesenswert?

Wenn ich das jetzt nochmal zusammenfasse, dann gibt es die richtige 
Lösung nicht da entweder

a) die Typekonvertierung nicht definiert ist und ich ggf. ein Problem 
mit anderen Compiler (oder Versionen) bekomme.

b) die Union und Pointer von der Endianess der Architektur abhängen, 
worunter die Portierbarkeit leidet.


Mikro 7. schrieb:
>
> Wie wurde denn der ursprüngliche int16_t zu den zwei unsigned char
> gemacht!?

Der kommt über eine CAN-Message die ich als ein unsigend char[] bekomme.

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.