Forum: Compiler & IDEs Vergleich von signed und unsigned


von Schmitti (Gast)


Lesenswert?

Hallo zusammen,

hab mal eine kurze Frage.

Wie sieht die implizierte Typumwandlung nach dem Standard bei folgendem 
Konstrukt aus?
1
uint8_t var1;
2
int8_t var2;
3
4
if(var1 == var2)
5
{
6
...
7
}
An welcher Stelle findet man sowas im Standard?

Danke im Voraus

Schmitti

von (prx) A. K. (prx)


Lesenswert?

Zu finden unter "integer promotion rules".

Hier: Da beide Seiten in ein "int" passen, wird das Ergebnis so 
ausfallen, als würden beide Seiten in "int" umgewandelt.

In diesem Fall eines einfachen "==" ohne Rechnung links wie rechts ist 
das allerdings einerlei.

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


Lesenswert?

A. K. schrieb:

> In diesem Fall eines einfachen "==" ohne Rechnung links wie rechts ist
> das allerdings einerlei.

Nein, ist es nicht.  var2 wird vorzeichenerweitert in einen int
gewandelt, var1 nicht (weil sie unsigned ist).

6.3 Conversions
6.3.1 Arithmetic operands
6.3.1.1 Boolean, characters, and integers

(Ziemlich abstrakte Abhandlung von "rank", am Ende:)

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. [...]

Damit ist klar, dass var1 als Bitmuster in den int-Wert übernommen
wird, denn int kann alle Werte eines uint8_t abbilden.  Damit ist
auch klar, dass var2 sign-extended übernommen werden muss, denn
ein Wert von bspw. -1 (0xFF als int8_t) muss weiterhin -1 bleiben
(0xFFFF als int).

Auch noch interessant in dem Zusammenhang, auch wenn es den Fall
hier nicht direkt berührt:

6.3.1.3 Signed and unsigned integers

von (prx) A. K. (prx)


Lesenswert?

Jörg Wunsch schrieb:

> Nein, ist es nicht.  var2 wird vorzeichenerweitert in einen int
> gewandelt, var1 nicht (weil sie unsigned ist).

Stimmt. Genauer gesagt, var2 wird auch in ein "int" gewandelt, aber aber 
ohne Vorzeichen, also als positive Zahl verstanden.

Ich hatte da allerdings eher Spielchen wie
   var1 == var2 + 1
im Auge gehabt, bei denen in 8- und 16-Bit Rechnung durchaus 
verschiedene Ergebnisse rauskommen.

von Karl H. (kbuchegg)


Lesenswert?

Du brauchst gar nicht so weit zu gehen :-)
1
#include <stdio.h>
2
3
int main()
4
{
5
  uint8_t i;
6
  
7
  i = -1;
8
  if( i == -1 )
9
    printf( "stimmt\n" );
10
  else
11
    printf( "stimmt nicht\n" );
12
}

Dieses Programm gibt (korrekterweise) "stimmt nicht" aus. Und das kann 
einen Nichtwissenden ganz schön in den Wahnsinn treiben :-)

von Schmitti (Gast)


Lesenswert?

Herzlichen Dank für die schnelle Antwort.

War genau das was ich gesucht habe!!!

von ..... (Gast)


Lesenswert?

Hallo,

ich hätte da auch mal eine Frage.

Ich habe in vielen uC-Programmen gesehen dass unsigned int verwendet 
wird.

Wieso spielt es eine so große Rolle ob ich int oder unsigned int 
verwende.

MfG

von (prx) A. K. (prx)


Lesenswert?

..... schrieb:

> Wieso spielt es eine so große Rolle ob ich int oder unsigned int
> verwende.

Einige 8-Bit Mikrocontroller tun sich mit diversen vorzeichenbehafteten 
Operationen wie Vergleichen und Rechtsschieben etwas schwer (z.B. 8051 
und PIC).

von klaus (Gast)


Lesenswert?

Ich versuche gerade die Integer Promotion Regeln abzuleiten. Ist 
folgendes richtig ?

Für einen Typ T in einem Ausdruck gilt:
(1) Wenn sizeof(T) >= sizeof(int): Keine Änderung von T
(2) Wenn sizeof(T) < sizeof(int):
    (a) unsigned(T): T wird zu "unsigned int" promoted
    (b) signed(T):   T wird zu "int" promoted


Ist dies vollständig und deckt auch alle Regeln ab?
Viele Grüße
Klaus

von (prx) A. K. (prx)


Lesenswert?

klaus schrieb:

> (1) Wenn sizeof(T) >= sizeof(int): Keine Änderung von T

Bedenke, dass Operatoren auch mal 2 Operanden haben.

> (2) Wenn sizeof(T) < sizeof(int):
>     (a) unsigned(T): T wird zu "unsigned int" promoted
>     (b) signed(T):   T wird zu "int" promoted

(2) Wenn sizeof(T) < sizeof(int): T wird zu "int" promoted

von Stefan E. (sternst)


Lesenswert?

2a stimmt nicht. Wenn T unsigned ist und sein Wertebereich komplett 
durch ein int (also signed) abgedeckt werden kann (was ja in der Regel 
der Fall ist), dann wird es zu int promotet.

Davon abgesehen spielen natürlich auch noch die Typen der restlichen 
Operanden eine Rolle. Denn für die meisten Operatoren gilt, dass alle 
Operanden den gleichen Typ haben müssen. Deshalb ist keine deiner Regeln 
allgemein in jedem Fall gültig.

von klaus (Gast)


Lesenswert?

> 2a stimmt nicht. Wenn T unsigned ist und und sein Wertebereich komplett
> durch ein int (also signed) abgedeckt werden kann (was ja in der Regel
> der Fall ist), dann wird es zu int promotet

Nach meinem Verständnis geht das Gegenteil aus dem obigen Beispiel 
hervor:
1
int main()
2
{
3
  uint8_t i;
4
  
5
  i = -1;
6
  if( i == -1 )
7
    printf( "stimmt\n" );
8
  else
9
    printf( "stimmt nicht\n" );
10
}

Wenn hier i zu einem "int" promoted würde, also signed wäre, gäbe es ja 
hier keine Überraschungen, da es vorzeichenrichtig erweitert würde (also 
-1 wäre). Im anderen Fall würde es von 0xFF zu 0x000000FF erweitert und 
mit 0xFFFFFFFF verglichen (bei sizeof(int) = 4).

von (prx) A. K. (prx)


Lesenswert?

klaus schrieb:

> Wenn hier i zu einem "int" promoted würde, also signed wäre, gäbe es ja
> hier keine Überraschungen, da es vorzeichenrichtig erweitert würde

"i" ist vorzeichenlos, dessen Wertebereich von 0..255 lässt sich durch
"int" abdecken. Die Erweiterung selbst ist vorzeichenlos, aber nicht das 
Ergebnis der Erweiterung. Also kommt sehr wohl
  if (255 == -1)
dabei raus.

von Stefan E. (sternst)


Lesenswert?

klaus schrieb:

> Wenn hier i zu einem "int" promoted würde, also signed wäre, gäbe es ja
> hier keine Überraschungen, da es vorzeichenrichtig erweitert würde

i ist unsigned, enthält also eigentlich 255 (und eben nicht -1) und wird 
daher vorzeichenrichtig zu 0x00FF erweitert, also ebenfalls 255.

von (prx) A. K. (prx)


Lesenswert?

Tip: Rein formal müsste ein Compiler bei
   i = -1;
eine Warnung ausspucken, weil der Operand nicht ins Ziel passt. 
Allerdings würden viele bestehenden Programme in Warnungen absaufen, 
weshalb sowas rasch wieder rausfliegt oder abgeschaltet wird.

von Karl H. (kbuchegg)


Lesenswert?

> Wenn hier i zu einem "int" promoted würde, also signed wäre,

i ist aber nicht signed. i ist unsigned

> gäbe es ja hier keine Überraschungen, da es vorzeichenrichtig
> erweitert würde (also -1 wäre).

Der Dreh besteht ja gerade darin, dass i eben nicht signed ist. i 
enthält 255 (wenn wir mal 2-er Komplement annehmen)

Du machst den Fehler, der interessanterweise so häufig gemacht wird: Der 
Zieldatentyp hat mit der Auswahl der Operation nichts zu tun. Die 
Promotion ist hier von unsigned char nach int. Wie diese Promotion 
gemacht wird (ob Vorzeichen berücksichtigt werden muss oder nicht) hängt 
einzig und alleine vom Ausgangsdatentyp ab.

Das ist eine der Grundregeln von C. Bei der Auswahl der Operation spielt 
der Zieldatentyp nur insofern eine Rolle als er das anzustrebende Ziel 
vorgibt. Aber die Details der Operation werden immer nur vom 
"Ausgangsmaterial" abgenommen. Insofern kann man sagen: Der Zieldatentyp 
spielt bei der Auswahl der Operation keine Rolle. (Für eine Addition 
spielt es keine Rolle wo das Ergebnis abgelegt wird)

Deine Regel kann man daher so zusammenfassen:
Alles was kleiner als int ist (egal ob signed oder unsigned) wird zu 
int.

Das ist auch eines der Probleme, warum der gcc manchmal mit sanfter 
Gewalt zu Optimierungen gezwungen werden muss und sich als 
'Universalcompiler' auf Byteebene manchmal schwer tut. Spezialcompiler 
für µC nehmen es mit dieser int-Promotion in manchen Bereichen nicht 
ganz so genau.

von klaus (Gast)


Lesenswert?

Bin heute etwas schwer von Begriff :-) Es ist doch aber schon so, dass 
anhand des Quelldatentypes entschieden wird, was auf Bitebene geschieht 
oder?

Beispiel:
1
signed char x = -1
2
unsigned char y = -1
3
4
if (x == -1) // 0xFFFFFFFF == 0xFFFFFFFF bzw. -1 == -1
5
{
6
//...
7
}
8
if (y == -1) // 0x000000FF == 0xFFFFFFFF bzw. 255 == -1
9
{
10
//...
11
}

x und y enthalten ursprünglich auf Bitebene den gleichen Wert. Aber je 
nachdem ob der Typ signed oder unsigned ist, erfolgt auf Bitebene eine 
andere Behandlung bei der Erweiterung. Denke ich zu "bitorientiert" ?

von Karl H. (kbuchegg)


Lesenswert?

klaus schrieb:

> x und y enthalten ursprünglich auf Bitebene den gleichen Wert. Aber je
> nachdem ob der Typ signed oder unsigned ist, erfolgt auf Bitebene eine
> andere Behandlung bei der Erweiterung.

Jetzt hast du es :-)

von (prx) A. K. (prx)


Lesenswert?

klaus schrieb:

> x und y enthalten ursprünglich auf Bitebene den gleichen Wert. Aber je
> nachdem ob der Typ signed oder unsigned ist, erfolgt auf Bitebene eine
> andere Behandlung bei der Erweiterung. Denke ich zu "bitorientiert" ?

Korrekt. Der Datentyp der Quelle entscheidet was geschieht, nicht der 
Typ des Ziels.

von klaus (Gast)


Lesenswert?

Okay habs geschnallt, vielen Dank! :-)

von comtronic_99 (Gast)


Lesenswert?

Die Ausgabe auf

#include <stdio.h>

int main()
{
  uint8_t i;

  i = -1;
  if( i == -1 )
    printf( "stimmt\n" );
  else
    printf( "stimmt nicht\n" );
}

scheint Compilerspezifisch zu sein ...

Bei mir wird stimmt ausgegeben :o

Das würde heissen, je nach Compiler/Entwicklungsumgebung erhalte ich bei 
gleichem Code ein anderes Ergebnis ...

von Karl H. (kbuchegg)


Lesenswert?

comtronic_99 schrieb:

> scheint Compilerspezifisch zu sein ...

an und für sich nicht. Da gibt es Regel im C-Standard, die diesen Fall 
handhaben.
Welcher Compiler ist das?
Mit welchen Flags rufst du ihn auf?

von (prx) A. K. (prx)


Lesenswert?

comtronic_99 schrieb:
> scheint Compilerspezifisch zu sein ...

Ein Compiler, der sich an den C Standard hält, darf das nicht. Es gibt 
aber C-Compiler für 8-Bit µCs, bei denen "int" 8 Bits breit sein kann. 
Dann kommt genau das dabei raus.

von Falk B. (falk)


Lesenswert?

@Karl Heinz Buchegger (kbuchegg) (Moderator)

>  uint8_t i;

>  i = -1;

Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

von (prx) A. K. (prx)


Lesenswert?

Bei GCC nur wenn explizit gefordert mit -Wconversion. Weder -Wall noch 
-Wextra enthalten das.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Falk Brunner schrieb:
>>  uint8_t i;
>
>>  i = -1;
>
> Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

Der GCC tut das auch, aber nur, wenn man die entsprechende Warnung mit
-Wconversion explizit aktiviert. Nur -Wall und sogar -Wextra sind zu
wenig. Mit -Wconversion kann man nämlich kaum existierenden C-Code
kompilieren, ohne seitenweise Warnungen zu bekommen.

Wenn man genügen Warnungsregister zieht, erhält man diese Meldungen:
1
sutest.c: In function ‘main’:
2
sutest.c:8:3: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]
3
   i = -1;
4
   ^
5
sutest.c:9:3: warning: comparison is always false due to limited range of data type [-Wtype-limits]
6
   if( i == -1 )
7
   ^

Das sollte eigentlich als Anlass ausreichen, den Code noch einmal
gründlich zu prüfen.

Edit: Beim Schreiben zu lange getrödelt :)

von RicO (Gast)


Lesenswert?

tsakamm schrieb:
>
> Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

tsakamm schrieb:
> @Karl Heinz Buchegger (kbuchegg) (Moderator)
>>  uint8_t i;
>>  i = -1;
> Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

Im Frühjahr tuts der meistens nich

von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner schrieb:
> @Karl Heinz Buchegger (kbuchegg) (Moderator)
>
>>  uint8_t i;
>
>>  i = -1;
>
> Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

Als Faustregel:

Der Compiler MUSS Errors ausgeben. Die sind vom C-Standard gefordert.
Was er darüber hinaus anwarnt oder nicht anwarnt, entscheidet der 
Compilerbauer.

von zitt (Gast)


Lesenswert?

Gibt's denn sowas wie den acid Test auch für C-Compiler ?

von Karl H. (kbuchegg)


Lesenswert?

Google
"c compiler conformance testing"

zb
http://www.peren.com/pages/cvsa_set.htm

von Andy K. (notandy)


Lesenswert?


von Stefan R. (srand)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Als Faustregel:
>
> Der Compiler MUSS Errors ausgeben. Die sind vom C-Standard gefordert.
> Was er darüber hinaus anwarnt oder nicht anwarnt, entscheidet der
> Compilerbauer.

Ohne es nachzuschauen: Mir war so, daß die Unterscheidung im Standard 
gar nicht existiert, und das alles nur "diagnostics" sind.

Die Unterteilung in "error - bricht ab" und "warning - compiliert 
weiter" wäre dann Implementierungssache.

Ist dem nicht so?

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


Lesenswert?

Stefan Rand schrieb:
> Die Unterteilung in "error - bricht ab" und "warning - compiliert
> weiter" wäre dann Implementierungssache.

Jein.

“The intent is that an implementation should identify the nature of,
and where possible localize, each violation. Of course, an
implementation is free to produce any number of diagnostics as long as
a valid program is still correctly translated. It may also
successfully translate an invalid program.”

Also letztlich ist die einzige Forderung, dass ein gültiger Quelltext
sauber compiliert wird.

Lediglich für die Direktive #error ist zwingend ein Abbruch der
Übersetzung mit Fehlerstatus gefordert.

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.