www.mikrocontroller.net

Forum: Compiler & IDEs Vergleich von signed und unsigned


Autor: Schmitti (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

hab mal eine kurze Frage.

Wie sieht die implizierte Typumwandlung nach dem Standard bei folgendem 
Konstrukt aus?
uint8_t var1;
int8_t var2;

if(var1 == var2)
{
...
}
An welcher Stelle findet man sowas im Standard?

Danke im Voraus

Schmitti

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du brauchst gar nicht so weit zu gehen :-)
#include <stdio.h>

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

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

Autor: Schmitti (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Herzlichen Dank für die schnelle Antwort.

War genau das was ich gesucht habe!!!

Autor: ..... (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
int main()
{
  uint8_t i;
  
  i = -1;
  if( i == -1 )
    printf( "stimmt\n" );
  else
    printf( "stimmt nicht\n" );
}

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).

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
signed char x = -1
unsigned char y = -1

if (x == -1) // 0xFFFFFFFF == 0xFFFFFFFF bzw. -1 == -1
{
//...
}
if (y == -1) // 0x000000FF == 0xFFFFFFFF bzw. 255 == -1
{
//...
}

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" ?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay habs geschnallt, vielen Dank! :-)

Autor: comtronic_99 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karl Heinz Buchegger (kbuchegg) (Moderator)

>  uint8_t i;

>  i = -1;

Hier muss der Compiler aber MINDESTENS ne Warung ausgeben.

Autor: A. K. (prx)
Datum:

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

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
sutest.c: In function ‘main’:
sutest.c:8:3: warning: negative integer implicitly converted to unsigned type [-Wsign-conversion]
   i = -1;
   ^
sutest.c:9:3: warning: comparison is always false due to limited range of data type [-Wtype-limits]
   if( i == -1 )
   ^

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

Edit: Beim Schreiben zu lange getrödelt :)

Autor: RicO (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: zitt (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Google
"c compiler conformance testing"

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

Autor: Andy K. (notandy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Stefan Rand (srand)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.