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?
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?
> 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
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;
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.
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. ;-)
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.
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...
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
intmain(void){
2
signedints;
3
unsignedintu;
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
return0;
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.
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.
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.
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.
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.
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.
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>
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.
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.
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.
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
typedefunsignedshortuint16;
sondern so
1
typedefuint16_tuint16;
deklarieren. Ich hatte vor kurzem mit Code zu tun, wo jemand
1
typedefunsignedlonguint32;
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
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
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).
@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.
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.