c - 2x unsigned char to int16_t

Gast #5091732
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?
Gast #5091750
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.
Gast #5091757
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;
Gast #5091794
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.
Moderator Persönliche Seite #5091807
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.
Gast #5091812
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
Gast #5091814
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.
Gast #5091819
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.
Gast #5091826
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)
Gast #5091827
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 ...
Gast #5091837
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...
Gast #5091871
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.

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