Forum: PC-Programmierung Kurzform von "if" bzw signed/unsigned Problem


von StinkyWinky (Gast)


Lesenswert?

Folgender Code (Auszug):
1
  typedef signed short       int16;
2
  typedef signed int         int32;
3
  typedef unsigned short     uint16;
4
  typedef unsigned int       uint32;
5
6
  #define SIGN_SIGNED 1
7
8
  uint32 tPar;
9
  uint32 tSign;
10
  int32 tTmp;
11
12
  tTmp = ((tSign == SIGN_SIGNED)? ((int16)tPar):tPar);   // Zeile 1626
Warnung vom GCC: file.c|1626|warning: signed and unsigned type in 
conditional expression

Wenn ich jedoch die ausführliche Schreibweise von "if" verwende, erhalte 
ich keine Warnung:
1
    if (tSign == SIGN_SIGNED)
2
    {
3
      tTmp = (int16)tPar;
4
    }
5
    else
6
    {
7
      tTmp = tPar;
8
    }

Wie müsste denn die Kurzform richtig lauten, damit es keine Warnung 
gibt?

von ... (Gast)


Lesenswert?

Leiß doch mal wie Warnung.  Er meckert weil in einem Ausfürhrungsfall 
signed und im anderen Ausfürhungsfall unsigned zugewiesen wird.

Andere Frage: warum castest du nach int16 und nicht nach uint16?
tPar ist unsigned. Hinten weißt du also unsigned zu und vorne castest du 
explizit zu einem signed-wert.
In verbindung mit der warnung.. wotzu der Forumpost?

von Peter S. (psavr)


Lesenswert?

> typedef signed short       int16;
> typedef signed int         int32;
> typedef unsigned short     uint16;
> typedef unsigned int       uint32;

Gewöhne Dir unbedingt ab, eigne Dateitypen zu definieren und verwende 
stattdessen die Typen aus <stdint.h>

int8_t, int16_t, int32_t, uint8_t, uint16_t, uint32_t

von (prx) A. K. (prx)


Lesenswert?

Peter S. schrieb:

> Gewöhne Dir unbedingt ab, eigne Dateitypen zu definieren und verwende
> stattdessen die Typen aus <stdint.h>

Zumindest ergibt es wenig Sinn, grad diese Typen zu definieren, weil 
redundant und nicht portabel. Aber gegen die zentrale Definition eigener 
Datentypen für konkrete Inhalte spricht nichts, wie beispielsweise sowas 
wie
   typedef uint32_t timestamp_t;

von Sven P. (Gast)


Lesenswert?

A. K. schrieb:
> wie beispielsweise sowas wie
>    typedef uint32_t timestamp_t;

Vorallem gewöhne dir an, vorher z.B. in Manpages zu schauen, denn dort 
gibt es einen standardisierten time_t :-P

Zum Problem: Ein Ausdruck hat einen eindeutigen Typen. Dieser 
?:-Operator funktioniert als Ausdruck, der den jeweils gewählten Wert 
hat. Da die beiden Möglichkeiten bei dir aber verschiedene Typen haben, 
kann/will sich der Übersetzer nicht für einen entscheiden und warnt 
dich.

Im zweiten Beispiel passiert schlimmstenfalls ein indirekter Cast.

von (prx) A. K. (prx)


Lesenswert?

Sven P. schrieb:

> Vorallem gewöhne dir an, vorher z.B. in Manpages zu schauen, denn dort
> gibt es einen standardisierten time_t :-P

Und deshalb bin ich gezwungen für einen damit in keiner Weise 
zusammenhängenden Datentyp unbedingt time_t zu verwenden?

Ausserdem solltest du dir angewöhnen, in Manpages zu schauen, denn 
time_t hat ein Vorzeichen und dieser hier hatte mit Absicht keines. ;-)

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


Lesenswert?

A. K. schrieb:
> Ausserdem solltest du dir angewöhnen, in Manpages zu schauen, denn
> time_t hat ein Vorzeichen und dieser hier hatte mit Absicht keines. ;-)

Da hast du aber jetzt ein ganz konkretes System im Auge.  Die Single
UNIX Specification (V3) sagt über time_t lediglich aus:

"time_t and clock_t shall be integer or real-floating types."

Eine Aussage über ein fehlendes oder vorhandenes Vorzeichen ist damit
ausdrücklich nicht impliziert.

von Sven P. (Gast)


Lesenswert?

Klugscheißen? Ok :-D

A. K. schrieb:
> Sven P. schrieb:
>
>> Vorallem gewöhne dir an, vorher z.B. in Manpages zu schauen, denn dort
>> gibt es einen standardisierten time_t :-P
>
> Und deshalb bin ich gezwungen für einen damit in keiner Weise
> zusammenhängenden Datentyp unbedingt time_t zu verwenden?
Das ging aber aus dem Kontext nicht hervor. Du schriebst Timestamp, und 
genau das ist time_t.

> Ausserdem solltest du dir angewöhnen, in Manpages zu schauen, denn
> time_t hat ein Vorzeichen und dieser hier hatte mit Absicht keines. ;-)
Siehe Kontext.
Über das Vorzeichen von time_t kann man darüber nichts sagen, denn es 
ist nach ISO/IEC 9899:TC2 schlicht implementation-defined.

Ansonsten schlechtes Beispiel gewählt, tse tse tse. Kein Wunder, dass C 
so einen schlechten Ruf hat...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Zum ursprünglichen Thema:

Es gab zu diesem Thema mal einen Bug-Report, der aber abgelehnt wurde:

  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=28488#c0

Dass der Compiler in den Beispielen des Threaderöffners mal eine Warnung
ausgibt und mal nicht, liegt daran, dass wir es mit drei verschiedenen
Operationen zu tun haben, bei denen a und b unterschiedliche Signedness
haben:

- Conditional Expression: (irgendwas) : a ? b
- Equality Expression:    a == b
- Assignment Expression:  a = b

Wenn in diesen Ausdrücken a und b nicht kompatibel sind, kann eine
Warnung ausgegeben werden, muss aber nicht. Das Mischen von signed und
unsigned Variablen in einem Ausdruck ist ja schließlich kein Fehler,
sondern höchstens vielleicht nicht das, was der Programmierer beabsich-
tigt hat. Die Regeln, wie bei gemischten Operanden diese automatisch
konvertiert werden, sind im C-Standard klar festgelegt (Usual Arithmetic
Conversions). Von daher liegt es im Ermessen der Compiler-Entwickler, wo
eine Warnung sinnvoll ist und wo nicht.

Ich habe mal verschiedene Operationen, die Kandidaten für Warnungen sein
könnten, getestet (mit std=c99 -Wall -Wextra) und die Compiler-Meldung
jeweils dahinter geschrieben:
1
int main(void) {
2
  signed   int s;
3
  unsigned int u;
4
5
  u = s;     // -
6
  u = -1;    // -
7
  s = u;     // -
8
  u - s;     // -
9
  u * s;     // -
10
  u == s;    // comparison between signed and unsigned integer expressions
11
  u == 1;    // -
12
  u == -1;   // comparison between signed and unsigned integer expressions
13
  u < s;     // comparison between signed and unsigned integer expressions
14
  u < 1;     // -
15
  u < 0;     // comparison of unsigned expression < 0 is always false
16
  u < -1;    // comparison between signed and unsigned integer expressions
17
  1 ? u : s; // signed and unsigned type in conditional expression
18
19
  return 0;
20
}

So ganz kann ich auch nicht nachvollziehen, warum nur in einigen dieser
Fälle Warnungen ausgegeben werden. Erst dachte ich, die Besonderheit bei
1?u:s käme vielleicht aus der C++-Ecke, da dort der Typ eines Ausdrucks
darüber entscheidet, welche von mehreren überladenen Funktionen aufgeru-
fen wird. Interessanterweise gibt der GCC aber gerade bei C++-Programmen
diese Warnung nicht aus.

von Vlad T. (vlad_tepesch)


Lesenswert?

Yalu X. schrieb:
> u < 0;     // comparison of unsigned expression < 0 is always false
> u < -1;    // comparison between signed and unsigned integer expressions

ich finde die 2 unterschiedlichen Warnungen komisch.

von Rolf M. (rmagnus)


Lesenswert?

Vlad Tepesch schrieb:
> Yalu X. schrieb:
>> u < 0;     // comparison of unsigned expression < 0 is always false
>> u < -1;    // comparison between signed and unsigned integer expressions
>
> ich finde die 2 unterschiedlichen Warnungen komisch.

Warum? Es sind ja auch zwei unterschiedliche Probleme.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vlad Tepesch schrieb:
> Yalu X. schrieb:
>> u < 0;     // comparison of unsigned expression < 0 is always false
>> u < -1;    // comparison between signed and unsigned integer expressions
>
> ich finde die 2 unterschiedlichen Warnungen komisch.

Naja, im ersten Fall kann der Compiler die signed 0 problemlos in eine
unsigned 0 konvertieren, wie es de Standard in diesem Fall vorsieht.
Dann stellt er intelligenterweise fest, dass der Vergleich trivial ist.

Im zweiten Fall hapert es schon bei der Konvertierung. Der Compiler wird
die -1 in UINT_MAX konvertieren, was dem Vergleich natürlich ein völlig
anderes Gesicht gibt. Davor warnt uns der Compiler.

von Sven P. (Gast)


Lesenswert?

Yalu X. schrieb:
> Das Mischen von signed und
> unsigned Variablen in einem Ausdruck ist ja schließlich kein Fehler,
> sondern höchstens vielleicht nicht das, was der Programmierer beabsich-
> tigt hat.
Darauf wollte ich hinaus.

Strikt nach Standard verhält sich der GCC nicht ganz konform denke ich. 
Der Standard schreibt für den ?:-Operator explizit vor, dass aus den 
beiden letzten Ausdrücken des Operators (sofern es sich um arithmetische 
handelt) der Ergebnistyp nach den 'Usual Conversions' gewonnen wird.

Hier sind uint32_t und int16_t in den Ausdrücken, das müsste dann zu 
uint32_t werden.

von Rolf M. (rmagnus)


Lesenswert?

Sven P. schrieb:
> Hier sind uint32_t und int16_t in den Ausdrücken, das müsste dann zu
> uint32_t werden.

Wird es das denn nicht?

von Sven P. (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Sven P. schrieb:
>> Hier sind uint32_t und int16_t in den Ausdrücken, das müsste dann zu
>> uint32_t werden.
>
> Wird es das denn nicht?

Ich hoffe doch, dass es das wird. Aber die Warnung des Übersetzers ist 
halt unter diesem Gesichtspunkt nicht ganz nachvollziehbar, da nichts 
irgendwie Undefiniertes passiert.

von StinkyWinky (Gast)


Lesenswert?

Danke für die fundierten Antworten!

D.h. es ist wohl angebracht, die ausführliche if-Abfrage zu verwenden, 
um die Warnung los zu werden.

<OT>
Die Typdefinition uint16 entstand, da meine Uralt Toolchain (HiCross 
Compiler für HC11) die Typen uint16_t nicht kannte. Abgesehen davon 
stört mich das unnütze "_t"
</OT>

von Yalu X. (yalu) (Moderator)


Lesenswert?

StinkyWinky schrieb:
> D.h. es ist wohl angebracht, die ausführliche if-Abfrage zu verwenden,
> um die Warnung los zu werden.

Nein, keinesfalls. Du musst nur dafür sorgen, dass der zweite und dritte
Operand von ?: vom gleichen Typ sind oder zumindest die gleiche Signed-
ness haben, notfalls mit einem expliziten Cast.

von Rolf M. (rmagnus)


Lesenswert?

Sven P. schrieb:
> Rolf Magnus schrieb:
>> Sven P. schrieb:
>>> Hier sind uint32_t und int16_t in den Ausdrücken, das müsste dann zu
>>> uint32_t werden.
>>
>> Wird es das denn nicht?
>
> Ich hoffe doch, dass es das wird. Aber die Warnung des Übersetzers ist
> halt unter diesem Gesichtspunkt nicht ganz nachvollziehbar, da nichts
> irgendwie Undefiniertes passiert.

Der Compiler warnt an vielen Stellen, an denen etwas passiert, das zwar 
genau definiert, aber mit einigermaßen hoher Wahrscheinlichkeit vom 
Programmierer so nicht gedacht war.
Jedenfalls stimmt diese Aussage in dem Bezug nicht:

Sven P. schrieb:
> Strikt nach Standard verhält sich der GCC nicht ganz konform denke ich.

Es gibt dort nirgends etwas, das dem Compiler verbietet, an dieser 
Stelle (oder allgemein irgendeiner Stelle) eine Warnung auszugeben.

von StinkyWinky (Gast)


Lesenswert?

Achso:
1
  tTmp = ((tSign == SIGN_SIGNED)? (uint32)((int16)tPar):tPar);
Wird ohne Warnung übersetzt.

Das Problem: tPar (uint32_t) enthält einen 16-Bit Wert, der entweder 
signed oder unsigned sein kann, angezeigt von tSign.
Wenn tPar signed ist, soll er auf 32-Bit aufgeblasen werden 
(sign-extension), daher der Cast auf int16_t, das Resultat muss aber in 
tTmp (uint32_t) gespeichert werden.
Falls tPar unsigned ist, direkt in tTmp speichern.

von Yalu X. (yalu) (Moderator)


Lesenswert?

StinkyWinky schrieb:
> Die Typdefinition uint16 entstand, da meine Uralt Toolchain (HiCross
> Compiler für HC11) die Typen uint16_t nicht kannte.

Klar, in Urzeiten gab es uint16_t u.ä. noch nicht, diese Typen kamen
erst mit C99.

> Abgesehen davon stört mich das unnütze "_t"

Dann solltest du die Typen aber wenigstens nicht so
1
typedef unsigned short     uint16;

sondern so
1
typedef uint16_t     uint16;

deklarieren. Ich hatte vor kurzem mit Code zu tun, wo jemand
1
typedef unsigned long     uint32;

deklariert und mit diesem Datentyp absichtlich Überläufe genutzt hat, um
damit Modulo-2³²-Arithmetik zu machen. Nachdem das Programm auf einem
64-Bit-Linux-System kompiliert wurde, funktionierte es nicht mehr, weil
dort ein long 64 Bit hat. Mit
1
typedef uint32_t     uint32;

wäre das nicht passiert.

von Yalu X. (yalu) (Moderator)


Lesenswert?

StinkyWinky schrieb:
> Das Problem: tPar (uint32_t) enthält einen 16-Bit Wert, der entweder
> signed oder unsigned sein kann, angezeigt von tSign.
> Wenn tPar signed ist, soll er auf 32-Bit aufgeblasen werden
> (sign-extension), daher der Cast auf int16_t, das Resultat muss aber in
> tTmp (uint32_t) gespeichert werden.

Ah, jetzt habe ich verstanden, was du vorhast. Damit das auch aus dem
Quellcode klar wird, würde ich statt
1
  tTmp = ((tSign == SIGN_SIGNED)? (uint32)((int16)tPar):tPar);

schreiben:
1
  tTmp = tSign == SIGN_SIGNED ? (uint32)(int32)(int16)tPar : tPar;

Auch wenn's hässlich aussieht, drückst du damit aus, dass

- von der 32-Bit-Variable tPar nur die niederwertigen 16 Bit als
  vorzeichenbehaftete Zahl betrachtet werden soll (int16-Cast),

- diese 16-Bit-Zahl vorzeichenrichtig auf 32 Bit erweitert werden soll
  (int32-Cast)

- und das Ergebnis in eine vorzeichenlose Zahl umgewandelt werden soll
  (uint32-Cast).

von Rolf M. (rmagnus)


Lesenswert?

StinkyWinky schrieb:
> das Resultat muss aber in tTmp (uint32_t) gespeichert werden.

tTmp ist aber vom Typ int32_t.

von StinkyWinky (Gast)


Lesenswert?

@Rolf
Danke für den Hinweis. Da scheint im "echten" Code noch etwas faul zu 
sein, bzw. ich verstehe selber noch nicht ganz, was der Erfinder 
beabsichtigt hat.

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


Lesenswert?

StinkyWinky schrieb:
> Die Typdefinition uint16 entstand, da meine Uralt Toolchain (HiCross
> Compiler für HC11) die Typen uint16_t nicht kannte. Abgesehen davon
> stört mich das unnütze "_t"

Das _t ist ganz nützlich für das Syntaxhighlighting der Editoren.
Es ist halt eine Konvention, die in den einschlägigen Standards
(sowohl C als auch Single UNIX Specification, vormals POSIX) sehr
häufig zur Kennzeichnung einer Typdefinition benutzt wird.

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.