Forum: PC-Programmierung C/C++: Kennt ihr diesen "Operator" schon?


von C. P. P. Lovejoy (Gast)


Lesenswert?

Das folgende C++ Konstrukt hab ich grad so ähnlich im Netz entdeckt:
1
for (unsigned int i = stdv.size(); i --> 0; )
2
{
3
  std::cout << stdv[i];
4
}

Ich habs weder gekannt, noch auf anhieb verstanden. Da es auch in C 
geht, wollt ichs Euch nicht vorenthalten.

Ist von dort: 
https://stackoverflow.com/questions/275994/whats-the-best-way-to-do-a-backwards-loop-in-c-c-c

von g457 (Gast)


Lesenswert?

Welcher "operator" ist denn so neu für Dich? Ich seh da nur 
Standardkost.

von Carl D. (jcw2)


Lesenswert?

Formatierung mit Mehrwert ;-)

von nicht"Gast" (Gast)


Lesenswert?

Standardkost würde ich jetzt nicht sagen.

Ich würde eher jeden verhauen, der solch ein Konstrukt tatsächlich 
benutzen möchte :)

Das fällt in die Rubrik kann man machen, sollte man aber nicht.

von Bimbo. (Gast)


Lesenswert?

Das ist kein Operator. Das ist identisch mit:
1
i --> 0;
2
3
wird zu 
4
5
i-- > 0;

von Servo (Gast)


Lesenswert?

Hi,

wenn ich es richtig verstehe, dann wird die Variable i jetzt schon vor 
dem Vergleich dekrementiert, anstatt erst am Schleifenwendepunkt. Sollte 
man aber bedenken, wenn man i noch im Schleifenkörper braucht.

Ich persönlich halte von dieser Verkrypterei nicht viel, insbesondere C 
bringt das immer wieder in Verruf.

Viele Grüße,
Servo

von leo (Gast)


Lesenswert?

Servo schrieb:
> wenn ich es richtig verstehe, dann wird die Variable i jetzt schon vor
> dem Vergleich dekrementiert,

Nein. Das ist ganz normales Postdekrement.

> anstatt erst am Schleifenwendepunkt.

Was soll das sein?

leo

von g457 (Gast)


Lesenswert?

> Standardkost würde ich jetzt nicht sagen.

Simple assignment, postfix decrement und relational operator gibts so 
seit mindestens 30 Jahren (ich vermute mal dass er auf diese 3 
Operatoren raus will). Kaum ein Programm kommt ohne die aus, 
0815-Standardkost. Wer die nicht (er)kennt, kennt nicht wirklich was.

Nix für ungut.

von Carl D. (jcw2)


Lesenswert?

Vielleicht sollte man wirklich C++ nutzen.
Es gibt da nämlich auch rückwärts laufende Iteratoren, die das Ganze 
mittels std::for_each() ausdrückbar machen. (falls "rückwärts" das Ziel 
ist)

Edit: seh gerade, StackOverflow meint das auch ;-)

: Bearbeitet durch User
von Jakob (Gast)


Lesenswert?

Sagt bloß, ihr kennt den down-to Operator nicht mehr
https://www.sololearn.com/Discuss/1601179/have-you-heard-about-down-to-operator

von C. P. P. Lovejoy (Gast)


Lesenswert?

Das ursprüngliche Problem für diese rhetorische Frage war, ein Array 
rückwärts zu durchlaufen.

Wie durläuft man ein Array rückwärts, wenn man das Programmieren gelernt 
hat?, so dass man:

- unsgined int verwenden kann und
- mit den Arraydimensionen arbeiten kann, ohne +/- 1?

von Carl D. (jcw2)


Lesenswert?

C. P. P. Lovejoy schrieb:
> Das ursprüngliche Problem für diese rhetorische Frage war, ein Array
> rückwärts zu durchlaufen.
>
> Wie durläuft man ein Array rückwärts, wenn man das Programmieren gelernt
> hat?, so dass man:
>
> - unsgined int verwenden kann und
> - mit den Arraydimensionen arbeiten kann, ohne +/- 1?

Indem man von rbegin() nach rend() iteriert.

von Guest (Gast)


Lesenswert?

Ach komm, der String-Klammer-Operator ist schöner=
1
int i = 0;
2
char a = i["abcde"];

von C. P. P. Lovejoy (Gast)


Lesenswert?

Carl D. schrieb:
> Indem man von rbegin() nach rend() iteriert.

Wie bekommt man dann den Index?

von C. P. P. Lovejoy (Gast)


Lesenswert?

Guest schrieb:
> Ach komm, der String-Klammer-Operator ist schöner=

Sind das eigentlich Bugs oder Features in C?

von Carl D. (jcw2)


Lesenswert?

C. P. P. Lovejoy schrieb:
> Carl D. schrieb:
>> Indem man von rbegin() nach rend() iteriert.
>
> Wie bekommt man dann den Index?

Vor dem Rückwärtsiterieren sollte man ansatzweise das Vorwärtsiterieren 
verstehen. Man braucht da keinen Index, der Iterator ist quasi ein 
Zeiger auf das vector/array/...-Element.

Z.B. int-Vector rückwärts ausgeben:
1
for ( auto i = v.rbegin(); i != v.rend(); i++ ) {
2
    std::cout << *i << std::endl;
3
}

C++11 vorausgesetzt.

: Bearbeitet durch User
von leo (Gast)


Lesenswert?

Carl D. schrieb:
> Man braucht da keinen Index, der Iterator ist quasi ein
> Zeiger auf das vector/array/...-Element.

Was ist da der Unterschied zu einem Pointer-In(De-)krement? Mehr Syntax?

leo

von Servo (Gast)


Lesenswert?

leo schrieb:
>> wenn ich es richtig verstehe, dann wird die Variable i jetzt schon vor
>> dem Vergleich dekrementiert,
>
> Nein. Das ist ganz normales Postdekrement.

Ja hast recht. Insofern macht das noch weniger Sinn. Ich würde sowas 
auch niemals so hinschreiben, da stolpert man doch drüber. Ein einzelnes 
i--; oder i++; ist doch viel eindeutiger.

>
>> anstatt erst am Schleifenwendepunkt.
>
> Was soll das sein?

Ach komm. Ich weiß wirklich nicht, ob es da ein anderes Wort für gibt, 
aber in diesem Kontext kann ja man sich eindeutig denken was gemeint 
ist. Die for-Schleife ist vorprüfend, und das letzte Statement in der 
for-Klammer wird erst nach der Ausführung des Rumpfes abgearbeitet. An 
dieser Stelle "wendet" dann die Schleife. Daher meine kreative 
Wortschöpfung" :-)

Viele Grüße,
Servo

von Andreas M. (amesser)


Lesenswert?

leo schrieb:
> Servo schrieb:
>> wenn ich es richtig verstehe, dann wird die Variable i jetzt schon vor
>> dem Vergleich dekrementiert,
>
> Nein. Das ist ganz normales Postdekrement.

In der hier vorgestellten Syntax wird der Check (i>0) und der Dekrement 
(i--) vor dem Ausführen des Schleifenkörpers gemacht, wenn es
1
for (..; i > 0; i--)

wäre, dann würde der Dekrement nach dem Ausführen des Schleifenkörpers 
gemacht. Ersteres iteriert über [stdv.size() -1; 0], letzteres über 
[stdv.size(); 1].

Ich würde diese Form aber auch nicht nutzen. Da muss man drei mal 
schauen bis man kappiert was da passiert.

von Carl D. (jcw2)


Lesenswert?

leo schrieb:
> Carl D. schrieb:
>> Man braucht da keinen Index, der Iterator ist quasi ein
>> Zeiger auf das vector/array/...-Element.
>
> Was ist da der Unterschied zu einem Pointer-In(De-)krement? Mehr Syntax?
>
> leo

Z.B. daß v auch ein anderer Container mit Rückwärts-Iterator sein darf, 
ohne daß man einen (überladenen) [] Operator bräuchte.
Ein Iterator kann ein (Smart-)Pointer sein, oder aber auch was ganz 
anderes.

: Bearbeitet durch User
von C. P. P. Lovejoy (Gast)


Lesenswert?

Carl D. schrieb:
> C++11 vorausgesetzt.

Danke fürs Beispiel. Schön, dass C schon immer eine Lösung hat, und C++ 
im Zeitgeist frolockt.

von Carl D. (jcw2)


Lesenswert?

C. P. P. Lovejoy schrieb:
> Carl D. schrieb:
>> C++11 vorausgesetzt.
>
> Danke fürs Beispiel. Schön, dass C schon immer eine Lösung hat, und C++
> im Zeitgeist frolockt.

In C++ kann v aber auch ein Objekt sein, das aus stdin liest und dessen 
Iterator über den * Operator das aktuelle Zeichen liefert, via ++ 
Operator das nächste Zeichen liest und dieses via != Operator mit '\n' 
vergleicht. Dann iteriert man mit dem gezeigten Code über die Zeichen 
einer Zeile. Da tut sich der Index schwer.

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
> C. P. P. Lovejoy schrieb:
>> Carl D. schrieb:
>>> C++11 vorausgesetzt.
>>
>> Danke fürs Beispiel. Schön, dass C schon immer eine Lösung hat, und C++
>> im Zeitgeist frolockt.
>
> In C++ kann v aber auch ein Objekt sein, das aus stdin liest und dessen
> Iterator über den * Operator das aktuelle Zeichen liefert, via ++
> Operator das nächste Zeichen liest und dieses via != Operator mit '\n'
> vergleicht. Dann iteriert man mit dem gezeigten Code über die Zeichen
> einer Zeile. Da tut sich der Index schwer.

Ein reverse-Iterator aber auch... :)

von Wilhelm M. (wimalopaan)


Lesenswert?

Jeder Zeiger ist ein Iterator, aber nicht jeder Iterator ist ein Zeiger.

Ein Iterator ist ein abstraktes Konzept (nein, ich meine nicht concepts 
und requirements, denn das Iterator-Konzept ist Uralt in C++). Dieses 
Konzept verlangt eine bestimmte Operationenmenge. Jeder Dytentyp, der 
diese Typanforderungen erfüllt, ist eine Iterator. Und kann auch für die 
Algorithmen der stdlib verwendet werden.

von Volle22 (Gast)


Lesenswert?

übrigens haben Prozessoren mit einer Loop Einheit im Core meist das 
Problem den Schleifenzähler/Iterator nicht in der Load/Store Einheit 
gleichzeitig verwenden zu können.
Wenn man also den Iterator auch  als Array Index verwendet wird kann die 
"Zero Overhead Loop Unit" nicht verwendet werden  => Schleife benötigt 
mehr Takte.

Eine Zweite Variable die auch problemlos in die andere Richtung laufen 
kann ist effizienter.
Die HW-Loop Counter sind meist übrigens Down-Counter.

von Wilhelm M. (wimalopaan)


Lesenswert?

C. P. P. Lovejoy schrieb:
> Das folgende C++ Konstrukt hab ich grad so ähnlich im Netz entdeckt:
>
>
1
> for (unsigned int i = stdv.size(); i --> 0; )
2
> {
3
>   std::cout << stdv[i];
4
> }
5
>
>
> Ich habs weder gekannt, noch auf anhieb verstanden. Da es auch in C
> geht, wollt ichs Euch nicht vorenthalten.
>
> Ist von dort:
> 
https://stackoverflow.com/questions/275994/whats-the-best-way-to-do-a-backwards-loop-in-c-c-c

Dort steht übrigens: "... typographically best ..."

von GEKU (Gast)


Lesenswert?

Servo schrieb:
> wenn ich es richtig verstehe, dann wird die Variable i jetzt schon vor
> dem Vergleich dekrementiert, anstatt erst am Schleifenwendepunkt. Sollte
> man aber bedenken, wenn man i noch im Schleifenkörper braucht.

Wenn ich c richtig verstanden habe, dann wird der dritte Parameter im 
"Schleifenkopf"   am Ende  des "Schleifenkörpers" durchgeführt.

Zur Vollständigkeit :

Der erste Parameter im "Schleifenkopf" wird einmal  am Beginn  der 
Schleifeninitalisierung, der Zweite  vor Eintritt  im Schleifenkörper 
durchgeführt.

von Bernd K. (prof7bit)


Lesenswert?

Bimbo. schrieb:
> Das ist kein Operator. Das ist identisch mit:
>
1
> i --> 0;
2
> 
3
> wird zu
4
> 
5
> i-- > 0;
6
>

Der Parser oder Tokenizer oder meinetwegen auch die ganze Grammatik ist 
krank wenn das erlaubt ist ohne einen Syntaxfehler auszuspucken.

/Meinung

von Vincent H. (vinci)


Lesenswert?

Die Eingangs gepostete Schleife nutze ich eigentlich sehr gerne. Meiner 
Meinung nach beugt das auch versehentliche Endlosschleifen vor in denen 
unsigned < 0 verglichen wird.


Guest schrieb:
> Ach komm, der String-Klammer-Operator ist schöner=
>
1
> int i = 0;
2
> char a = i["abcde"];
3
>

Das kannte ich auch nicht...


Und generell bezüglich C++:
https://youtu.be/tsG95Y-C14k

;)

von René H. (Firma: anonymous) (hb9frh)


Lesenswert?

Guest schrieb:
> Ach komm, der String-Klammer-Operator ist schöner=int i = 0;
> char a = i["abcde"];

Und wofür soll der gut sein?

Grüsse,
René

von A. S. (Gast)


Lesenswert?

Ich glaube, da hat jemand ein "schönes" Konstrukt in die falsche 
Schleife gesteckt.

Standard-Anwendungsfall ist
1
   while(n-->0) ...

Wenn man n bekommt und es nicht als Index braucht. Z.b. n Mal einen Port 
toggeln soll.

von Peter D. (peda)


Lesenswert?

Bernd K. schrieb:
> Der Parser oder Tokenizer oder meinetwegen auch die ganze Grammatik ist
> krank wenn das erlaubt ist ohne einen Syntaxfehler auszuspucken.

Dabei ist es doch ganz einfach. In C sind Whitespaces und Zeilenumbrüche 
kein Syntaxelement. Sie können also weggelassen werden, solange sich der 
Ausdruck noch eindeutig parsen läßt.
Nur der Mensch braucht sie zur besseren Lesbarkeit. Also ist auch er in 
der Verantwortung, sie sinnvoll zu setzen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Vincent H. schrieb:
> Guest schrieb:
>> Ach komm, der String-Klammer-Operator ist schöner=
>>> int i = 0;
>> char a = i["abcde"];
>>
> Das kannte ich auch nicht...

Subscript-Operator
Es ist definiert:
1
a[b] == *(a + b)

Daher ist
1
*(a + 5)

und
1
*(5 + a)
dasselble.

von Walter K. (walter_k488)


Lesenswert?

nicht"Gast" schrieb:
> Standardkost würde ich jetzt nicht sagen.
>
> Ich würde eher jeden verhauen, der solch ein Konstrukt tatsächlich
> benutzen möchte :)

Jau! Und Fahrrad nur mit Helm besteigen - und an die Strasse bei jeder 
50cm hohen Böschung eine doppelte Leitplanke und nur noch in Warnweste 
und mit Schutzbrille rumrennen!!!

LoL

von M. Н. (Gast)


Lesenswert?

C. P. P. Lovejoy schrieb:
> for (unsigned int i = stdv.size(); i --> 0; )

Ich verstehe nicht genau, was hier neu sein soll?
Habe nciht alles gelesen. Das sieht mir aber nach altem Hut aus. Kein 
neuer Operator.

i-->0 ist lediglich (i--)>0
Und das ist:
* Dekrementiere i
* liefere Vergleich, ob i (vor dem dekrement) größer als 0

Stichwort: Operator Precedence. Soltle man grob kennen von den Sprachen, 
die man verwendet.

und dann eben vor jedem Schleifendurchlauf durchgeführt (Zweites 
Statement in den Klammern der for-Schleife) und der Einsprung in den 
Schleifenkörper findet nur statt, wenn der Ausdruck != 0 liefert.

Ich stelle leider immer wieder fest, dass die Grundfesten von C, 
speziell eine for-Schleife, bei den meisten nicht richtig gelehrt 
wurden.

for (a; b; c) d;

1) Als erstes wird das Statement "a" ausgeführt.
2) Dann wird zur Prüfung der Einsprungbedingnung übergegangen. Dazu wird 
"b" ausgeführt. Der Rückgabewert von was auch immer da steht, 
entscheidet, ob der Schleifenkörper "d" ausgeführt wird.
3) Am Ende wird Statement "c" ausgeführt.
4) => Weiter bei Punkt 2)

Viele scheinen darauf festgenagelt zu sein, dass
1) a nur eine Initialisierung,
2) b nur eine Bedingung / Vergleich
3) und c nur ein Inkrement/Dekrement sein kann.

Da kann jedoch jedes beliebige Statement stehen.

Dazu ein kleines Beispiel:
Die Elemente einer verkettete Liste 'list_head' mit der Struktur
1
struct list_item {
2
 void *data;
3
 struct list_item *next;
4
}
können bspw. so gezählt werden (ja ich weiß, verkettete Listen sind 
langsam und skalieren schlecht):
1
int len;
2
struct list_item *iter;
3
4
for (len = 0, iter = list_head; iter; iter = iter->next, len++);

von Wilhelm M. (wimalopaan)


Lesenswert?

M. H. schrieb:
> Ich verstehe nicht genau, was hier neu sein soll?
> Habe nciht alles gelesen.

Hättest Du machen sollen. Dann wäre alles klar gewesen.

von Peter D. (peda)


Lesenswert?

M. H. schrieb:
> for (a; b; c) d;

Zu beachten ist aber, daß mit continue zu c gesprungen wird.
Bei do/while und while springt continue dagegen zum Vergleich.

von BringMischWerkstatt (Gast)


Lesenswert?

Bernd K. schrieb:
> Der Parser oder Tokenizer oder meinetwegen auch die ganze Grammatik ist
> krank wenn das erlaubt ist ohne einen Syntaxfehler auszuspucken.

Interpunktion ist jetzt auch nicht Deine Stärke.

von Wilhelm M. (wimalopaan)


Lesenswert?

M. H. schrieb:
> Viele scheinen darauf festgenagelt zu sein, dass
> 1) a nur eine Initialisierung,
> 2) b nur eine Bedingung / Vergleich
> 3) und c nur ein Inkrement/Dekrement sein kann.

M. H. schrieb:
> Da kann jedoch jedes beliebige Statement stehen.

Bei a) stimmt das so leider nicht.

Hier sind nur expression-statements und simple-declarations erlaubt.

M. H. schrieb:

> for (len = 0, iter = list_head; iter; iter = iter->next, len++);

Hier hast Du also für a) den Komma-Operator eingesetzt 
(expression-stmt).

von Yalu X. (yalu) (Moderator)


Lesenswert?

C. P. P. Lovejoy schrieb:
> C/C++: Kennt ihr diesen "Operator" schon?

Ein kurzes "Häh?" hat mit dieses --> schon entlockt :)

Aber auch wenn das korrekt ist, würde ich dennoch die Schreibweise

1
for (unsigned int i = stdv.size(); i-- > 0; )

bevorzugen, um anderen den "Häh?"-Effekt zu ersparen. Da ich unäre
Operatoren immer direkt (ohne Leerzeichen) an den Operanden hefte und
binäre Operatoren meist beidseitig mit Leerzeichen von den Operanden
trenne, ergibt sich diese Schreibweise bei mir ganz von selber.

Bernd K. schrieb:
> Der Parser oder Tokenizer oder meinetwegen auch die ganze Grammatik ist
> krank wenn das erlaubt ist ohne einen Syntaxfehler auszuspucken.

In den meisten Programmiersprachen können mehrere Operatoren direkt
hintereinander geschrieben werden, da sie neben ihrer eigentlichen
Funktion auch als Delimiter fungieren. So ist bspw.

1
a <-- b

in C, C++, Java, Python, Ruby, Pascal u.v.m. ein legaler Ausdruck, der
in C, C++ und Java äquivalent zu
1
a < (--b)

und in Python, Ruby und Pascal (wo es kein -- gibt) äquivalent zu

1
a < (-(-b))

ist.

So gesehen ist jede der genannten Sprachen "krank" ;-)

Eine der wenigen "gesunden" Ausnahmen stellt Haskell dar: Dort wird das
<-- tatsächlich als ein einzelner Operator geparst, der aber – da nicht
in der Standardbibliothek enthalten – selbst definiert werden muss. Soll
das Verhalten von Python, Ruby und Pascal nachgebildet werden, lautet
diese Definition ganz einfach

1
x <-- y   =   x < -(-y)

Dann ist 4 <-- 4 == False und 3 <-- 4 == True.

von A. S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Aber auch wenn das korrekt ist, würde ich dennoch die Schreibweise
>
> for (unsigned int i = stdv.size(); i-- > 0; )
>
> bevorzugen

Als Ausdruck künstlerischer Potenz vielleicht.
Wenn ich mit stdv.size-1 in die Schleife gehen will und auch die 0 haben 
will, dann sollte es auch so schreiben, falls ein Programmierer 
(Nachfolger) und nicht-Künstler das mal lesen muss. Also:
1
    /* langweilige version */
2
    for(unsigned int i = stdv.size()-1; i>=0; i--)
Ja, ist ein bisschen länger. Frag einfach mal 5 Leute: Die Funktion gibt 
10 zurück, mit welchen werten i wird die Schleife durchlaufen? und 
schaue, wo sie mehr straucheln.

von Vincent H. (vinci)


Lesenswert?

A. S. schrieb:
> Yalu X. schrieb:
>> Aber auch wenn das korrekt ist, würde ich dennoch die Schreibweise
>>
>> for (unsigned int i = stdv.size(); i-- > 0; )
>>
>> bevorzugen
>
> Als Ausdruck künstlerischer Potenz vielleicht.
> Wenn ich mit stdv.size-1 in die Schleife gehen will und auch die 0 haben
> will, dann sollte es auch so schreiben, falls ein Programmierer
> (Nachfolger) und nicht-Künstler das mal lesen muss. Also:
>
>
1
>     /* langweilige version */
2
>     for(unsigned int i = stdv.size()-1; i>=0; i--)
3
>
> Ja, ist ein bisschen länger. Frag einfach mal 5 Leute: Die Funktion gibt
> 10 zurück, mit welchen werten i wird die Schleife durchlaufen? und
> schaue, wo sie mehr straucheln.


Satire? :D

Falls nicht, hint:
1
.LBB0_1:
2
        jmp     .LBB0_1

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

A. S. schrieb:
> Als Ausdruck künstlerischer Potenz vielleicht.
> Wenn ich mit stdv.size-1 in die Schleife gehen will und auch die 0 haben
> will, dann sollte es auch so schreiben, falls ein Programmierer
> (Nachfolger) und nicht-Künstler das mal lesen muss. Also:
>     /* langweilige version */
>     for(unsigned int i = stdv.size()-1; i>=0; i--)

Bamm!!! Juhu, der Klassiker!

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

A. S. schrieb:
> for(unsigned int i = stdv.size()-1; i>=0; i--)

Vincent H. schrieb:
> Satire? :D

Wilhelm M. schrieb:
> Bamm!!! Juhu, der Klassiker!

Lassen wir den GCC auch noch einmal nachtreten:

1
test.c:5:40: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
2
    5 |   for(unsigned int i = stdv.size()-1; i>=0; i--)
3
      |                                       ~^~~

von nicht"Gast" (Gast)


Lesenswert?

Walter K. schrieb:
> Jau! Und Fahrrad nur mit Helm besteigen - und an die Strasse bei jeder
> 50cm hohen Böschung eine doppelte Leitplanke und nur noch in Warnweste
> und mit Schutzbrille rumrennen!!!

Du kannst das natürlich halten wie ein Dachdecker.

Einfache Tatsache ist aber, dass eine solche Schreibweise unüblich ist 
und der geneigte Leser erst mal drüber fällt, was da jetzt wirklich 
passiert.

Dazu gibts keine Perfomanceverbesserung gegenüber einer üblichen 
Schreibweise. Ergo -> Schrott der nur Fehler verursacht.

von Sven B. (scummos)


Lesenswert?


von M. Н. (Gast)


Lesenswert?

Peter D. schrieb:
> M. H. schrieb:
>> for (a; b; c) d;
>
> Zu beachten ist aber, daß mit continue zu c gesprungen wird.
> Bei do/while und while springt continue dagegen zum Vergleich.

Ist essentiell das gleiche bzw. sogar dasselbe Verhalten.
continue überspringt den Rest des Statementblocks "d". Bei einer 
for-Schleife kommt dann eben "c" vor "b".

von Heiko L. (zer0)


Lesenswert?

A. S. schrieb:
> Ja, ist ein bisschen länger. Frag einfach mal 5 Leute: Die Funktion gibt
> 10 zurück, mit welchen werten i wird die Schleife durchlaufen? und
> schaue, wo sie mehr straucheln.

Die Antwort, die man hier lernt wäre, dass man das nicht sagen kann, 
weil es implementation-defined ist. Und wenn im Body nicht irgendwas mit 
Seiteneffekt steht sogar UB.

von mh (Gast)


Lesenswert?

Heiko L. schrieb:
> A. S. schrieb:
>> Ja, ist ein bisschen länger. Frag einfach mal 5 Leute: Die Funktion gibt
>> 10 zurück, mit welchen werten i wird die Schleife durchlaufen? und
>> schaue, wo sie mehr straucheln.
>
> Die Antwort, die man hier lernt wäre, dass man das nicht sagen kann,
> weil es implementation-defined ist. Und wenn im Body nicht irgendwas mit
> Seiteneffekt steht sogar UB.

Wo ist da etwas implementation-defined?

von Heiko L. (zer0)


Lesenswert?

mh schrieb:
> Heiko L. schrieb:
>> A. S. schrieb:
>>> Ja, ist ein bisschen länger. Frag einfach mal 5 Leute: Die Funktion gibt
>>> 10 zurück, mit welchen werten i wird die Schleife durchlaufen? und
>>> schaue, wo sie mehr straucheln.
>>
>> Die Antwort, die man hier lernt wäre, dass man das nicht sagen kann,
>> weil es implementation-defined ist. Und wenn im Body nicht irgendwas mit
>> Seiteneffekt steht sogar UB.
>
> Wo ist da etwas implementation-defined?

"mit welchen Werten" - das hängt von der Breite des unsigned ints ab. 
Implementation defined.

von Rolf M. (rmagnus)


Lesenswert?

Heiko L. schrieb:
> Die Antwort, die man hier lernt wäre, dass man das nicht sagen kann,
> weil es implementation-defined ist.

Doch, kann man sagen: Alle Werte im Bereich 0 bis UINT_MAX.

von Heiko L. (zer0)


Lesenswert?

Rolf M. schrieb:
> Heiko L. schrieb:
>> Die Antwort, die man hier lernt wäre, dass man das nicht sagen kann,
>> weil es implementation-defined ist.
>
> Doch, kann man sagen: Alle Werte im Bereich 0 bis UINT_MAX.

Ach so... ich dachte, die Frage zielt auf eine Aufzählung...

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Ich glaube, die Frage war sowieso ganz anders gedacht.

von Heiko L. (zer0)


Lesenswert?

Rolf M. schrieb:
> Ich glaube, die Frage war sowieso ganz anders gedacht.

Klar. Da fragt man sich, wie lange es wohl dauert, bis der Fehler 
auffällt.

<-- Hahahaha!

Zum Beispiel einem Build-Server.

: Bearbeitet durch User
von Markus E. (engelmarkus)


Angehängte Dateien:

Lesenswert?

Yalu X. schrieb:
> Ein kurzes "Häh?" hat mit dieses --> schon entlockt :)
Insbesondere wenn man eine Code-Schriftart mit den passenden Ligaturen 
verwendet ;) .

Eigentlich hatte ich ja gehofft, es geht um den operator<=>...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

also lesbarer wird es
1
for (unsigned int i = stdv.size()-1; i > 0; i--)
2
{
3
  std::cout << stdv[i];
4
}

Dann sieht man das die Wirkungsweise nur darauf zielt das die for 
Schleife um einmal weniger ausgeführt wird wie sonst üblich. Also ohne 
das Zuweisungs minus ein.

Am Ende könnte auch das dastehen, je nachdem womit i initialisiert wird.
Ohne Zusammenhang kann man darüber nur spekulieren.
1
for (unsigned int i = stdv.size(); i > 1; i--)
2
{
3
  std::cout << stdv[i];
4
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> also lesbarer wird es
> for (unsigned int i = stdv.size()-1; i > 0; i--)
> {
>   std::cout << stdv[i];
> }

entspricht aber nicht dem Original!

h

Veit D. schrieb:
> Am Ende könnte auch das dastehen, je nachdem womit i initialisiert wird.
> Ohne Zusammenhang kann man darüber nur spekulieren.
> for (unsigned int i = stdv.size(); i > 1; i--)
> {
>   std::cout << stdv[i];
> }

Und das ist total falsch!

Und auch in dem ursprünglichen Code ist ein subtiler Fehler drin.

So ist es richtig:
1
    for (auto i{stdv.size()}; i-- > 0;) {
2
      std::cout << stdv[i];
3
    }

Den Fehler bekommt man übrigens auch angezeigt wenn man korrekt 
initialisieren und den falschen DT verwendet:
1
    for (unsigned int i{stdv.size()}; i-- > 0;) {
2
      std::cout << stdv[i];
3
    }

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dann eben so
1
for (int i=stdv.size()-1; i>=0; i--)
2
{
3
   ...
4
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> for (int i=stdv.size()-1; i>=0; i--)
> {
>    ...
> }

Immer noch falsch.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe das getestet, zeigt exakt das gleiche Verhalten.
1
const int var = 5;
2
3
4
void setup(void) {
5
  Serial.begin(9600);
6
  
7
  for (int i=var; i-- > 0;)
8
  {
9
     Serial.println(i);
10
  }
11
  
12
  Serial.println();
13
  
14
  for (int i=var-1; i>=0; i--)
15
  {
16
     Serial.println(i);
17
  }
18
}
19
20
21
void loop(void)
22
{
23
  
24
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> ich habe das getestet, zeigt exakt das gleiche Verhalten.

Und Du meinst jetzt, ein(!) kleiner Test sei ein Beweis für die 
Korrektheit?

Im übrigen entspricht das nicht der Version, die ein Problem hat. Was Du 
jetzt gepostest hast, ist tatsächlich so korrekt.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

du meinst den Unterschied zwischen signed und unsigned. Ja das habe ich 
in weiser Voraussicht korrigiert. Denn unsigend > 0 ist immer wahr. Wäre 
also sinnfrei.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> du meinst den Unterschied zwischen signed und unsigned.

Jein.
Auch da passieren beliebig viele Fehler, wenn signed und unsigned Typen 
gemischt werden. Eigentlich ein sehr bekanntes Problem:

Schau Dir dies mal an, dann verstehst Du was ich meine:
1
    std::vector<char> v1(1024Ul * 1024UL * 1024UL * 5);
2
3
    int l1 = v1.size(); // Deine Variante
4
    std::cout << v1.size() << '\n';   
5
    std::cout << l1 << '\n'; 
6
    
7
    auto l2{v1.size()}; // Meine Variante
8
    std::cout << l2 << '\n';   
9
10
    int l3{v1.size()}; // Hier kommt eine Warnung

von Veit D. (devil-elec)


Lesenswert?

Hallo,

du möchtest bestimmt mit int einen Überlauf und damit ein falsches 
Ergebnis provozieren. Ich erhalte allerdings abgesehen von der Warnung 
3x die Ausgabe 1073741824.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> du möchtest bestimmt mit int einen Überlauf und damit ein falsches
> Ergebnis provozieren.

Genau, Du hast es ;-)

Veit D. schrieb:
> Ich erhalte allerdings abgesehen von der Warnung
> 3x die Ausgabe 1073741824.

Ist die erste Zeile so, wie ich sie geschrieben habe? Vermutlich hast Du 
da schon den ersten Überlauf.

Für Größenangaben (Container-Größen, Elementanzahlen) sollte man immer 
size_t verwenden oder eben automatische Typinferenz. Ab C++20 kann man 
generell auch von den vorzeichenlosen Typen weg und verwendet c.ssize().

von Veit D. (devil-elec)


Lesenswert?

Hallo,

also sollte normalerweise der erste Wert falsch sein bzw. von den 
anderen beiden abweichen?

Ich habe das in CodeBlocks 17.12 wie folgt ausprobiert, allerdings "nur" 
mit gcc 7.1. Das ist laut Buch frisch installiert. Normalerweise 
programmiere ich direkt für 8Bit Atmel µC und nicht am PC für den PC.
1
#include <iostream>
2
#include <vector>
3
using namespace std;
4
5
int main()
6
{
7
    cout << "Hello world!" << endl;
8
9
    std::vector<char> v1(1024UL * 1024UL * 1024UL * 5);
10
11
    int l1 = v1.size(); // Deine Variante
12
    std::cout << v1.size() << '\n';
13
    std::cout << l1 << '\n';
14
15
    auto l2{v1.size()}; // Meine Variante
16
    std::cout << l2 << '\n';
17
18
    // int l3{v1.size()}; // Hier kommt eine Warnung
19
20
    return 0;
21
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich wollte die ausgebliebene int Provokation nochmal testen.
Auf meinem PC ist int 32 bit groß. Dadurch klappt das nicht.

Ändere ich int in int16_t erhalte ich
1073741824
0
1073741824

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> ich wollte die ausgebliebene int Provokation

Wer hat Dich denn provoziert?

Du meinst wohl "integer promotion".

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich übersetze. Du wolltest den Wertebereichsüberlauf provozieren, sprich 
bewusst herbeiführen. Was aus irgendwelchen Gründen nicht klappt.

"integer promotion".
Ich wollte dem Integer keinen Doktortitel verleihen.

von mh (Gast)


Lesenswert?

Veit D. schrieb:
> Ich habe das in CodeBlocks 17.12 wie folgt ausprobiert, allerdings "nur"
> mit gcc 7.1

Auf was für einem System? Wie groß ist ein size_t?


Veit D. schrieb:
> int l3{v1.size()}; // Hier kommt eine Warnung

Was kommt denn da für eine Warnung? Das müsste nen Fehler geben.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Ich wollte dem Integer keinen Doktortitel verleihen.

So heißt es nunmal.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Was kommt denn da für eine Warnung? Das müsste nen Fehler geben.

Nein, kein Fehler. Sondern nur die "narrowing" Warnung.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Was aus irgendwelchen Gründen nicht klappt.

Zeigt mal die Fehlermeldung, die folgende Zeile auf Deinem System 
verursacht:
1
    decltype(std::vector<char>{}.size())::_;

von Veit D. (devil-elec)


Lesenswert?

Hallo,

diese Zeile
1
decltype(std::vector<char>{}.size())::_;
verursacht diesen Fehlermeldung
decltype evaluates to 'std::vector<char>::size_type {aka unsigned int}', 
which is not a class or enumeration type|

und
1
int l3{v1.size()}; // Hier kommt eine Warnung
bringt folgende Warnung
narrowing conversion of 'v1.std::vector<char>::size()' from 
'std::vector<char>::size_type {aka unsigned int}' to 'int' inside { } 
[-Wnarrowing]|

Aber darum ging es mir nicht.
Es ging um diese Zeile
1
int l1 = v1.size(); // Deine Variante

hier müßte doch auf Grund vom Wertebereichüberlauf ein falsches Ergebnis 
erscheinen. Macht er nicht weil das Ergebnis in 32bit int passt. Bohre 
ich den Wert auf erhalte ich überall 0, was auch nicht sein kann.
1
#include <iostream>
2
#include <vector>
3
using namespace std;
4
5
int main()
6
{
7
    std::vector<char> v1(1024UL * 1024UL * 1024UL * 1024UL * 5);
8
9
    int l1 = v1.size(); // Deine Variante
10
    std::cout << v1.size() << '\n';
11
    std::cout << l1 << '\n';
12
13
    auto l2{v1.size()}; // Meine Variante
14
    std::cout << l2 << '\n';
15
16
    return 0;
17
}

Ergebnis 3x 0.

Also entweder habe ich 3x 1073741824 oder 3x 0. Kann beides nicht 
richtig sein. Verständlicher?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

machst du das auf dem PC oder µC?

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> machst du das auf dem PC oder µC?

Auf dem PC: siehe Forumstitel

von Wilhelm M. (wimalopaan)


Lesenswert?

Ok, Du arbeitest auf einem ILP32-System (32-Bit), da ist dann ein int 
und ein long unsigned int gleich mit 32-Bit.

Dann versuche folgendes:
1
    auto x1(std::numeric_limits<unsigned int>::max());
2
    int  x2(std::numeric_limits<unsigned int>::max());
3
4
    std::cout << x1 << '\n';    
5
    std::cout << x2 << '\n';

von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> mh schrieb:
>> Was kommt denn da für eine Warnung? Das müsste nen Fehler geben.
>
> Nein, kein Fehler. Sondern nur die "narrowing" Warnung.

Hmmm ist das nicht "list initialization"? Müsste dann nicht nen Fehler 
sein? Hier ein Auszug aus 
https://en.cppreference.com/w/cpp/language/list_initialization (hab grad 
keine Lust auf Standard lesen)
1
Otherwise (if T is not a class type), if the braced-init-list has only one element and either T isn't a reference type or is a reference type whose referenced type is same as or is a base class of the type of the element, T is direct-initialized (in direct-list-initialization) or copy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.

clang sagt, dass es nen error ist.
1
error: non-constant-expression cannot be narrowed from type 'std::vector::size_type' (aka 'unsigned long') to 'int' in initializer list [-Wc++11-narrowing]
2
    int l3{v1.size()}; // Hier kommt eine Warnung

von Rolf M. (rmagnus)


Lesenswert?

Veit D. schrieb:
> Bohre ich den Wert auf erhalte ich überall 0, was auch nicht sein kann.

Warum nicht?
1
>     std::vector<char> v1(1024UL * 1024UL * 1024UL * 1024UL * 5);
Hier läuft es wie zu erwarten ist über. 1024*1024*1024 gibt 2^30. Das 
passt in 32 Bit. Wenn man das mit 1024 multipliziert, bekommst du 2^40. 
Davon bleiben nur die unteren 32 Bit übrig, und die sind alle 0. Wenn 
man das dann mit 5 multipliziert, ist es immer noch 0. Es wird also ein 
vector mit 0 Elementen erzeugt. Dass dann für size() 0 zurückkommt, ist 
also zu erwarten.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

>
1
>     auto x1(std::numeric_limits<unsigned int>::max());
2
>     int  x2(std::numeric_limits<unsigned int>::max());
3
> 
4
>     std::cout << x1 << '\n';
5
>     std::cout << x2 << '\n';
6
>

das macht was es soll

4294967295
-1

Ich glaube wir haben jetzt genug getestet.
Problem war und ist klar dargelegt wurden.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Hmmm ist das nicht "list initialization"?

Nein, es ist brace-initialization, in diesem Fall als 
direct-scalar-initialization. Für Aggregate wäre es dann 
list-initialization.

Dafür hat sich der Begriff "uniform initialization syntax" gebildet.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Ich glaube wir haben jetzt genug getestet.
> Problem war und ist klar dargelegt wurden.

Denke ich auch, und es freut mich, wenn Du den Unterschied verstanden 
hast.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> clang sagt, dass es nen error ist.

Ja, für clang++ ist es ein error, für gcc nur bei einer 
constant-expression, sonst nur eine Warnung.

von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> mh schrieb:
>> Hmmm ist das nicht "list initialization"?
>
> Nein, es ist brace-initialization, in diesem Fall als
> direct-scalar-initialization. Für Aggregate wäre es dann
> list-initialization.
>
> Dafür hat sich der Begriff "uniform initialization syntax" gebildet.


Soweit ich das überblicke handelt es sich um "direct 
list-initialization". direct-initialization nach 
http://eel.is/c++draft/dcl.init#16.1 und list-initialized nach 
http://eel.is/c++draft/dcl.init#17.1.

Und bei list-initialized trifft http://eel.is/c++draft/dcl.init#list-3.9 
zu. Dort gibt es dann auch ein Beispiel:
1
int x1 {2};                         // OK
2
int x2 {2.0};                       // error: narrowing

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Soweit ich das überblicke handelt es sich um "direct
> list-initialization"

In

https://en.cppreference.com/w/cpp/language/direct_initialization

ist
1
auto x{42};

direct-initialization mit einem single brace enclosed initializer. Dies 
ist
eine Spezialform der list-initialization

https://en.cppreference.com/w/cpp/language/list_initialization

mit einer Liste mit einem Wert.

Die brace-initialization von Scalaren oder Aggregaten wird dann auch 
ugs. als "uniform initialization syntax" bezeichnet. Den Ausdruck finde 
ich sehr gut, denn diese Syntax ist wirklich universell, und tut das, 
was man erwartet, und warnt bzw. lehnt ein narrowing an/ab.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> direct-initialization mit einem single brace enclosed initializer. Dies
> ist
> eine Spezialform der list-initialization
> https://en.cppreference.com/w/cpp/language/list_initialization

Das ist genau das was ich geschriben hab. Und wenn man aus den 
beschriebenen Fällen, den einen Fall heraussucht, der hier zutrifft, 
findet man:
1
T object { arg1, arg2, ... };  (1)
2
3
List initialization is performed in the following situations:
4
 direct-list-initialization (both explicit and non-explicit constructors are considered)
5
  1) initialization of a named variable with a braced-init-list (that is, a 
6
  possibly empty brace-enclosed list of expressions or nested braced-init-lists)
7
8
Explanation
9
 The effects of list initialization of an object of type T are:
10
  Otherwise (if T is not a class type), if the braced-init-list has only one 
11
  element and either T isn't a reference type or is a reference type whose 
12
  referenced type is same as or is a base class of the type of the element, T 
13
  is direct-initialized (in direct-list-initialization) or copy-initialized 
14
  (in copy-list-initialization), except that narrowing conversions are not 
15
  allowed.

Also: narrowing conversions are not allowed

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Das ist genau das was ich geschriben hab.

Sehr gut, da sind wir uns einig. Und Du hattest die bessere, 
standard-konforme Wortwahl!

mh schrieb:
> except that narrowing conversions are not
>   allowed.

Ich denke, dass ist nicht normativ. Sprich, der gcc darf ein mögliches 
narrowing warnen, und muss ein definitives narrowing mit Wertänderung 
als Fehler behandeln. Offensichtlich sieht das der clang anders (was mir 
besser gefällt).

Das sollte man (Du?) mal auf der isocpp ML klären.

von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Sprich, der gcc darf ein mögliches
> narrowing warnen, und muss ein definitives narrowing mit Wertänderung
> als Fehler behandeln.

Ob, der tatsächliche Wert in den Typ passt, ist egal. In beiden Fällen 
ist es laut Standard ill-formed 
http://eel.is/c++draft/dcl.init#list-7.4. Er muss also in beiden fällen 
nur warnen, darf aber einen Fehler erzeugen.

> Das sollte man (Du?) mal auf der isocpp ML klären.

Das Probem ist eher der gcc. Und ich vermute, die haben das mit Absicht 
gemacht.

von Veit D. (devil-elec)


Lesenswert?

Rolf M. schrieb:
> Veit D. schrieb:
>> Bohre ich den Wert auf erhalte ich überall 0, was auch nicht sein kann.
>
> Warum nicht?

Hallo Rolf,

dich hatte ich komplett übersehen. Sorry.
Okay. Warum habe ich was anderes erwartet?
Weil mir nachwievor die Differenz als falsches Ergebnis fehlt.
1
uint32_t var1 = (1024UL * 1024UL * 1024UL * 5);
2
std::cout << var1 << '\n';
3
4
uint16_t var2 = 1024UL * 1024UL * 1024UL * 5;
5
std::cout << var2 << '\n';

Ausgabe:

1073741824
0


Ich rechne nochmal.
1
1024 * 1024 * 1024 * 5 = 5.368.709.120
Wertebereich von uint32 ist 0 ... 4.294.967.296
Die Differenz ist 1073741824. Das ist der Rest vom Überlauf. Wie 
erwartet.

Wertebereich von uint16 ist 0 ... 65.536
Der Wert 65.536 passt 81920 mal rein. Rest 0.

:-)  :-)  :-)

Alles klar, habe micht selbst geschlagen.
Ich hatte nicht erwartet das es beim letzen Überlauf ohne Rest aufgeht.

Jetzt bin ich glücklich mit mir selbst.   :-)

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.