Hallo zusammen,
gibt es eine portable Vorgehensweise, meinem Compiler klarzumachen, daß
ein Integer eine positive Zahl ist, damit er keine desbezügliche Warnung
mehr abgeben muß?
Gegeben ist die folgende Konfiguration:
1
externbar(unsignedintu);
2
3
voidfoo(inti)
4
{
5
assert(i>INT_MIN);
6
i=abs(i);
7
8
// ich bin aufgrund meines Vorwissens _sehr_ sicher, dass i
9
// nicht negativ ist:
10
bar(i&~INT_MIN);
11
}
Wie oben beschrieben, habe ich dem Compiler jetzt auf brutale Weise
klargemacht, daß i > 0 ist, und er warnt mich nicht mehr.
Gibt es eine "schönere" Variante?
Viele Grüße
W.T.
Sehr schön. :-) Betragsbildung mit Ausschluss des undefinierten Falls
|INT_MIN|. Für Produktivsysteme besser was anderes als assert()
benutzen.
Danach, wie oben gesagt auf unsigned casten. Casten ist zwar idR "böse".
Es gibt aber Ausnahmen, so wie hier.
> bar(i & ~INT_MIN);
Löscht das führende Bit womit lediglich nicht-negative Werte im 2er
Komplement übrig bleiben (nach abs() also redundant).
Bin überrascht (gerade getestet), dass der gcc den Ausdruck auswertet
und als Ergebnis die Konvertierung nicht länger beanstandet
(-Wconversion).
Trotzdem, "besser" ist:
> bar((unsigned)i);
Mikro 7. schrieb:> Betragsbildung mit Ausschluss des undefinierten Falls> |INT_MIN|.
Kannst Du erläutern wie so etwas passieren könnten/ob man darauf
generell aufpassen muss?
lalala schrieb:> Kannst Du erläutern wie so etwas passieren könnten/ob man darauf> generell aufpassen muss?
abs liefert einen int und keinen unsigned int. Ich erkläre das mal mit
int == 8 Bit: Eine vorzeichenbehaftete 8 Bit Ganzzahl hat meistens einen
Wertebereich von -128 bis +127. +128 ist somit nicht darstellbar, daher
muss dieser Fall separat abgefangen werden. Dies wäre alles kein
Problem, wenn abs einen unsigned int zurückgeben würde.
Hier gibt es eine Diskussion darüber:
Beitrag "8-bit-AVR: int32 to uint32"
Hallo zusammen,
danke für die zahlreichen Antworten.
Jörg W. schrieb:> Was spricht gegen einen Cast nach unsigned int?
Erst einmal nichts. Ich habe nur irgendwann einmal gelernt "casts sind
böse - dann warnt Dich der Compiler vor nichts mehr!" Deswegen suchte
ich nach einer sanfteren Methode. Aber wenn selbst Du der Meinung bist,
daß ein Cast hier angebracht ist - dann nehme ich an, daß das hier
tatsächlich angebracht ist.
Sven B. schrieb:> uint32_t j = abs(i)?
Die Idee ist nicht so gut. Aus dem "abs" werden im Assembler-Listing
einige Zeilen, wobei doch nur der Zweck war, den Compiler zum schweigen
zu bringen. Interessanterweise ist der GCC danach allerdings wirklich
der Meinung, daß der Wert positiv ist, und warnt nicht mehr.
Mikro 7. schrieb:> Sehr schön. :-) Betragsbildung mit Ausschluss des undefinierten Falls> |INT_MIN|. Für Produktivsysteme besser was anderes als assert()> benutzen.
Der assert war eher als Schutz vor der Forenkultur gedacht. "In
Wirklichkeit" ist der Wertebereich noch weitaus eingeschränkter.
Ich bleibe dann beim cast: Bei bar( (unsigned) i); ist der Zweck ohne
Kommentar direkt ersichtlich, bei bar(i & ~INT_MIN); könnte das
irgendwann einmal zu Mißverständnissen führen.
Walter T. schrieb:> Jörg W. schrieb:>> Was spricht gegen einen Cast nach unsigned int?>> Erst einmal nichts. Ich habe nur irgendwann einmal gelernt "casts sind> böse - dann warnt Dich der Compiler vor nichts mehr!" Deswegen suchte> ich nach einer sanfteren Methode. Aber wenn selbst Du der Meinung bist,> daß ein Cast hier angebracht ist - dann nehme ich an, daß das hier> tatsächlich angebracht ist.>> Sven B. schrieb:>> uint32_t j = abs(i)?>> Die Idee ist nicht so gut. Aus dem "abs" werden im Assembler-Listing> einige Zeilen, wobei doch nur der Zweck war, den Compiler zum schweigen> zu bringen. Interessanterweise ist der GCC danach allerdings wirklich> der Meinung, daß der Wert positiv ist, und warnt nicht mehr.
Warum schreibst du dann nicht einfach
1
assert(i>=0);
wenn du eh weißt, dass der Wert größer als 0 ist? assert() ist genau
dafür da (und nicht dafür, dass die App abstürzt, wenn die Bedingung
nicht erfüllt ist -- das ist nur ein Seiteneffekt).
Walter T. schrieb:>> Was spricht gegen einen Cast nach unsigned int?>> Erst einmal nichts. Ich habe nur irgendwann einmal gelernt "casts sind> böse
Sind sie auch – wenn man sie blind überall dann anwendet, wenn der
Compiler mal 'ne Warnung schmeißt.
Wenn du aber nach dem Begutachten der Warnung zu dem Schluss gekommen
bist: „dort ist sichergestellt, dass da nichts Dummes passieren kann“,
dann ist der Cast genau das Mittel der Wahl, das einem die Entwickler
von C mit auf den Weg gegeben haben: es entsteht kein zusätzlicher
Code dadurch, und du kannst dem Compiler deinen Vorsatz kundtun.
Walter T. schrieb:> Erst einmal nichts. Ich habe nur irgendwann einmal gelernt "casts sind> böse - dann warnt Dich der Compiler vor nichts mehr!"
Dies gilt für die Nutzung von Cast ohne Sinn und Verstand.
Ein Cast dient dazu, dem Compiler zu sagen: "Ich weiß was ich tue."
Wenn das nicht gelogen ist, ist ein Cast lieb und nett.
In C++ würde man dafür einen static_cast benutzen. Der hat den Vorteil,
dass man damit nicht aus Versehen Integer in Zeiger, Zeiger in Integer,
Zeiger in inkompatible Zeiger, const in nicht-const etc. casten kann.
Walter T. schrieb:> gibt es eine portable Vorgehensweise, meinem Compiler klarzumachen, daß> ein Integer eine positive Zahl ist, damit er keine desbezügliche Warnung> mehr abgeben muß?
Selbstverständlich gibt es dafür eine Vorgehensweise.
Aber: Was willst du denn eigentlich am Ende damit bezwecken?
Mir ist der Sinn deiner ganzen Bestrebung nicht ersichtlich. Es sollte
doch nicht der Zweck darin bestehen, daß der Compiler nicht klagt,
sondern er sollte darin bestehen, daß das Programm am Ende so
funktioniert wie erwünscht.
Also wenn du eine Funktion hast, die ein Integer-Argument entgegennimmt
und ihrerseits eine andere Funktion aufruft, die eine unsigned
Integer-Argument entgegennimmt, was willst du denn mit den Fällen tun,
die da rausfallen? Also was soll passieren, wenn deine Funktion ein
Argument < 0 bekommt? Und was ist mit den Ergebnissen der aufgerufenen
Funktion für Argumente > maxint? Werden die irgendwo gebraucht?
Ich würde das Argument in der Funktion testen auf < 0 und daraus die
entsprechende Reaktion ableiten. Und wenn es >= 0 ist, dann würde ich es
in eine lokale unsigned integer Variable speichern, die ihrerseits als
Argument der zweiten Funktion dient. Sowas erkennt heutzutage ein
C-Compiler mit recht großer Wahrscheinlichkeit.
W.S.
W.S. schrieb:> was willst du denn mit den Fällen tun, die da rausfallen
Es fallen keine raus. Er schrieb doch bereits einleitend, dass es
anderweitig sichergestellt ist, dass der Wertebereich deutlich
eingeschränkt ist.
Jörg W. schrieb:> Es fallen keine raus.
Das ist eine (schimpfwort) Programmiertechnik.
Wenn man eine Funktiom (int xyz) schreibt und intern in dieser Funktion
nur positive Argumente zugelassen sind, dann muß man auch darüber
nachdenken, was man macht, wenn eben DOCH der Funktion mal was Negatives
angeboten wird.
W.S.
W.S. schrieb:> Wenn man eine Funktiom (int xyz) schreibt und intern in dieser Funktion> nur positive Argumente zugelassen sind, dann muß man auch darüber> nachdenken, was man macht, wenn eben DOCH der Funktion mal was Negatives> angeboten wird.
Dreifache Absicherung ist doch genauso Humbug. Das kostet nur
unsinnig Performance.
Wenn klar ist, dass der Aufrufer nur bestimmte Werte übergeben kann,
muss sich der Aufgerufene nicht erneut drum kümmern – am Ende hat er
vielleicht nichtmal eine sinnvolle Möglichkeit, wie dieses „drum
kümmern“ überhaupt vonstatten gehen soll: Gleich alles hinschmeißen
per abort()? Was, wenn das Ganze in einem völlig nebensächlichen
Umfeld passiert und es total daneben ist, nur deshalb die Applikation
nicht weiterlaufen zu lassen?
Wenn überhaupt, dann gehört da ein assert() hin, damit es während
des Debuggens rausfällt.
Was genau sinnvoll ist, kann man nur in der Gesamtsituation
beurteilen.
Auf jeden Fall gehört es natürlich ordentlich dokumentiert, wenn sich
eine Funktion auf bestimmte „von außen“ zugesicherte Eigenschaften
verlässt. Das ist viel wichtiger, als da noch irgendwelche
„Angst-Tests“ reinzufummeln.
W.S. schrieb:> Wenn man eine Funktiom (int xyz) schreibt und intern in dieser Funktion> nur positive Argumente zugelassen sind, dann muß man auch darüber> nachdenken, was man macht, wenn eben DOCH der Funktion mal was Negatives> angeboten wird.
Ist doch ganz einfach: Man druckt die Doku aus, in der diese
Preconditions stehen und prügelt damit den Fehler aus dem Programmierer,
der sie nicht beachtet hat, heraus.
Welcher Anteil der sicherheitsrelevanten Bugs wird wohl durch solche
"Der Check würde 2 Takte kosten, deshalb muss man halt aufpassen und
Doku lesen"-Funktionen verursacht?
Tom schrieb:> Welcher Anteil der sicherheitsrelevanten Bugs
Dann lies dir doch mal die diversen sicherheitsrelevanten Bugs durch.
Das sind nicht die, bei denen sich jemand ernsthaft um sowas Gedanken
gemacht hat wie hier. Das sind sehr oft recht subtile Dinger, bei
denen ein Überlauf nicht ordentlich erkannt worden ist oder dergleichen.
Also eher sowas wie das, was rauskommt, wenn man einfach blind ein
abs() da reingesetzt hätte und dabei den fall INT_MIN ignoriert.
Tom schrieb:> Welcher Anteil der sicherheitsrelevanten Bugs wird wohl durch solche> "Der Check würde 2 Takte kosten, deshalb muss man halt aufpassen und> Doku lesen"-Funktionen verursacht?
Gegenfrage: Wenn nur positive Zahlen erlaubt sind, warum hat der
Prototyp dann 'int' als Argument und nicht 'unsigned int'?
Tom schrieb:> Welcher Anteil der sicherheitsrelevanten Bugs wird wohl durch solche> "Der Check würde 2 Takte kosten, deshalb muss man halt aufpassen und> Doku lesen"-Funktionen verursacht?
Kein einziger. Die Ursache liegt nicht in der nicht gemachten doppelten
und dreifachen Gegenprüfung, sondern in der falschen Übergabe.
Jörg W. schrieb:> Wenn überhaupt, dann gehört da ein assert() hin, damit es während> des Debuggens rausfällt.
Ich schätze mal, daß du den Sinn nicht wirklich verstanden hast.
Ich habe solche Probleme eigentlich immer, weil es bei mir eben immer
vorkommt, daß Meßwerte verarbeitet werden müssen. Und diese Meßwerte
müssen beim Kunden einjustiert werden, sowas kann auch mal schief gehen
und dennoch müssen die verarbeitenden Funktionen damit klarkommen. Das
ist alles eben NICHT per assert auf meinem Schreibtisch erledigbar. Und
genau so schätze ich das hier vorliegende Grundproblem ein. Walter meint
zwar, daß die bei normalem Verlauf der Dinge überhaupt in Frage
kommenden Inputs recht begrenzt sind, aber auch er kann sich in einer
solchen Funktion wie sie hier vorliegt eben nicht verlassen. Deshalb
sollte man Code möglichst eigensicher schreiben, alles andere ist
Seiltänzerei.
Und ganz üble Probleme gibt's gelegentlich mit Meßwerten, die im float
oder double vorliegen. Wenn da per mißlungener Justage mal ein NAN
herauskommt, kriegt man das auf manchen Systemen weiter hinten nicht mal
sauber herausgefunden, geschweige denn per Einschränkung in 2 Grenzwerte
irgendwie sinnvoll behandelt.
W.S.
Rolf M. schrieb:> Ist doch ganz einfach: Man druckt die Doku aus,...
auch du hast das ganze nicht wirklich verstanden.
Du kannst die Realwelt nicht verprügeln und so eine Kiste muß trotz
unrealistischer oder verbotener Inputwerte, die aus der realen Welt
kommen, immer noch irgendwie sinnvoll sich verhalten. Entweder (wenn
möglich) sättigendes Verhalten oder Not-Aus für den Kernreaktor. Je nach
Anwendung.
W.S.
Nase schrieb:> Gegenfrage: Wenn nur positive Zahlen erlaubt sind, warum hat der> Prototyp dann 'int' als Argument und nicht 'unsigned int'?
Weil der eigentliche Input eben int IST und nicht unsigned. Und weil
die betreffende Funktion mit eben diesem Input möglicherweise auch noch
anderes anstellt, als ihn bloß einer anderen Funktion zu übergeben, die
nur unsigned verträgt.
Beispiel für die Langsamdenker:
Ein Audio-ADC liefert Werte von -32000 bis +32000 und in der Funktion
"VerarbeiteADCWert(int Wert)" muß u.a eine Systemfunktion
"MySystem(unsigned int blabla)" mit eben diesem ADC-Wert aufgerufen
werden. Und eben diese blöde Systemfunktion kann der TO nicht ändern aus
sonstigen Gründen. Also was tun?
W.S.
W.S. schrieb:> Beispiel für die Langsamdenker:
Lass mal, bevor du hier in Beleidigungen verfällst.
Der TE hatte eine konkrete Frage, hat eine konkrete Antwort bekommen,
und damit hat sich die Angelegenheit zu seiner Zufriedenheit erledigt.
Er hat uns seine Randbedingungen genannt, und er hat uns nicht darum
gebeten, dass wir ihm noch garantieren müssen, wie er diese einhält.
Man muss jetzt nicht darüber sinnieren, was mit dem Code alles noch
passieren könnte, wenn man ihn statt in einem MP3-Player oder in
einem Spielzeugauto nun zur Steuerung eines Flugzeugs oder einer
Mondrakete einsetzt.
W.S. schrieb:> Rolf M. schrieb:>> Ist doch ganz einfach: Man druckt die Doku aus,...>> auch du hast das ganze nicht wirklich verstanden.> Du kannst die Realwelt nicht verprügeln und so eine Kiste muß trotz> unrealistischer oder verbotener Inputwerte, die aus der realen Welt> kommen, immer noch irgendwie sinnvoll sich verhalten.
Nein, da hat du was nicht verstanden. Werte, die das Programm von
außen bekommt, müssen natürlich geprüft werden, und auf fehlerhafte
Daten muss es adäquat reagieren. Darum ging es hier aber gar nicht. Es
ging um Daten, deren Korrektheit das Programm selbst intern schon
sichergestellt hat und dann so an eine Funktion übergibt. Ein assert für
solche Fälle lasse ich mir ja noch gefallen, aber in der Funktion dann
nochmal eine Prüfung mit Rückfallebene einzubauen und in Unterfunktionen
und Unterunterfunktionen u.s.w. ist dann irgendwann etwas übertrieben.