Forum: Compiler & IDEs Integer ist positiv - portabel


von Walter T. (nicolas)


Lesenswert?

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
extern bar(unsigned int u);
2
3
void foo(int i)
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.

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


Lesenswert?

Was spricht gegen einen Cast nach unsigned int?

von Sven B. (scummos)


Lesenswert?

uint32_t j = abs(i)?

von Mikro 7. (mikro77)


Lesenswert?

Walter T. schrieb:

>
1
> void foo(int i)
2
> {
3
>     assert(i > INT_MIN);
4
>     i = abs(i);
5
>

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

von lalala (Gast)


Lesenswert?

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?

von Mikro 7. (mikro77)


Lesenswert?

> Betragsbildung mit Ausschluss des undefinierten Falls
> |INT_MIN|.
1
console> man abs
2
...
3
Trying to take the absolute value of the most negative integer is not defined.

von B. S. (bestucki)


Lesenswert?

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"

von Walter T. (nicolas)


Lesenswert?

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.

von tictactoe (Gast)


Lesenswert?

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

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


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von Hans (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

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


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

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


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Tom (Gast)


Lesenswert?

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?

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


Lesenswert?

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.

von Nase (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

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


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

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.