Forum: Compiler & IDEs unsigend char minus unsigend char in einem IF


von Daniel B. (dbuergin)


Lesenswert?

Hallo

Einer von Euch Cracks kann mir sicher sagen, unter welchem Stichwort ich 
das folgende Probleme suchen kann:

Warum wird beim ersten Program der IF nicht wahr aber beim zweiten sehr 
wohl ?
1
#include <stdio.h>
2
3
int main(void)
4
{
5
  unsigned char day, wday;
6
7
  day  = 1;
8
  wday = 5;
9
10
  if ((day - wday) >= 25)
11
  {
12
    printf("In IF\n");
13
  }
14
}

und mit "unsigned int" anstatt "unsigned char":
1
#include <stdio.h>
2
3
int main(void)
4
{
5
  unsigned int day, wday;
6
7
  day  = 1;
8
  wday = 5;
9
10
  if ((day - wday) >= 25)
11
  {
12
    printf("In IF\n");
13
  }
14
}

Wird bei einer solchen Rechnung nicht zuerst das Resultat errechnet, 
also -4 und dieses dann über das 2'er Komplement in eine vorzeichenlose 
Zahl umgewandelt ?

Bei char also 256 - 2 = 252 und bei int (32bit) 2,147,483,647 - 4. Also 
beides sehr hohe Zahlen, und somit sollte der IF doch immer wahr werden 
?

Ich habe da wohl noch etwas nicht begriffen ;-)

Danke für die Nachhilfe.

Daniel

Umgebung: gcc 4.7.2 unter 32bit Linux und gcc 4.6.1 für ARM-Cortex M4

von (prx) A. K. (prx)


Lesenswert?

uchar - uchar => int - int => int
uint - uint => uint

Kleiner als in int wird in C nicht gerechnet, sondern zu int erweitert 
und dann gerechnet.

von Daniel B. (dbuergin)


Lesenswert?

Und scheinbar: ushort - ushort auch int ?, denn hier wird der if auch 
nicht wahr.

In welcher Lektüre finde ich das, meine C-Bücher schweigen sich hier aus 
(oder ich finde es nicht darin...)?

von (prx) A. K. (prx)


Lesenswert?

Daniel B. schrieb:
> Und scheinbar: ushort - ushort auch int ?

Nur wenn short < int, also üblicherweise 32-Bit aufwärts.

von (prx) A. K. (prx)


Lesenswert?

Daniel B. schrieb:
> In welcher Lektüre finde ich das, meine C-Bücher schweigen sich hier aus
> (oder ich finde es nicht darin...)?

Garantiert Letzteres. Oder die Bücher taugen nur für den Kachelofen.

von Daniel B. (dbuergin)


Lesenswert?

Ok, dann suche ich mal weiter ;-)

Besten Dank

von (prx) A. K. (prx)


Lesenswert?

Es gibt haufenweise Doku im Web, unter "c usual arithmetic conversions". 
Wie das üblicherweise auf Deutsch genannt wird weiss grad ich nicht.

von Daniel B. (dbuergin)


Lesenswert?

English passt auch, danke und Deine erweiterte erste Anwort erklärt 
alles.

Somit ist also ein solcher Vergleich mit uchar völlig korrekt, auch wenn 
er sich einem nicht so erfahrenen C-Programmierer nicht auf Anhieb 
erschliesst ? Eigentlich wäre da ein Verwenden von int angebrachter, 
braucht aber natürlich auch mehr Speicher.

von Karl H. (kbuchegg)


Lesenswert?

In eneglischsprachiger Literatur könnte man im Stichwortregister auch 
mal unter "Integer Promotion" bzw. nach "Promotion rules" suchen.

Und ja. Die sind wichtig. Ein Buch, in dem die nicht vorkommen, taugt 
nicht viel.

von Matthias L. (Gast)


Lesenswert?

>Eigentlich wäre da ein Verwenden von int angebrachter,
>braucht aber natürlich auch mehr Speicher.

Oder mit einer u8 Hilfsvarable. Ich bin letzten auchdarüber gestolpert..

von Karl H. (kbuchegg)


Lesenswert?

Daniel B. schrieb:
> English passt auch, danke und Deine erweiterte erste Anwort erklärt
> alles.
>
> Somit ist also ein solcher Vergleich mit uchar völlig korrekt

ist er.

> erschliesst ? Eigentlich wäre da ein Verwenden von int angebrachter,

Nicht wirklich.
Der Knackpunkt ist ein anderer.

Der Knackpunkt besteht darin, dass bei der Promotion ein niedrigerer 
Datentyp in einen höheren Datentyp gewandelt wird, SOFERN dieser alle 
darstellbaren Werte des niedrigeren Datentyps ebenfalls darstellen kann.

Du hast einen unsigned char. Der wird für die Rechnerei implizit auf 
einen int hochgecastet, WEIL ein int alle Werte eines unsigned char 
(0..255) aufnehmen kann. Und über diesen Umweg kriegen die Werte wieder 
ein Vorzeichen, welches dann in die Berechnung eingeht.
Wären die Werte von vorneherein schon ein unsigned int gewesen, dann 
würde dieser Schritt nicht passieren.

Daher:

  if (((unsigned int)day - wday) >= 25)

und dein Vergleich funktioniert wieder, wie gewünscht.

Das 'Problem' an dieser Stelle ist der Vergleich, der das Vorzeichen 
mitberücksichtigt. Hättest du dieses hier gemacht

   unsigned char diff = day - week;

dann würde zwar konzeptionell wieder dieselbe Promotion ablaufen, aber 
da das Ergebnis wieder einem unsigned char zugewiesen wird, verflüchtigt 
sich das zwischenzeitliche Vorzeichen wieder ohne Schaden zu 
hinterlassen

   if( diff >= 25 )

funktioniert dann wie erwartet.

von Daniel B. (dbuergin)


Lesenswert?

Die "Promotion rules" habe ich sogar gefunden, aber wohl gerade die 
falsche Seite, oder sie nicht begriffen (wohl auch hier letzteres...)

"http://de.wikipedia.org/wiki/Promotion_(Typumwandlung)";

Dort wird uchar -> ushort -> uint und da war ich wieder bei meinem 
Problem.

Aber danke Jungs, super Erklärungen.

Btw, das Codeteil ist aus der Sommerzeitroutine von Peter Dannegger, und 
ein Crack wie er weiss natürlich was er macht, mir hat es sich aber beim 
Studium des Codes nicht erschlossen.

von Rolf M. (rmagnus)


Lesenswert?

Daniel B. schrieb:
> Die "Promotion rules" habe ich sogar gefunden, aber wohl gerade die
> falsche Seite, oder sie nicht begriffen (wohl auch hier letzteres...)
>
> "http://de.wikipedia.org/wiki/Promotion_(Typumwandlung)";
>
> Dort wird uchar -> ushort -> uint und da war ich wieder bei meinem
> Problem.

Das erscheint mir falsch. Außerdem verstehe ich nicht, warum die 
Promotion hier in zwei Schritten ablaufen sollte. In der C99-Definition 
steht:

***********************************************************************
The following may be used in an expression wherever an int or unsigned 
int may be used:
— An object or expression with an integer type whose integer conversion 
rank is less than the rank of int and unsigned int.
— A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type, the value is 
converted to an int; otherwise, it is converted to an unsigned int. 
These are called the integer promotions.
***********************************************************************

und:

***********************************************************************
— The rank of long long int shall be greater than the rank of long int, 
which shall be greater than the rank of int, which shall be greater than 
the rank of short int, which shall be greater than the rank of signed 
char.
— The rank of any unsigned integer type shall equal the rank of the 
corresponding signed integer type, if any.
***********************************************************************

Also mit anderen Worten: Alles, was kleiner ist, als int, wird erstmal 
zu int promoted, sofern es reinpaßt.

von Peter D. (peda)


Lesenswert?

Daniel B. schrieb:
> Btw, das Codeteil ist aus der Sommerzeitroutine von Peter Dannegger, und
> ein Crack wie er weiss natürlich was er macht, mir hat es sich aber beim
> Studium des Codes nicht erschlossen.

Die int-Promotion ist hier natürlich richtig, da ja die Umschaltung 
frühestens am 25.3. erfolgen darf.
Beim unsigned Vergleich könnte ja schon am 1.3. Sommerzeit sein.

So ist es besser verstehbar:
1
int main(void)
2
{
3
  uint8_t day, wday;
4
5
  day  = 1;
6
  wday = 5;
7
8
  if ((int8_t)(day - wday) >= 25)
9
  {
10
    printf("Sommerzeit\n");
11
  }
12
}

von Daniel B. (dbuergin)


Lesenswert?

Hehe, was meinst Du warum ich auf das Ganze gekommen bin ;-)

Meine neue grosse NTP-gesteuerte 7-Segment-LED Uhr an der Wand hat 
sauber am 1 und 2. März auf Sommerzeit gestellt.
Also Code untersucht, und rausgefunden, dass ich meinen, zuerst selber 
entwickelten Code mit Teilen von Deinem "Time.c" ergänzt habe, dabei 
aber versehentlich uint Variablen verwendet habe...

Sehr schön, habe ich wieder mal was gelernt.

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.