PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : -1 ist großer als 3 ?



Siro
21.01.2011, 12:22
ich habe eine merkwürdige Erscheinung :-)

Mein Hauptprogramm verzweigt in den "if" Zweig und mir ist eigentlich nicht klar warum. Ich weis wie ich den Fehler beheben kann, möchte aber gern verstehen warum ich das tun muss.
wenn ich die Abfrage wie folgt ändere geht es:

if (index >= (int)(sizeof(menue) / sizeof(TMenuStruct)))

ich muss also eine explizite Typenwandlung vornehmen.
Ich dachte ohne spezielle Angaben wird immer ein int angenommen...



typedef struct
{
int a;
int b;
} TMenuStruct;


const TMenuStruct menue[]={
{ 1,1 },
{ 2,2 },
{ 3,3 }
};

int index;

int main(void)
{

index = -1;
if (index >= (sizeof(menue) / sizeof(TMenuStruct)))
index = 0;
}


Antwort erbeten,
mfg. Siro

Babbage
21.01.2011, 13:52
Ich vermute das das die Lösung bringt:

if (index >= (int)(sizeof(menue) / sizeof(TMenuStruct)))

index ist vom Typ int und sizeof meines Wissens ein unsigned int oder char.
D.h. der Vergleich könnte als unsigned ausgeführt werden.

Bei einem 16-Bit int wird -1 Dezimal intern als 1111111111111111 Binär dargestellt.
Wird dieser Wert in einen signed umgewandelt kommt ein Wert von 65535 raus.

Babbage

sternst
21.01.2011, 14:38
Ich dachte ohne spezielle Angaben wird immer ein int angenommen...
Nö. Der Typ der rechten Seite ergibt sich aus den Typen der Operanden. Und da der Rückgabetyp von sizeof unsigned ist, ...

Siro
21.01.2011, 16:29
Den Ausdruck
(sizeof(menue) / sizeof(TMenuStruct))
kann der Preprozessor doch vorausberechnen und entsprechend eine Konstante einsetzen, in diesem Falle die 3. Wenn ich das selbst direkt im Programcode mache geht es auch einwandfrei.
Warum sollte er meine index Variable plötzlich als unsigned betrachten ???? Das gibt doch irgendwie (für mich) keinen Sinn.
Das heisst für mich soviel, daß man keinen signed mit einem unsigned korrekt vergleichen kann ohne explizite Typwandlung....
also dann auf Nummer sicher:
if ((int)(index) >= (int)((int)(sizeof(menue) / (int)sizeof(TMenuStruct))))
geht doch O:)

sternst
21.01.2011, 17:02
kann der Preprozessor doch vorausberechnen und entsprechend eine Konstante einsetzen, in diesem Falle die 3Nein, der Präprozessor berechnet keine Ausdrücke. Der Compiler macht das, aber selbstverständlich verändert er bei dem Ersetzen durch eine Konstante den Typ nicht. Und da sizeof einen unsigned Rückgabe-Typ hat, ist auch das Ergebnis der Division (und damit die rechte Seite des Vergleichs) ein unsigned Typ. Es ist völlig unerheblich, ob diese Division nun tatsächlich zur Laufzeit stattfindet, oder ob der Compiler dort direkt eine Konstante verwendet. Der Typ ist in beiden Fällen der gleiche.

Wenn ich das selbst direkt im Programcode mache geht es auch einwandfrei. In dem Fall hat die 3 aber auch einen signed Typ.


Das heisst für mich soviel, daß man keinen signed mit einem unsigned korrekt vergleichen kann ohne explizite Typwandlung....So kann man das auch wieder nicht verallgemeinern. Die Größe der Typen spielt auch noch eine Rolle. In diesem Fall würde der Vergleich zum Beispiel funktionieren:
signed long a = -1;
unsigned int b = 3;
if (a < b) ...

Babbage
21.01.2011, 17:10
Warum sollte er meine index Variable plötzlich als unsigned betrachten ???? Das gibt doch irgendwie (für mich) keinen Sinn.

Der Vergleich von einem signed und unsigned muss halt irgendwie gemacht werden.
Wie soll der Compiler dann -1 und 3 Vergleichen wenn er nur 16 Bit hat?
- er wandelt den int in unsigned int, dann knallt es bei negativen Werten im signed int
- oder er wandelt den unsigned int in einen signed um, dann knallt es bei Werten Größer 32767 im unsigned int weil das dann plötzlich negative Zahlen sind

Wie sollte da der Compiler entscheiden?
Bringt der Compiler denn eine Warnung?

sternst
21.01.2011, 17:58
Wie sollte da der Compiler entscheiden? Er braucht sich eigentlich nicht zu entscheiden. Der C-Standard regelt ganz genau, was in einem solchen Fall zu tun ist. Wenn die beiden Typen die gleiche Größe haben, sich aber in der Signedness unterscheiden, "gewinnt" immer der unsigned Typ (der signed Typ wird also in den unsigned Typ gewandelt).

Babbage
21.01.2011, 19:35
@sternst war ne rethorische Frage nachdem diese Umwandlung für Siro keinen Sinn ergab.

Siro
21.01.2011, 22:09
Ersteinmal Danke für Euer Interesse,
sternst schrieb:

"gewinnt" immer der unsigned Typ (der signed Typ wird also in den unsigned Typ gewandelt).

Meine Meinung:
Das finde ich "ABSOLUT FATAL", das muss ja schiefgehen.


weil ich habe Jahrzehntelang Assembler programmiert
und bin unweigerlich irgendwann auf selbiges Problem gestoßen,
einen Signed mit einem Unsigned zu vergleichen.
Habe mich dann dafür entschieden, sobald "ein" Signed im Spiel ist,
beide Operatoren als Signed zu interpretieren. Die Wahrscheinlichkeit
daß meine Werte unglücklicherweise im Bereich des "Vorzeichens" landen,
also bei 16 Bit > 32768 oder bei 32 Bit ..... wahr eher gering.
Wie die C-Compiler das nun auswerten, da habe ich natürlich keinen Einfluß. Ich bin nur wirklich erstaunt, daß es in meinem Beispiel schief läuft und das völlig ohne irgend ein Warning.
Na wa solls, Wichtig ist, ich habe das Problem erkannt und kann entsprechend entgegenwirken.

Was ich aber überhaupt nicht verstehe:
Wie schon erwähnt ist eine Auflösung des Codes:
sizeof(menue) / sizeof(TMenuStruct)
eine Konstante, hier wird ja keine "Funktion" aufgerufen
und es ist absolut sichergestllet, daß ein positiver Wert rauskommt.
Und genau diesen Wert soll er ja einsetzten.
Ob signed oder unsigned ist hier völlig unerheblich.
Es ist lediglich eine "Konstante". Wie kommt er dazu meinen als
signed definierten "index" als unsigned zu interpretieren ?

Okay, jetzt bin ich wieder beim Text vom "sternst"
"gewinnt" immer der unsigned Typ (der signed Typ wird also in den unsigned Typ gewandelt).

Im nächsten Leben schreibe ich meine Compiler selber.
Spass muss sein.

Siro

SprinterSB
22.01.2011, 12:20
Habe mich dann dafür entschieden, sobald "ein" Signed im Spiel ist,
beide Operatoren als Signed zu interpretieren.


Nun, die Herren und Damen, die den C-Standard festsetzen, entschieden sich eben anders...


Wie die C-Compiler das nun auswerten, da habe ich natürlich keinen Einfluß.

Man hat aber Einfluß darauf, die C-Doku zu lesen :-)


Ich bin nur wirklich erstaunt, daß es in meinem Beispiel schief läuft und das völlig ohne irgend ein Warning.

Freilich muss man Warnungen auch aktivieren und berücksichtigen. Als Optionen sind mindestend -W -Wall ratsam, und mein avr-gcc warnt die Stelle auch an.
"foo.c:7: warning: comparison between signed and unsigned"


Was ich aber überhaupt nicht verstehe:
[...] es ist absolut sichergestllet, daß ein positiver Wert rauskommt.
Und genau diesen Wert soll er ja einsetzten.
Ob signed oder unsigned ist hier völlig unerheblich.
Es ist lediglich eine "Konstante". Wie kommt er dazu meinen als
signed definierten "index" als unsigned zu interpretieren?

Der Compiler muss eben einen Vergleich ausführen, und den kann er signed oder unsigned machen. Der C-Standard ergibt, daß der Vergleich auf unsigned zu machen ist. Auf einer Maschine, wo ein int 32 Bits groß ist, könnte eine Promotion nach 32 Bits stattfinden, und das Ergebnis wäre wie von dir erwartet. Aber hier ist ein int 16 Bits groß und es wird nicht über int-Größe hinaus erweitert.

An manchen Stellen ist der C-Standard so geschrieben daß "vernünftiger" Code gut optimierbar ist. Beispiel sind etwa die Strict-Aliasing-Rules oder die etwas seltsame EIgenschaft von Shifts: Ein wert in einer Schleife 100x um 1 nach links zu schieben gibt idR was anderes, als ein << 100.


Im nächsten Leben schreibe ich meine Compiler selber.
Spass muss sein.

Dann ist der Compiler aber die falsche Baustelle für dich, zumindest wenn es ein Compiler werden soll, der sich an Standards hält. Du musst schon zur Fraktion der Standard-Schreiber wechseln :-)

Siro
24.01.2011, 16:59
Ich gebe mich geschlagen, mir fehlen da doch noch einige Informationen was "C" angeht. Also hab ich ich mal etwas gegoogelt wegen einem "vernünftigen" C-Buch in Deustch. Ich habe zwar eines in der Firma, aber alles was ich suche steht da nicht drin. Negativwerbung wollte ich aber jetzt nicht machen...
Nun meine ich ein recht gutes gefunden zu haben.
C von A bis Z von Jürgen Wolf.
Da steht unter anderem das Problem mit kombinierten signed und unsigned drin, sowie diverse andere Eigenheiten von C.
Wenn man die ersteinmal kennt, braucht man nicht immer stundenlang in seiner Software suchen. Achja, ich schaffe es nicht mit der IAR-IDE die Warningstufe oder ähnliches so einzustellen, daß ich auch eine entsprechende Warnung bekomme, wenn ich signed und unsigned mixe.
Ebsowenig bekomme ich ein Warning wenn ich einem unsigned char eine -1 zuweise. Ich glaube aber, ich bin der einzige hier im Forum der mit der IAR arbeitet, oder doch nicht ?
Siro

chientech
30.01.2011, 14:18
Hi,

@SprinterSB
ich hab mir deinen Rat gleich zu herzen nehmen wollen:


Als Optionen sind mindestend -W -Wall ratsam

aber schließt sich das nicht gegenseitig aus?
No Warnings (-w)
All Warnings (-Wall)

gruß ch

SprinterSB
30.01.2011, 19:15
Nein, schliesst sich nicht aus. Ein Großes W ist was anderes als ein kleines w :-)

chientech
05.02.2011, 07:38
@ OK ich glaub dir.

Gibt es irgendwo eine übersicht?

hab hier weder -W noch -w gefunden.
http://www.rn-wissen.de/index.php/Avr-gcc#Allgemeine_Optionen_f.C3.BCr_GCC

SprinterSB
05.02.2011, 12:18
http://gcc.gnu.org/onlinedocs/gcc-4.3.0/gcc/index.html#toc_Invoking-GCC

Eine Erklärung aller Optionen würde das Wiki sprengen; dort sind nur einige Wichtige Optionen erklärt. Es ist also keine schlechte Idee, die Dokumentation der verwendeten Software parat zu haben.