Forum: Mikrocontroller und Digitale Elektronik switch case auf union und struct gehen schief


von Jdelphi (Gast)


Lesenswert?

PROZESSOR = 18F87K90
Microchip C18 Compiler.

char i_help;

union reg
{
    char data;
    struct
    {
      char ek          : 2;  //Elektrodenkontrolle
      char sprache    : 1;  //Sprachauswahl
      char TK          : 1;
       char Temp_Sens   : 1;
       char rx_enable   : 1;
      char RT2025      : 1;
      char calfail     : 1;
    };
};
union reg reg0;

  reg0.ek = 0x00;  // Geht
  reg0.ek = 0x01;  // Geht
  reg0.ek = 0x02;  // Geht nicht richtig
  reg0.ek = 0x03;  // Geht nicht richtig

  switch (reg0.ek) {
      case 0x00 :  Do1;  // Geht
      break;
      case 0x01 :  Do2;  // Get
      break;
      case 0x02 :  Do3;  // Get nicht
      break;
      case 0x03 :  Do4;  // Get nicht
      break;
      default   :  DoMist; // Hier lande ich bei 0x02 und 0x03
      break;
  }


So nun hab ich folgendes kleines Problem. Wieso geht das nicht ??
reg0.ek ist ja 2 Bit breit, womit es insgesammt 4 zustände annehmen 
kann.
Ich kenn jedoch nur die zustände 0x00 und 0x01 auswerten, 0x02 und 0x03 
bekomme ich nie ausgewertet.
Wenn ich reg0.ek einen anderen wert als 0x00 oder 0x01 zuweise, lnde ich 
immer in DoMist.

Habe ich irgendwo einen Denkfehler?

Der Debugger meldet mir in reg0.ek immer den richtigen wert.

von Volker Z. (vza)


Lesenswert?

versuch mal
1
...
2
  unsigned char ek : 2;  //Elektrodenkontrolle
3
...

natürlich bei allen variablen.

Volker

von duck&wech (Gast)


Lesenswert?

Charakter mit Vorzeichen habe ich sowieso noch nie gesehen ...

von marcus6100 (Gast)


Lesenswert?

duck&wech schrieb:
> Charakter mit Vorzeichen habe ich sowieso noch nie gesehen ...

Es kommt immer darauf an wie das der Compiler sieht.

Ich hatte mal das Problem das ein Microsoft Visual C++
Programm immer dann gecrasht ist, wenn aus einer Datei
ein ß gelesen und an isdigit gefüttert wurde.


char c = '▀'; // 0xDF
isdigit(c); // crash

Ursache: Übergabeparameter wird von signed char auf int erweitert.

Workaround:
isdigit((unsigned char)c);

von Jdelphi (Gast)


Lesenswert?

Cool hatte nun geklappt.

Also macht der Compiler oder wer auch immerein 0x00, 0x01 und ein 0xFF 
und 0xFE draus.

Danke nochmal.

von Jdelphi (Gast)


Lesenswert?

Ich hatte auch bisher immer gedacht das ein Char kein vorzeichen hatt. 
bei C18 ist das aber anscheinend anders.

http://www.sprut.de/electronic/pic/c/pic_c/pic_c20_variablen.html

Der post oben drüber bezieht sich darauf, dass ich i_help den wert von 
reg0.ek zuweise. Danach dürfte i 0x00, 0x01, xFF oder 0xFE haben.

von Volker Z. (vza)


Lesenswert?

Jdelphi schrieb:
> Der post oben drüber bezieht sich darauf, dass ich i_help den wert von
> reg0.ek zuweise. Danach dürfte i 0x00, 0x01, xFF oder 0xFE haben.

Besser (anschaulicher) :
-2,-1,0 und 1.

von Martin (Gast)


Lesenswert?

Jdelphi schrieb:
> bei C18 ist das aber anscheinend anders.

B.9 im Handbuch. Sollte man lesen.

von Karl H. (kbuchegg)


Lesenswert?

Jdelphi schrieb:
> Ich hatte auch bisher immer gedacht das ein Char kein vorzeichen hatt.

Das ist in C nicht festgelegt und wird der jeweiligen Implementierung 
überlassen.

Darum ist es zb extrem wichtig, dass man sich die Sichtweisen aneignet:

   char            für Textverarbeitung

   signed char     für alle Berechnungen mit kleinen Zahlen
                   mit Vorzeichen

   unsigned char   für alle Berechnungen mit kleinen Zahlen
                   ohne Vorzeichen

Am besten fährt man, wenn man es sich zur Gewohnheit macht, zwischen 
diesen 3(!) Datentypen streng zu unterscheiden. char ist ausschliesslich 
für Zeichenverarbeitung reserviert und wenn man mit Bytes operiert, dann 
ist unsigned char der richtige Datentyp. Alles andere führt über kurz 
oder lang immer irgendwann zu Problemen. Und sei es nur bei 
automatischen Datetyperweiterungen (Integer Promotions), wenn ein char 
laut Standard-C-Regeln zu einem int aufgeblasen werden muss.

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Am besten fährt man, wenn man es sich zur Gewohnheit macht, zwischen
> diesen 3(!) Datentypen streng zu unterscheiden.

Blöd ist nur, wenn man Protokolle hat, die Text und Daten enthalten (was 
in der Regel der Fall ist).
Bei unsigned char meckert der GCC, wenn man es als Text parst.
Bei char wird nicht gemeckert, aber dann geht das Daten auslesen schief, 
da char nach signed int expandiert.

Wie kann man nur auf die blöde Idee kommen, daß es negativen Text gibt.

Die C-Entwickler haben einem damit ein ganz schönes Ei ins Nest gelegt, 
das jeder Programmierer ausbaden muß und sich dumm und dämlich castet.

Ich bin darauf auch schon reingefallen.

Am saubersten wäre es, Datenströme immer als unsigned zu definieren und 
dem GCC das Meckern darüber abschalten zu können.


Peter

von (prx) A. K. (prx)


Lesenswert?

Peter Dannegger schrieb:

> Wie kann man nur auf die blöde Idee kommen, daß es negativen Text gibt.

Anno 1970 gab es nur positiven Text, weil der ASCII-Zeichensatz nur die 
Werte 0-127 kennt. Umlaute hatten die Amerikaner nicht auf der Rechnung.

> Die C-Entwickler haben einem damit ein ganz schönes Ei ins Nest gelegt

Genau genommen waren es nicht die C-Entwickler, sondern DEC.

Damals brachte DEC die PDP-11 raus, deren Bytebefehle die Daten vor der 
Rechnung vorzeichenbehaftet nach 16 Bits konvertiert und dann 
verarbeitet. Wem da ein gewisser Zusammenhang mit der Definition von C 
auffällt: Auf solchen Maschinen hat Unix seine Kindheit verbracht, und 
damit auch C. Vorzeichenlos wäre Zeichenverarbeitung weniger effizient 
gewesen.

Und weil viele sich daran gewöhnt hatten, dass "char" vorzeichenbehaftet 
ist, hat man das so fortgesetzt und viele Compiler machen das auch heute 
noch so. Nicht selten umschaltbar.

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:

>> Die C-Entwickler haben einem damit ein ganz schönes Ei ins Nest gelegt
>
> Genau genommen waren es nicht die C-Entwickler, sondern DEC.
>
> Damals brachte DEC die PDP-11 raus, deren Bytebefehle die Daten vor der
> Rechnung vorzeichenbehaftet nach 16 Bits konvertiert und dann
> verarbeitet. Wem da ein gewisser Zusammenhang mit der Definition von C
> auffällt: Auf solchen Maschinen hat Unix seine Kindheit verbracht, und
> damit auch C. Vorzeichenlos wäre Zeichenverarbeitung weniger effizient
> gewesen.

Interessante Anekdote. Das wusste ich bisher gar nicht. Dank dafür.


Aber Peter hat schon recht. Da ist wohl jeder im Laufe seiner Laufbahn 
ein paar mal reingefallen. Und aus Erfahrung weiß ich: Bei sowas suchst 
du dich dumm und dämlich.

     if( c == 212 )

der Debugger zeigt dir c in hex an, 0xD4 ist 212, und du denkst die 
ganze Zeit: verdammt, das Bitmuster stimmt. Warum schlägt der Vergleich 
nicht an!

von DirkB (Gast)


Lesenswert?

Wie wird das denn bei IBM mit EBCDIC gelöst?
Oder nimmt man da nur Fortran oder Cobol :-)

von (prx) A. K. (prx)


Lesenswert?

DirkB schrieb:

> Wie wird das denn bei IBM mit EBCDIC gelöst?

Bloss weil DEC bei der PDP-11 die Byteverarbeitung so implementierte, 
dass man Reste davon noch heute in C wiederfindet, musste IBM das Jahre 
zuvor ja nicht auch so machen.

Und ja, die IBMs 360/370er wurden nicht mit C behelligt, sondern 
hauptsächlich mit Fortran, Cobol und viel Assembler. Oder PL/I. Die 
Besonderheit an C ist die Einstufung von Einzelzeichen als Zahlenwerte, 
also "char" als kleiner Integer. Das ist in anderen Sprachen 
ungebräuchlich. Ohne dies hat man das Problem nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Blöd ist nur, wenn man Protokolle hat, die Text und Daten enthalten (was
> in der Regel der Fall ist).

Dafür haben die C-Götter den typecast erfunden.

Den muss man bei Texten dann typischerweise zweimal benutzen: jeweils
an der Import- und an der Export-Schnittstelle, also bspw.
1
  char buf[];
2
3
  buf[idx++] = (char)UDR;
4
5
...
6
7
  lcd_sendbyte((uint8_t)*cp++);

von Rolf Magnus (Gast)


Lesenswert?

marcus6100 schrieb:
> Ich hatte mal das Problem das ein Microsoft Visual C++
> Programm immer dann gecrasht ist, wenn aus einer Datei
> ein ß gelesen und an isdigit gefüttert wurde.
>
>
> char c = '▀'; // 0xDF
> isdigit(c); // crash
>
> Ursache: Übergabeparameter wird von signed char auf int erweitert.
>
> Workaround:
> isdigit((unsigned char)c);

Das ist kein Workarund, sondern der von C so vorgesehene Weg, auch wenn 
das wirklich blöd ist.
Die ISO-Norm sagt zu allen Funktionen, die mit einzelnen Zeichen 
arbeiten:

************************************************************************ 
******
The header <ctype.h> declares several functions useful for classifying 
and mapping characters. In all cases the argument is an int, the value 
of which shall
be representable as an unsigned char or shall equal the value of the 
macro EOF. If
the argument has any other value, the behavior is undefined.
************************************************************************ 
******

Das Verhalten des Compilers ist also korrekt.

Jdelphi schrieb:
> Ich hatte auch bisher immer gedacht das ein Char kein vorzeichen hatt.
> bei C18 ist das aber anscheinend anders.

Eigentlich haben sie meistens eins. ISO C läßt dem Compiler hier die 
Wahl, aber mit Vorzeichen ist eigentlich sinnvoller, da ja auch alle 
anderen Integer-Typen ein Vorzeichen haben, wenn weder signed noch 
unsigned davorsteht.

Peter Dannegger schrieb:
> Wie kann man nur auf die blöde Idee kommen, daß es negativen Text gibt.

Aber positiven? Es gibt bei Text schlicht und ergreifend kein Konzept 
"vorzeichenlos oder vorzeichenbehaftet". Es ist einfach Text, und da man 
mit Text nicht rechnet, ist die "Vorzeichenbehaftetheit" des 
dahinterstehenden Datentyps egal.

> Die C-Entwickler haben einem damit ein ganz schönes Ei ins Nest gelegt,
> das jeder Programmierer ausbaden muß und sich dumm und dämlich castet.

Das ist aber doch in den meisten Sprachen so. In vielen anderen Sprachen 
kann man mit dem Zeichentyp halt nur nicht direkt rechnen, aber wenn ich 
rohe Bytes habe, muß ich die eigentlich in so gut wie jeder Sprache 
erstmal konvertieren, um sie als Text nutzen zu können. Das mußt du in C 
halt auch...

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.