Forum: Compiler & IDEs Klarer unterschied zwischen CHAR und BYTE


von Simon (Gast)


Lesenswert?

1
typedef void                    VOID;
2
3
typedef char                    CHAR8;
4
typedef unsigned char           UCHAR8;
5
6
typedef unsigned char           BYTE;                           /* 8-bit unsigned  */
7
typedef unsigned short int      WORD;                           /* 16-bit unsigned */
8
typedef unsigned long           DWORD;                          /* 32-bit unsigned */

Somit scheint der unterschied zwischen char und byte nur noch
das vorzeichen zu sein.

Ich möchte nun ein Zeichen aus einer ASCII Tabelle abspeichern,
was macht es hier fuer einen sinn CHAR zu verwenden und somit ein 
Vorzeichen+
zu haben? -127 bis +127 deckt alle 127 ASCII zeichen doch schon ab.
und ein BYTE ist so gesehen auch nicht größer, es können nur mehr Zahlen 
dargestellt
werden weil ebend das sign bit fehlt.

Kann da jemand klarheit rein bringen?

von manny (Gast)


Lesenswert?

Simon schrieb:
> Somit scheint der unterschied zwischen char und byte nur noch
> das vorzeichen zu sein.

Weder chars noch bytes haben Vorzeichen.

von Dr. Sommer (Gast)


Lesenswert?

Ein char muss nicht 8bit sein. Wenn du eine 8bit-Zahl willst verwende 
(u)int8_t . "char" kann signed sein, muss aber nicht. unsigned char und 
signed char können beide gleich viele Zeichen darstellen (nämlich 
2^CHAR_BITS).

von Simon (Gast)


Lesenswert?

1
typedef char                    CHAR8;
2
typedef unsigned char           UCHAR8;
3
typedef unsigned char           BYTE;

Ja aber bleiben wir doch nur mal bei den 3 Typen.
UCHAR8 und BYTE sind das selbe, haben nur andere namen.

Wo ist den nun der magische unterschied zwischen.

"unsigned char" und "char" bzw ebend signed char.

Es gibt also zwei
+-127
und 0-254

Wenn ich hier rein Zahlen speichern will, ist alles klar.

Nur wenn ich hier rein zeichen Unterbringen möchte, worin unterscheiden 
sich dann die zwei?

von Rene H. (Gast)


Lesenswert?

Auf der Ebene C gar nicht, sie werden lediglich im Speicher anders 
abgelegt. Das eine im Zweierkomplement, das andere nicht.

Grüsse,
René

von Dr. Sommer (Gast)


Lesenswert?

Simon schrieb:
> Es gibt also zwei
> +-127
> und 0-254
Falsch. Falls CHAR_BITS=8 ist (zB auf x86 oder AVR oder ARM) geht 
signed char von -128 bis 127, und unsigned char von 0 bis 255. Beides 
mal 256 Möglichkeiten. "char" ist entweder äquivalent zu "signed char" 
oder zu "unsigned char", abhängig vom Compiler.

Simon schrieb:
> Nur wenn ich hier rein zeichen Unterbringen möchte, worin unterscheiden
> sich dann die zwei?
Was verstehst du unter "Zeichen"? C-String-Literale müssen immer in 
"char" gespeichert werden.

von Wolfgang H. (Gast)


Lesenswert?

Hi, Simon,

> Wo ist den nun der magische unterschied zwischen.
> "unsigned char" und "char" bzw ebend signed char.

In nicht so überarbeiteten Momenten, scheint mir, (oder einem anderen 
unter
Deinem Nick) war Dir das schon klar: Die "Magie" ist die Fehlermeldung 
Deines Compilers, wenn Dein Code den type "BIRNE" in type "Aepfel" 
wandeln will.

Ciao
Wolfgang Horn

von Karl H. (kbuchegg)


Lesenswert?

Simon schrieb:

> Wo ist den nun der magische unterschied zwischen.
>
> "unsigned char" und "char" bzw ebend signed char.


Bei einem char ist nicht geregelt ob er ein Vorzeichen hat oder nicht.
Welches der Fall ist entscheidet der COmpiler bzw. der Compilerbauer, je 
nachdem, was auf der entsprechenden CPU einfacher ist.

Es gibt aber Fälle, in denen du es nicht dem Zufall überlassen willst, 
ob ein char ein Vorzeichen hat oder nicht. Zb immer dann, wenn du 
tatsächlich damit rechnen willst. Bei reiner Textverarbeitung (im 
Bereich des ASCII Codes) ist das hingegen wurscht. Für Textverarbeitung 
nimmst du char und der Compiler darf sich aussuchen, ob für ihn mit oder 
ohne Vorzeichen einfacher umzusetzen ist (aber er muss dabei natürlich 
konsistent sein)

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Man muss in einem für einzelne Zeichen definierten Typ mit Vorzeichen 
keinen tieferen Sinn suchen. Dass es das überhaupt gibt hat seine 
Ursache in grauer Vorzeit, in der C in den USA in 7-Bit ASCII auf einer 
Maschine implementiert wurde, die Bytes mit Vorzeichen leichter 
verarbeiten konnte als ohne.

Seither muss die Welt mit diesem Unsinn leben, ob sie will oder nicht. 
Und so ist für den Typ "char" per C Definition nicht festgelegt, ob mit 
oder ohne Vorzeichen. Und da "char *" und "unsigned char *" zwei paar 
Stiefel und Strings "char *" sind, bleibt man bei Arrays aus Zeichen 
eben besser bei "char".

: Bearbeitet durch User
von Simon (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Zb immer dann, wenn du
> tatsächlich damit rechnen willst.

aaaah...Danke, ok gibt es dafür noch ein Beispiel?

Es gibt Leute die wandeln z.B. Ziffern 0-9 um indem sie einfach ein
0x30 (ascci Ziffer 0) hinzu addieren.

Meintest du das mit rechnen? nur das wäre sowohl bei signed als auch 
unsigned machbar.

von Karl H. (kbuchegg)


Lesenswert?

Simon schrieb:
> Karl Heinz Buchegger schrieb:
>> Zb immer dann, wenn du
>> tatsächlich damit rechnen willst.
>
> aaaah...Danke, ok gibt es dafür noch ein Beispiel?

5 - 9

reicht dir das als Beispiel?

> Es gibt Leute die wandeln z.B. Ziffern 0-9 um indem sie einfach ein
> 0x30 (ascci Ziffer 0) hinzu addieren.

Ja,
aber erstens handelt es sich da immer noch klar um Textverarbeitung.
Zweitens ist der ASCII Code von '0' - '9' in einem Bereich, in dem man 
keine Probleme mit dem Vorzeichen kriegt und drittens ist die Addition 
des ASCII Codes für '0' zu einer Konstanten im Bereich 0 bis 9 ebenfalls 
harmlos und bleibt im Bereich der 7-Bit ASCII Codes.

d.h. das fällt unter die Kategorie Textverarbeitung. Genauso wie die 
Umwandlung von Grossbuchstaben in Kleinbuchstaben, indem man mit dem 
ASCII Code rechnet. Ja, man rechnet damit, aber es ist trotzdem noch 
Textverarbeitung (Hinweis: Im Computer ist ausnahmslos alles eine Zahl 
und jegeliche Manipulation eine Berechnung. Entscheidend ist hier nicht 
der Vorgang das gerechnet wird, sondern mit welcher Absicht gerechnet 
wird. Und die Absicht ist hier nun mal einen Text zu bearbeiten)

> Meintest du das mit rechnen?

Nein.
Eine Schleife die 5 mal wiederholt werden muss hat einen Schleifenzähler 
der von 0 bis 4 läuft.

Übliche Raumtemeperaturen laufen (in unseren Breiten) von -30°C (in 
Kühlhäusern) bis +99°C (in der Sauna).

Die Anzahl der Fernseher in einem Haushalt wird selten größer als 255 
sein (und praktisch nie kleiner als 0)


...

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Der Vollständigkeit halber wäre noch zu erwähnen, dass nach wie vor 
Zeichensätze verwendet werden, in denen 'I'+1 nicht 'J' ist. In denen 
die Bedingung (c >= 'A' && c <= 'Z') nicht sinnvoll ist, weil sie 
beispielsweise '\\' einschliesst.

: Bearbeitet durch User
von Simon (Gast)


Lesenswert?

Das bedeutet für mich aber auch jedesmal casten zu müssen.
Mal ein Beispiel von Microchip
1
BOOL StringToIPAddress(BYTE* str, IP_ADDR* IPAddress)

Diese funktion verwendet doch ganz klar einen BYTE pointer, und nicht 
ein char pointer fuer seinen string, somit bleibt mir doch nur die Wahl 
zu casten oder ebend doch Zeichen als BYTE zu deklarieren.

von (prx) A. K. (prx)


Lesenswert?

Simon schrieb:
> Das bedeutet für mich aber auch jedesmal casten zu müssen.

Es gibt Umgebungen und APIs, in denen man nicht darum herum kommt.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Der Vollständigkeit halber wäre noch zu erwähnen, dass nach wie vor
> Zeichensätze verwendet werden, ....

absolut.
EBCDIC (und damit IBM) lässt grüssen. (andere kenn ich nicht, ausser 
noch viel proprietäreren Codierungen. Der ZX80 hatte glaub ich da was 
spezielles)

Aber man muss auch sagen, dass die mittlerweile (Gott sei Dank) ein 
Schattendasein fristen. Der Rest benutzt ASCII und hat damit schon 
Probleme genug.

Was natürlich nichts daran ändert, dass es darüber keine Garantie aus 
dem C-Standard gibt. (Wer hätte das gedacht :-))

von Karl H. (kbuchegg)


Lesenswert?

Simon schrieb:
> Das bedeutet für mich aber auch jedesmal casten zu müssen.
> Mal ein Beispiel von Microchip
>
>
1
> BOOL StringToIPAddress(BYTE* str, IP_ADDR* IPAddress)
2
>
>
> Diese funktion verwendet doch ganz klar einen BYTE pointer, und nicht
> ein char pointer fuer seinen string, somit bleibt mir doch nur die Wahl
> zu casten oder ebend doch Zeichen als BYTE zu deklarieren.
1
inline BOOL CharsToIPAddress( const char* str, IP_ADDR* IPAddress )
2
{
3
  return StringToIPAddress( (BYTE*)str, IPAddress );
4
}

oder auch
1
#define CharsToIPAddress(s,a) StringToIPAddress((BYTE*)(s), (a))


Wo liegt das Problem?
Mit Hilfsfunktionen und/oder Makros kann und konnte man in C schon immer 
die Dinge die nicht passen passend machen. Ohne den kompletten Code 
wegen Castings unleserlich zu machen.


Ja, das kommt vor, dass API-Schreiber nicht mitdenken und den falschen 
Typ benutzen. Aber man muss das ja nicht weiter treiben.

Das gibts ja an anderen Stellen auch:
zb Verwendung von Funktionen aus der mem...() Familie, wenn eigentlich 
str...() angebracht wäre. Und auch umgekehrt: Verwendung von Funktionen 
aus der str...() Familie, wenn eigentlich mem...() angebracht wäre. 
itoa() benutzt, wenn es eigentlich utoa() sein sollte, und natürlich 
auch umgekehrt. etc. etc.
Es gibt eben viele Stolpersteine in C. Wer eine Tante zum Lulu gehen 
braucht, sollte sich eine andere Programmiersprache suchen und einen 
großen Bogen um C machen.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Der ZX80 hatte glaub ich da was spezielles

Gezwungenermassen. Durch Z80 Prozessor und Board-Logik war vorgegeben, 
dass der Code für Zeilende 0x76 sein musste und in 0x40-0x7F kein 
weiterer gültiger Code liegen durfte.

> Aber man muss auch sagen, dass die mittlerweile (Gott sei Dank) ein
> Schattendasein fristen. Der Rest benutzt ASCII und hat damit schon
> Probleme genug.

Wobei die EBCDIC Maschinen immer noch gebaut, weiterentwickelt, 
eingesetzt und u.A. in C programmiert werden. Was man vom ZX80 nicht 
behaupten kann.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

A. K. schrieb:
> Wobei die EBCDIC Maschinen immer noch gebaut, weiterentwickelt,
> eingesetzt und u.A. in C programmiert werden.

Tatsächlich? Wo, und warum?

von W.S. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Aber man muss auch sagen, dass die mittlerweile (Gott sei Dank) ein
> Schattendasein fristen.

RTTY.

von (prx) A. K. (prx)


Lesenswert?

Rufus Τ. Firefly schrieb:
>> Wobei die EBCDIC Maschinen immer noch gebaut, weiterentwickelt,
>> eingesetzt und u.A. in C programmiert werden.
>
> Tatsächlich? Wo, und warum?

IBMs System z lebt als Nachfolger von 360 und 370 weiterhin fort, auf 
aktuell gehaltener technischer Basis (derzeit 5,5GHz Hexacore Chips). 
Vorwiegend wohl als Basis hochverfügbarer Systeme, denn dafür werden 
diese Maschinen hardwaremässig speziell konstruiert. Und die sind in 
allerlei Sprachen programmierbar, darunter auch C.

Da diese Maschinen partitioniert betrieben werden, ist der in der 
jeweiligen Partition verwendete Zeichensatz frei definierbar. Linux auf 
System z verwendet zwar kein EBCDIC, die traditionellen Betriebssysteme 
hingegen schon: http://www.ibm.com/developerworks/library/l-systemz/

: Bearbeitet durch User
von Simon (Gast)


Lesenswert?

Ich will das Thema ja nicht wieder aufwärmen, aber ehm:

Warum nimmt man hier char und baut daraus eine union mit BYTE. um dann 
auch noch Bit zugriff zu haben?
1
  union
2
  {
3
    struct
4
    {
5
      char IPAddress:1;  // Leased IP address is valid
6
      char Gateway:1;    // Gateway address is valid
7
      char Mask:1;    // Subnet mask is valid
8
      char DNS:1;      // Primary DNS is valid
9
      char DNS2:1;    // Secondary DNS is valid
10
      char HostName:1;  // Host name is valid (not implemented)
11
    } bits;
12
    BYTE val;
13
  } validValues;

von Simon (Gast)


Lesenswert?

Es kommt noch besser,
diesmal mit unsigned char

Nur warum einmal unsigned und einmal nicht, und warum überhaupt char?
1
  union
2
  {
3
      struct
4
      {
5
          unsigned char bIsBound : 1;        // Whether or not DHCP is currently bound
6
          unsigned char bEvent : 1;      
7
          unsigned char bOfferReceived : 1;    
8
    unsigned char bDHCPServerDetected : 1;  
9
    unsigned char bUseUnicastMode : 1;    // Indicates if the 
10
      } bits;
11
      BYTE val;
12
  } flags;

von (prx) A. K. (prx)


Lesenswert?

Simon schrieb:
> Nur warum einmal unsigned und einmal nicht, und warum überhaupt char?

"char" kann je nach Plattform negative Werte annehmen und ein Bitfeld 
eines Basistyp mit Vorzeichen kann ebenfalls negative Werte annehmen. 
Ein Bitfeld mit Basistyp char und 1 Bit Länge kann je nach Plattform 0/1 
oder 0/-1 enthalten. Sinnvoll ist ein nacktes "char" also i.d.R. nicht. 
Wenn schon, dann "signed char" oder "unsigned char".

Allerdings sind Bitfelder, die eine genau definierte Anordnung 
voraussetzen, von Haus aus nur begrenzt portabel. Wer seinen Code für 
genau eine Plattform schreibt, wie bei Controllern oft der Fall, der 
legt nicht immer viel Wert auf solche Feinheiten.

Ob man als Basistyp "unsigned" statt "unsigned char" wählt kann je nach 
Compiler oder ABI Unterschiede ergeben. Viele Plattformen behandeln ein 
als "unsigned" deklariertes Bitfeld hinsichtlich Aligment wie ein 
normales "unsigned".

So wird im ARM ABI die Struct
  struct { unsigned i:1; }
auf 4 Bytes aligned,
  struct { unsigned char i:1; }
aber nicht.

: Bearbeitet durch User
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.