Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage C


von ebm (Gast)


Lesenswert?

Guten Tag,

bisher dachte ich
1
int x = 20;
2
a = a++;
3
printf("%d\n",a);

würde immer das Ergebnis 21 liefern. So habe ich es zumindest gelernt.
Jetzt sagt mir aber einer, dass man sich nicht darauf verlassen kann.
Die Variable a würde undefiniert sein ?

von Felix P. (fixxl)


Lesenswert?

x und a sind nicht dasselbe...

von Achim S. (Gast)


Lesenswert?

willst du uns ein x für ein a vormachen?

von micha (Gast)


Lesenswert?

Ist doch egal; da "a" nicht deklariert ist, compiliert der Code sowieso 
nicht.

von Peter II (Gast)


Lesenswert?

ebm schrieb:
> int x = 20;
> a = a++;
> printf("%d\n",a);

wenn man davon ausgeht das du mit x a meinst ist das schon definiert.
1
int a = 20;
2
a = a++ + a++;
3
printf("%d\n",a);

aber jetzt ist es nicht mehr definiert.

von ebm (Gast)


Lesenswert?

Oh ja, ein Tipfehler.
1
int a = 20;
2
a = a++;
3
printf("%d\n",a);

so war das gemeint.

"The rules for sequencing says that you can only update a variable once 
between sequence points. Here you try to update it two times, and this 
causes a to become undefined."

Stimm das ?

von Martin L. (maveric00)


Lesenswert?

Hallo,

ja, stimmt: Du versuchst, in einer Operation a einen Wert zuzuweisen und 
a zu inkrementieren. Es ist aber nicht definiert, in welcher Reihenfolge 
dies passiert. a++ bedeutet so viel wie a=a+1, allerdings ist nicht 
definiert, wann dieses ausgeführt wird. Es ist nur definiert, dass der 
Wert von "a" zurückgeliefert wird, und nicht der Wert von "a+1". Es kann 
nun eine der folgende Sequenzen auftreten:

Erwartet: temp = a; a = temp ; a = a + 1
Auch möglich: temp = a ; a = a + 1 ; a = temp

Dazu der C99-Standard:

"The result of the postfix ++ operator is the value of the operand. 
After the result is obtained, the value of the operand is incremented. 
(That is, the value 1 of the appropriate type is added to it.) See the 
discussions of additive operators and compound assignment for 
information on constraints, types, and conversions and the effects of 
operations on pointers. The side effect of updating the stored value of 
the operand shall occur between the previous and the next sequence 
point."

Was Du an sich wahrscheinlich anstelle von "a=a++;" gemeint hast, ist 
ein einfaches "a++;". Dies beinhaltet schon die Zuweisungsoperation.

Schöne Grüße,
Martin

von ebm (Gast)


Lesenswert?

Vielen Dank für die Erklärung

von Christian K. (the_kirsch)


Lesenswert?

Das ist wirklich undefiniert, da du nicht weißt was zuerst passiert.
In diesem speziellen Fall würde der Kompiler aus

a = a++;

ein

a = a;
a = a+1;

draus machen;


was aber bei
a = a + a++ + a;
passiert ist wirklich Kompilerabhängig;

von Cyberfuzzy (Gast)


Lesenswert?

Hallo,

das ist nicht undefiniert. Was zuerst passiert, bestimmt man dadurch, ob 
man a++ oder ++a schreibt. Allerdings macht das nur mit zwei 
verschiedenen Variablen Sinn.

Im folgenden Beispiel wird zuerst b der Wert von a zugewiesen, danach 
wird a inkrementiert:
1
int a = 20;
2
int b;
3
b = a++;
4
printf("%i\n", a);
5
printf("%i\n", b);
Ergebnis:
a = 21
b = 20

Und hier wird zuerst a inkrementiert, danach wird der Wert auch b 
zugewiesen:
1
int a = 20;
2
int b;
3
b = ++a;
4
printf("%i\n", a);
5
printf("%i\n", b);
Ergebnis:
a = 21
b = 21

Nun zur Frage von ebm:
1
int a = 20;
2
a = a++;
3
printf("%i\n", a);
a ist hier definitiv nicht undefiniert, aber der Code ist nicht 
sinnvoll.
Der Code wird folgendermaßen ausgeführt: Zuerst macht wird a der Wert 
von a zugewiesen, was relativ sinnlos ist, danach wird a inkrementiert. 
An dieser Stelle hätte es auch ein einfaches a++; getan.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Cyberfuzzy schrieb:
> das ist nicht undefiniert. Was zuerst passiert, bestimmt man dadurch, ob
> man a++ oder ++a schreibt. Allerdings macht das nur mit zwei
> verschiedenen Variablen Sinn.

Warum antwortest Du auf Fragen, die der TO nie gestellt hat?

von TM F. (p_richner)


Lesenswert?

Warum nicht einfach:
1
int a = 20;
2
a++;
3
printf("%i\n", a);
??

von Karl H. (kbuchegg)


Lesenswert?

Cyberfuzzy schrieb:

> a ist hier definitiv nicht undefiniert,
doch ist es.
Das hier genau das rauskommt, was du erwartest, ändert daran nichts.
Laut C Standard ist so etwas undefiniertes Verhalten und das hängt nicht 
davon ab, ob in den üblichen Implementierungen in allen Codepfaden 
letzten Endes dasselbe Ergebnis rauskommt. Der Versuch dasselbe Objekt 
zwischen 2 Sequence Points mehrmals zu beschreiben mündet in 
undefiniertem Verhalten. Punkt. Es gibt keinen Grund, da jetzt 
irgendeine Ausnahmesituation zu konstruieren und der C Standard tut das 
auch nicht.

: Bearbeitet durch User
von Cyberfuzzy (Gast)


Lesenswert?

Frage fom TO:
ebm schrieb:
> Die Variable a würde undefiniert sein ?

Meine Antwort:
Cyberfuzzy schrieb:
> das ist nicht undefiniert.

von Cyberfuzzy (Gast)


Lesenswert?

Karl Heinz schrieb:
>> a ist hier definitiv nicht undefiniert,
> doch ist es.

Wieso? Zur Unterscheidung gibt es ein Postinkrement (a++) und ein 
Präinkrement (++a).

Nachzulesen auf der folgenden Seite:

http://de.wikipedia.org/wiki/Inkrement_und_Dekrement#Wertzuweisungszeitpunkt_in_.C3.BCbergeordneten_Ausdr.C3.BCcken

Dort steht:
Wird eine Inkrement- oder Dekrement-Operation in einer Anweisung 
innerhalb eines anderen Ausdruckes verwendet, muss unterschieden werden, 
ob die Werterhöhung oder -verminderung vor oder nach der Auswertung des 
übergeordneten Ausdrucks stattfindet.

von Karl H. (kbuchegg)


Lesenswert?

Cyberfuzzy schrieb:
> Karl Heinz schrieb:
>>> a ist hier definitiv nicht undefiniert,
>> doch ist es.
>
> Wieso? Zur Unterscheidung gibt es ein Postinkrement (a++) und ein
> Präinkrement (++a).
>
> Nachzulesen auf der folgenden Seite:

Das ist nicht die Frage, um die es in diesem Thread und im bewussten 
Abschnitt deiner Antwort geht.

Das Verhalten von
1
  a = a++;
ist undefiniert. Punkt.
Nicht weil ich das so sage, nicht weil es eine Platform gibt, in der was 
anderes rauskommt, nicht weil .... sondern weil es als undefiniert 
definiert wurde. Und das hat auch nichts mit Prefix oder Postfix 
Inkrementen zu tun. Das Verhalten von
1
  a = ++a;
ist genauso undefiniert. Aus genau dem gleichen Grund, den ebm um 9:30 
schon zitiert hat.
Beitrag "Re: Verständnisfrage C"

: Bearbeitet durch User
von npn (Gast)


Lesenswert?

Cyberfuzzy schrieb:
> Wieso? Zur Unterscheidung gibt es ein Postinkrement (a++) und ein
> Präinkrement (++a).

Nicht das ++a oder a++ ist undefiniert, sondern lediglich der Ausdruck 
a=a++, weil zwei Veränderungen einer Variable innerhalb eines Ausdruckes 
eben undefiniert sind.

von Cyberfuzzy (Gast)


Lesenswert?

npn schrieb:
> Nicht das ++a oder a++ ist undefiniert, sondern lediglich der Ausdruck
> a=a++, weil zwei Veränderungen einer Variable innerhalb eines Ausdruckes
> eben undefiniert sind.

Ok, das leuchtet mir ein. Ich wusste nicht, dass zwei Veränderungen 
einer Variable innerhalb eines Ausdruckes undefiniert sind.

Dann möchte ich mich bei euch (Karl Heinz und npn) bedanken, dass ihr 
beim Aufklären des Missverständnisses sachlich geblieben seid :-) Das 
ist in diesem Forum leider nicht immer der Fall.

von Karl H. (kbuchegg)


Lesenswert?

Cyberfuzzy schrieb:
> npn schrieb:
>> Nicht das ++a oder a++ ist undefiniert, sondern lediglich der Ausdruck
>> a=a++, weil zwei Veränderungen einer Variable innerhalb eines Ausdruckes
>> eben undefiniert sind.

Die C Definition ist hier auf den ersten Blick nicht so einfach.
Genau heisst es: zwischen 2 Sequence Points.

Sequence Points sind:
* bei &&, || und dem Komma Operator
* das ? im ternären Operator ?:
* unmittelbar bevor eine Funktion aufgerufen wird
* am Ende einer Initialisierung
* am Ende einer sog. Full Expression (vereinfacht könnte man sagen: bei 
einem ;)

Die Bedeutung eines Sequence Points besteht darin, dass mit Erreichen 
desselben alle Nebeneffekte abgeschlossen sein müssen. Bis zum Erreichen 
eines Sequence Points (des relevanten Sequence Points) hat der Compiler 
alle Freiheiten, die Reihenfolge der Operationen umzudrehen und 
umzuordnen.

Genau deswegen ist zb in
1
   a = foo() + bar();
nicht definiert, ob zuerst foo und dann bar aufgerufen wird, oder 
umgekehrt. Der für die Addition relevante Sequence Point ist der ';' und 
das einzige Sichere was man sagen kann ist: wenn der Sequence Point am 
';' errreicht wird, sind die beiden Funktionen aufgerufen worden. Aber 
es ist nicht definiert in welcher Reihenfolge.

Der Sequence Point "unmittelbar bevor eine Funktion aufgerufen wird" 
bezieht sich auf die Auswertung von Funktionsargumenten.

D.h. wir wissen, dass in
1
   calc( a++, b++ );
die Inkrements sicher erfolgt sind und komplett abgearbeitet wurden, ehe 
die Funktion tatsächlich aufgerufen wird. Sind a und b von calc aus 
zugreifbar (weil zb globale Variablen), dann haben die bereits die 
inkrementierten Werte, wenn die Funktion läuft (unabhängig davon, dass 
die FUnktion die Werte auch als Funktionsargumente bekommen würde). 
Aber:
1
  a = 5;
2
  calc( a++, a++ );
wäre wieder undefiniert. Denn der Sequence Point greift ja wieder nur 
beim Funktionsaufruf. Und damit es dazu kommt, müssten 2 Updates von a 
erfolgen, was wiederrum undefiniertes Verhalten nach sich zieht.

Die Werte in calc
1
void calc( int b, int c )
2
{
3
}
für b und c sind nicht definiert.

Wohingegen es an
1
  if( a++ == 4 && a++ == 5 )
es nichts auszusetzen gibt. Denn && ist ein Sequence Point.
Genauso wie
1
   int a = 8;
2
   int b = a++, c = a++;
ebenfalls definiert sind.

Trickreicher ist zb
1
   b = a++ ? a++ : 5;
hier kann Entwarnung gegeben werden. Ist definiert, denn ? ist ein 
Sequence Point, der zwischen den beiden Inkrements steht. Wohingegen
1
  a = ( a++ ) ? a++ : 5;
kein definiertes Verhalten hat, weil zwischen dem Inkrement im 
abhängigen Teil und der Zuweisung kein Sequence Point mehr existiert. 
Der nächste Sequence Points wäre dann erst wieder der ; des kompletten 
Statements.

: Bearbeitet durch User
von npn (Gast)


Lesenswert?

Karl Heinz schrieb:
> Genau heisst es: zwischen 2 Sequence Points.

Na das ist doch mal wieder eine prima ausführliche Erläuterung.
Das war mir nicht alles bis in die letzte Konsequenz klar.
Danke auch von mir. :-)

von ebm (Gast)


Lesenswert?

Vielen Dank für die ausführlichen Erklärungen.

von lalala (Gast)


Lesenswert?

@Karl Heinz: Super Erklärung. Einziger Kommentar:

Karl Heinz schrieb:
> Die Werte in calcvoid calc( int b, int c )
> für b und c sind nicht definiert.
Ist im Prinzip richtig, aber eigentlich ist es doch schlimmer. Bei 
undefinierten Code ist doch eigentlich alles erlaubt (wenn ich mich 
richtig erinnere). Wobei wohl die meisten Compiler jetzt z.B. nicht 
anfangen die Festplatte zu löschen.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Moment mal ... **visualStudioAnwerf**

> D.h. wir wissen, dass in
>    calc( a++, b++ );
> die Inkrements sicher erfolgt sind und komplett abgearbeitet wurden, ehe
> die Funktion tatsächlich aufgerufen wird.

Teste ich das via:
1
void foo(int x) {
2
  int y = x;
3
}
4
5
main() {
6
  int a=0;
7
  foo(a++);
8
}

so ist der übergebene Wert 0, erst nach der Ausführung von foo() (man 
sieht das beim single step) wird das lokale a auf 1 incrementiert.

Korrekterweise ist ein globales a beim Eintritt in foo() bereits 
incrementiert, aber der übergebene Wert an die Funktion ist der vor dem 
Increment:
1
int a=0;
2
3
void foo(int x) {
4
  int y = x;
5
}
6
7
main() {
8
  foo(a++);
9
}

> Aber:
>   a = 5;
>   calc( a++, a++ );
> wäre wieder undefiniert. Denn der Sequence Point greift ja wieder nur
> beim Funktionsaufruf. Und damit es dazu kommt, müssten 2 Updates von a
> erfolgen, was wiederrum undefiniertes Verhalten nach sich zieht.

Für folgenden Ausdruck würde das gelten:
calc( ++a, ++a );

: Bearbeitet durch User
von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Trickreicher ist zb   b = a++ ? a++ : 5;
> hier kann Entwarnung gegeben werden. Ist definiert, denn ? ist ein
> Sequence Point, der zwischen den beiden Inkrements steht.

Auch das kann ich im VS nicht nachvollziehen:
1
int a = 0, b;
2
b = a++ ? a++ : 5;
a=1, b=5

und
1
int a = 1, b;
2
b = a++ ? a++ : 5;
a=3, b=2

Auch hier erfolgt der Increment nach der Auswertung (wie ich es bei 
PostInc auch erwarten würde).

Auch hier ist das anders:
1
int a = 3;
2
if( a++ == 4 && a++ == 5 )
3
    int xxx=0;
Ausdruck ist false (steigt vor dem ersten && aus weil bereits false), 
und endet mit a=4

Dieses Konstrukt ist aber typisch für Pointerabsicherung:
1
struct ... *p = foo();
2
if(p && p.x == 42) ...

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Random ... schrieb:
> Auch das kann ich im VS nicht nachvollziehen:
>
1
> int a = 0, b;
2
> b = a++ ? a++ : 5;
3
>
> a=1, b=5
>
> und
>
1
> int a = 1, b;
2
> b = a++ ? a++ : 5;
3
>
> a=3, b=2

Klingt doch plausibel und widerspricht auch nicht der Aussage von Karl 
Heinz.

Du kannst das übrigens 1:1 so umschreiben:
1
int a = 1, b;
2
3
if (a++)
4
{
5
    b = a++;
6
}
7
else
8
{
9
    b = 5;
10
}

von Dirk B. (dirkb2)


Lesenswert?

Random ... schrieb:
> Auch das kann ich im VS nicht nachvollziehen:

Was erwartest du für Werte?

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Klingt doch plausibel

In diesem Fall stimmts, da hab ich mich verrannt :-)
Die Zuweisung entspricht dem Wert des ersten Increments.

Hab mich verrant, weil lt. obiger Darstellung a in foo(a++) bereits 
incrementiert übergeben werden soll.

Vgl:
> D.h. wir wissen, dass in
>    calc( a++, b++ );
> die Inkrements sicher erfolgt sind und komplett abgearbeitet wurden, ehe
> die Funktion tatsächlich aufgerufen wird.

Und irgendwie ist
1
if( a++ == 4 && a++ == 5 )
doch immer false??

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:
> Moment mal ... **visualStudioAnwerf**

moment mal

Hier gehts nicht um Preinkrement oder Postinkrement

Hier geht es darum, dass in
1
int a=0;
2
3
void foo(int x) {
4
  if( x == a + 1)
5
    printf( "passt" );
6
}
7
8
int main() {
9
  foo(a++);
10
}

Der Vergleich in der Funktion sicher auf TRUE lautet und der printf 
daher kommen muss. Wenn foo loslegt, ist der Inkrement von a bereits 
erfolgt. Nicht irgendwann, nicht nachdem die Funktion zurückkehrt, 
sondern vor dem Aufruf der Funktion.


Das die Funktion je nachdem ob du einen Preincrement oder einen 
Postinkrement benutzt einen andern Wert kriegt, ist eine Binsenweisheit. 
Aber darum geht es nicht. Es geht darum, wann a seinen neuen Wert 
kriegt.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Random ... schrieb:
> Und irgendwie istif( a++ == 4 && a++ == 5 )
> doch immer false??

Wenn vorher a = 4 ist, ist die Bedingung wahr, da beim && das erste 
Postincrement schon ausgeführt wurde. Dann ist a = 5 und somit auch der 
zweite Teil der Bedingung wahr.

von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:
>
> Und irgendwie ist
>
1
> if( a++ == 4 && a++ == 5 )
2
>
> doch immer false??

wenn a ursprünglich den Wert 4 hatte, dann nicht. Dann ergibt der 
Ausdruck true. Und zwar 100% ganz sicher.
Da && ein Sequence Point ist, ist garantiert dass bei der Auswertung des 
rechten a++ == 5, a bereits den Wert hat, den es durch den linken Teil 
a++ == 4 bekommt. Der Compiler kann diesen Inkrement nicht weiter nach 
hinten schieben als bis zum &&

: Bearbeitet durch User
von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> D.h. wir wissen, dass in   calc( a++, b++ );
> die Inkrements sicher erfolgt sind und komplett abgearbeitet wurden,
> ehe die Funktion tatsächlich aufgerufen wird.
Irreführend erklärt. Die Symbole sind abgearbeitet, der übergebene Value 
ist jedoch der vorherige.

> Sind a und b von calc aus
> zugreifbar (weil zb globale Variablen), dann haben die bereits die
> inkrementierten Werte, wenn die Funktion läuft
Soweit korrekt.

> (unabhängig davon, dass
> die FUnktion die Werte auch als Funktionsargumente bekommen würde).
das ist eben nicht richtig, sonst macht der post-inc ja auch keinen 
Sinn.

> Aber:  a = 5;
>   calc( a++, a++ );
> wäre wieder undefiniert. Denn der Sequence Point greift ja wieder nur
> beim Funktionsaufruf. Und damit es dazu kommt, müssten 2 Updates von a
> erfolgen, was wiederrum undefiniertes Verhalten nach sich zieht.

Auch nicht, übergeben wird der Wert vor dem Increment. Beim 
Funktionseintritt ist a dann 2x incrementiert, die Funktionsparameter 
sind aber die vor den beiden inc.

> Die Werte in calcvoid calc( int b, int c )
> {
> }
> für b und c sind nicht definiert.

Wie ich das erwarte, sind die übergebenen Werte für b und c in VS 
diejenigen vor dem increment.

Auch, wenn das schon sehr sonderbare Fälle sind, die man besser nicht 
codiert :-), entspricht das beobachtbare Verhalten von VisualStudio 
genau dem, was ein post-inc machen sollte.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> wenn a ursprünglich den Wert 4 hatte, dann nicht. Dann ergibt der
> Ausdruck true. Und zwar 100% ganz sicher.
Da stimme ich dir zu :-)

von Karl H. (kbuchegg)


Lesenswert?

Random, du verwechselst da etwas.

Es geht NICHT um den Unterschied zwischen Preinkrement und Postinkrement 
und welcher Wert in weiterer Folge in einem Ausdruck benutzt wird: der 
vor dem Inkrement oder der danach.

Die Sache ist die, dass das Erhöhen der Variable ein Nebeneffekt ist, 
den der Compiler rumschieben darf, wie es ihm in den Kram passt.

Der COmpiler darf
1
  i = ++a + 5;

so übersetzen
1
   Wert von a holen
2
   um 1 erhöhen
3
   Ergebnis zwischenspeichern
4
   5 addieren
5
   Zwischengespeichertes ergebnis nach a schreiben

Hier ist der Update der Variablen ganz an das Ende der Sequenz geschoben 
worden. Nach Ausführung der Anweisung, beim Erreiehen des Sequence 
Points (in dem Fall der ;) ist der Nebeneffekt das a einen neuen Wert 
gekriegt hat abgeschlossen. Das die jeweiligen Werte korrekt sind, ist 
sowieso Voraussetzung. Es geht einzig und alleine darum, zu welchem 
Zeitpunkt sie diesen Wert während der Programmausführung bekommen. Und 
da sind Programmierer gerne mal zu der Annahme verleitet, dass alle 
Dinge genau in der Reihenfolge passieren, in der sie eine Progammzeile 
von links nach rechts lesen. Pustekuchen. Das ist nicht garantiert. Es 
ist eben, um bei dem Beispiel zu bleiben, nicht garantiert, dass der 
inkrementierte Wert bei ++a sofort nach a zurück geschrieben wird. 
Genausowenig wie es bei a++ garantiert ist. In
1
  j = a++ + a;

hast du KEINE Garantie, dass das 'rechte' a bereits die um 1 erhöhte 
Version vom 'linken' a ist.

In
1
  a = 2;
2
  j = a++ + a;

kann das Ergebnis lauten: a gleich 3 und j gleich 5. Aber es ist nicht 
garantiert. Das Ergebnis könnte auch a gleich 3 und j gleich 4 lauten. 
Oder, wenn man es genau nimmt, könnte auch die Festplatte formatiert 
werden. Den letzten Fall mal ausgenommen, warum kann j 5 oder 4 sein?
WEil der Compiler den Update von a nach dem Inkrement innerhalb der 
Anweisung rumschieben darf, wie er will. Die erzeugte Sequenz
1
   Wert von a holen
2
   mit dem Wert von a addieren
3
   Ergebnis an j zuweisen
4
   a um 1 erhöhen und in a speichern
ist KORREKT! Der Nebeneffekt, dass a um 1 erhöht wird, ist beim 
Erreichen des Sequence Points abgeschlossen. Und mehr wird nicht 
gefordert.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Random ... schrieb:
> Wie ich das erwarte, sind die übergebenen Werte für b und c in VS
> diejenigen vor dem increment.

Hier wird darüber diskutiert, wie der C-Standard die Sachen auslegt und 
nicht irgendein Compiler.
Das Verhalten kann (und darf, da es undefiniert ist) auf einem andern 
Compiler oder in einer neueren Version ganz anders aussehen.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

1
calc( a++, a++ );

Da passt aber trotzdem was nicht ...
Hier würde ich erwarten, dass der Inc erst nach der Feststellung der 
Übergabeparameter (also beide 0) erfolgt.
1
int a=0;
2
3
int foo() {
4
  return a++;
5
//return ++a;
6
}
7
8
calc( foo(), foo() );
Hier wäre das ganze aber ziemlich undefiniert, da der Compiler frei 
entscheiden kann, was er zuerst ausführt.

von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:
>
>> Sind a und b von calc aus
>> zugreifbar (weil zb globale Variablen), dann haben die bereits die
>> inkrementierten Werte, wenn die Funktion läuft
> Soweit korrekt.
>
>> (unabhängig davon, dass
>> die FUnktion die Werte auch als Funktionsargumente bekommen würde).
> das ist eben nicht richtig, sonst macht der post-inc ja auch keinen
> Sinn.

Das ist eben schon richtig.
Das Problem ist ein anderes. Das Problem ist, dass du den falschen Baum 
anpinkelst :-)


>> Aber:  a = 5;
>>   calc( a++, a++ );
>> wäre wieder undefiniert. Denn der Sequence Point greift ja wieder nur
>> beim Funktionsaufruf. Und damit es dazu kommt, müssten 2 Updates von a
>> erfolgen, was wiederrum undefiniertes Verhalten nach sich zieht.
>
> Auch nicht
Doch das tut es.

>, übergeben wird der Wert vor dem Increment.

Das ist aber gar nicht gefragt.
Gefragt ist, ob

  a = 5
  calc( a++, a++ )

die Werte 5 und 6, oder 5 und 5, oder 6 und 5 an die Funktion übergibt.
Wenn dieses a++ abgearbeitet wird
1
  calc( a++, a++ )
2
             ^
3
             |
4
           dieses hier
mit welchem Wert von a wird dann gerechnet? Mit 5 oder mit dem bereits 
inkrementierten Wert von a, der hier
1
  calc( a++, a++ )
2
        ^
3
        |
4
       hier
entstanden ist.

Oder ist es etwa umgekehrt, dass zuerst dieser Inkrement gemacht wird
1
  calc( a++, a++ )
2
             ^
3
             |
4
          der hier

und erst dann dieses Argument
1
  calc( a++, a++ )
2
        ^
3
        |
4
        das hier
mit dem bereits erhöhten Wert von a ausgewertet wird?

Oder ist es vielleicht so, dass zuerst die Argumentewerte an die 
Funktion bestimmt werden und dann, bevor die Funktion aufgerufen wird, 
die beiden Inkrements gemacht werden?

Die Antwort darauf lautet: undefiniert.

: Bearbeitet durch User
von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Random ... schrieb:
>> Moment mal ... **visualStudioAnwerf**
>
> moment mal
>
> Hier gehts nicht um Preinkrement oder Postinkrement
>
> Hier geht es darum, dass in
>
>
1
> 
2
> int a=0;
3
> 
4
> void foo(int x) {
5
>   if( x == a + 1)
6
>     printf( "passt" );
7
> }
8
> 
9
> int main() {
10
>   foo(a++);
11
> }
12
>
>
> Der Vergleich in der Funktion sicher auf TRUE lautet und der printf
> daher kommen muss. Wenn foo loslegt, ist der Inkrement von a bereits
> erfolgt. Nicht irgendwann, nicht nachdem die Funktion zurückkehrt,
> sondern vor dem Aufruf der Funktion.

So weit richtig, was allerdings auch daran liegt, dass hier eine 
globable Variable verwendet wird, die übrigens Pfui ist, also schlechter 
Programmierstil. Aber zum Aufzeigen als Beispiel sicherlich ausreichend.


>
> Das die Funktion je nachdem ob du einen Preincrement oder einen
> Postinkrement benutzt einen andern Wert kriegt, ist eine Binsenweisheit.

Nein, hier ist es nicht mehr richtig.

Wenn der Wert per Copy by Value an die Funktion übergeben wird,
dann ist der Wert den die Funktion erhält bei einem Preincrement bereits 
um 1 erhöht, was bei einem Postincrement aber nicht der Fall  ist.
1
foo (int x)
2
{
3
  printf("x = %i\n", x);
4
}
5
6
int main()
7
{
8
  int i = 0;
9
  foo (++i);
10
  i = 0; // wir resetten i auf 0
11
  foo (i++);
12
}


Ausgabe:
x = 1
x = 0



Prinzipiell sollte man seine Finger von globalen Variablen lassen.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> In  j = a++ + a;
>
> hast du KEINE Garantie, dass das 'rechte' a bereits die um 1 erhöhte
> Version vom 'linken' a ist.

Moment ... in dem Beispiel erwarte ich, dass das 'rechte' a keinesfalls 
erhöht ist, da es sich um einen post-inc handelt:
1
int a = 1;
2
j = a++ + a;
hierfür erwarte ich:
j = 1 + 1;
a = a+1;

hier ists ganz klar undefiniert:
1
int a = 1;
2
j = ++a + a;
a = a+1;
j = 2 + ?;

von Dirk B. (dirkb2)


Lesenswert?

Random ... schrieb:
> Hier würde ich erwarten, dass ...

Dann ist deine Erwartung falsch.
Es kann so sein wie du sagst, muss es aber nicht.


> Hier wäre das ganze aber ziemlich undefiniert,
Ja, das ist es

> da der Compiler frei
> entscheiden kann, was er zuerst ausführt.
Ja, das darf er.

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:

> So weit richtig, was allerdings auch daran liegt, dass hier eine
> globable Variable verwendet wird, die übrigens Pfui ist, also schlechter
> Programmierstil. Aber zum Aufzeigen als Beispiel sicherlich ausreichend.

Machst du das eigentlich absichtlich, dass du ständig irgendwelche 
Schauplätze aufmachst, die mit dem zu erklärenden überhaupt nichts zu 
tun haben und dafür konsequent verweigerst, das erklärte zu akzeptieren?

Es geht nicht um Preinkrement, es geht nicht um Postinkrement und welche 
Wert daher an die Funktion übergeben werden. Es geht auch nicht um 
globale Variablen an sich, sondern es geht einzig und alleine darum, 
welche Garantie ich für das Verhalten der Variablen (bzw Ausrücke in 
denen sie vorkommt) habe, die in der Argumentliste benutzt wurden.

Und ja. Ich seh zu, dass ich so einfache Beispiele wie möglich 
konstruiere. Damit man an ihnen nachvollziehen kann, wie sich das 
verhält. Ich kann dir gerne auch komplizierte Beispiele bauen, bei denen 
du nur noch mit den Ohren schlackerst.

: Bearbeitet durch User
von Buffer (Gast)


Lesenswert?

Profi schrieb:
> Prinzipiell sollte man seine Finger von globalen Variablen lassen.

So,so... Profis eben....

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:

> Wenn der Wert per Copy by Value an die Funktion übergeben wird,
> dann ist der Wert den die Funktion erhält bei einem Preincrement bereits
> um 1 erhöht, was bei einem Postincrement aber nicht der Fall  ist.

ok.
Damit du endlich mal von deinem Pre Postinkrement wegkommst.

Welche WErte werden übergeben?
1
  a = 5;
2
  calc( a = 7, a );

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> D.h. wir wissen, dass in   calc( a++, b++ );
> die Inkrements sicher erfolgt sind und komplett abgearbeitet wurden, ehe
> die Funktion tatsächlich aufgerufen wird. Sind a und b von calc aus
> zugreifbar (weil zb globale Variablen), dann haben die bereits die
> inkrementierten Werte, wenn die Funktion läuft
soweit korrekt.

> (unabhängig davon, dass die FUnktion die Werte auch als
> Funktionsargumente bekommen würde).
stimme ich nicht zu, mind. ein increment wirkt sich nicht auf die 
übergebenen Parameter aus.

: Bearbeitet durch User
von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Profi schrieb:
>
>> So weit richtig, was allerdings auch daran liegt, dass hier eine
>> globable Variable verwendet wird, die übrigens Pfui ist, also schlechter
>> Programmierstil. Aber zum Aufzeigen als Beispiel sicherlich ausreichend.
>
> Machst du das eigentlich absichtlich, dass du ständig irgendwelche
> Schauplätze aufmachst,

Was faselst du da?
Ich bin gerade erstmalig in die Diskussion eingestiegen.

Das mit dem undefiniert habe ich ja bestätigt, nur hast du im zweiten 
Teil deines Satzes eine falsche Behauptung aufgestellt, weil du etwas 
neues aufgemacht hast (-> Binsenweisheit) und da habe ich dich dann 
korrigiert weil es eben falsch war.

von Profi (Gast)


Lesenswert?

Buffer schrieb:
> Profi schrieb:
>> Prinzipiell sollte man seine Finger von globalen Variablen lassen.
>
> So,so... Profis eben....

Tja, Elektrotechnik mag nicht meine Domäne sein (wobei ich mit euch gut 
mithalten kann, wie der andere Thread zeigt), aber Informatik ist es 
sehr wohl und lass dir das gesagt sein, globale Variablen nutzt von uns 
keiner, es sei denn es ist eine absolute Ausnahme die einen derart 
großen Vorteil mit sich bringt, dass es zu rechtfertigen ist.

von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:

> stimme ich nicht zu, mind. ein increment wirkt sich nicht auf die
> übergebenen Parameter aus.

du hast es immer noch nicht verstanden.

WEnn ein Funktionsaufruf kein Sequence Point wäre, dann kann ein 
COmpiler
1
  foo( a++ );
so implementieren
1
  nimm dern Wert von a
2
  rufe die Funktion auf
3
  erhöhe a um 1
4
  speichere den erhöhten Wert in a ab

genauso wie er ein
1
  foo( ++a );
so implementieren darf
1
  nimm den Wert von a
2
  inkrementiere den Wert
3
  rufe die Funktion auf
4
  speichere den inkrementierten Wert zurück nach a

Das hätte Auswirkungen!

Da aber ein Funktionsaufruf ein Sequence Point ist, ist es nicht 
zulässig, das zurückschreiben des inkrementierten Wertes (egal ob Pre 
oder Postinkrement) hinter den Funktionsaufruf zu verschieben.

Sorry. Aber das ist das was der C-Standard dazu zu sagen hat. Nochmal 
kaue ich das ganze jetzt nicht nochmal durch.

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:
> Buffer schrieb:
>> Profi schrieb:
>>> Prinzipiell sollte man seine Finger von globalen Variablen lassen.
>>
>> So,so... Profis eben....
>
> Tja, Elektrotechnik mag nicht meine Domäne sein (wobei ich mit euch gut
> mithalten kann, wie der andere Thread zeigt), aber Informatik ist es
> sehr wohl und lass dir das gesagt sein, globale Variablen nutzt von uns
> keiner, es sei denn es ist eine absolute Ausnahme die einen derart
> großen Vorteil mit sich bringt, dass es zu rechtfertigen ist.

OK. Dann eben ohne globale
1
void foo( int b, int *c )
2
{
3
}
4
5
int main()
6
{
7
  int a = 5;
8
  int *pA = &a;
9
10
  foo( a++, pA );
11
}

welche Garantie habe ich vom C Standard bezüglich b und *c innerhalb der 
Funktion?


Denn die globale Variable ist einfach nur ein Nebenschauplatz, mit dem 
man den Effekt am einfachsten zeigen kann. Man kann aber auch beliebig 
kompliziertere andere Szenarien konstruieren, in denen genau dasselbe 
gilt. Mit deiner Kritik an globalen Variablen hast du nicht unrecht. 
Aber das ist nicht das Thema um das es geht. Es geht darum, welche 
Garantien ich bei einem Funktionsaufruf bezüglich der Auswertung der 
Argumente habe und welche Garantien ich nicht habe. Du reitest auf 
Nebenschauplätzen rum, die überhaupt nicht das Thema sind.

Und im übrigen: Auch wenn globale Variablen pfui sind, gibt es sie doch. 
Und auch dann muss klar sein, was garantiert ist und was nicht. Eine 
Sprachdefinition kann sich nicht einfach auf den Standpunkt stellen "Das 
ist pfui, also ignorieren wir das einfach. Jeder darf machen was er 
will".
Als INformatiker sollte dir das eigentlich klar sein, dass Regeln so zu 
gestalten sind, dass sie ALLE Fälle mit einschliessen. Auch wenn da 
globale Variablen im Spiel sind.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Random ... schrieb:
>
1
> calc( a++, a++ );
2
>
>
> Da passt aber trotzdem was nicht ...
> Hier würde ich erwarten,

was du erwartest, interessiert nicht.
Was interessiert ist, welche Zusagen mir der C-Standard gibt und welche 
er mir nicht gibt.
Denn im Zweifel gilt genau das, was im Standard-Dokument drinnen steht 
und nicht das was du erwartest.

: Bearbeitet durch User
von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Profi schrieb:
>> Tja, Elektrotechnik mag nicht meine Domäne sein (wobei ich mit euch gut
>> mithalten kann, wie der andere Thread zeigt), aber Informatik ist es
>> sehr wohl und lass dir das gesagt sein, globale Variablen nutzt von uns
>> keiner, es sei denn es ist eine absolute Ausnahme die einen derart
>> großen Vorteil mit sich bringt, dass es zu rechtfertigen ist.
>
> OK. Dann eben ohne globalevoid foo( int b, int *c )
> {
> }
>
> int main()
> {
>   int a;
>   int *pA = &a;
>
>   foo( a++, pA );
> }
>
> welche Garantie habe ich vom C Standard bezüglich b und *c innerhalb der
> Funktion?
>

Das ist nicht definiert, nichts anderes habe ich behauptet.

Außerdem ist a nicht korrekt initialisiert.

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:

> Das ist nicht definiert, nichts anderes habe ich behauptet.
>
> Außerdem ist a nicht korrekt initialisiert.

Ich war ein wenig in Rage als ich getippt habe und hab beim schnellen 
Tippen ein paar Fehler gemacht.

Korrigierte Version
1
void foo( int b, int *c )
2
{
3
  printf( "%d %d\n", b, *c );
4
}
5
6
int main()
7
{
8
  int a = 5;
9
  int *pA = &a;
10
11
  foo( a++, pA );
12
}

definiert oder nicht definiert?

: Bearbeitet durch User
von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Profi schrieb:
>
>> Das ist nicht definiert, nichts anderes habe ich behauptet.
>>
>> Außerdem ist a nicht korrekt initialisiert.
>
> Ich war ein wenig in Rage als ich getippt habe und hab beim schnellen
> Tippen ein paar Fehler gemacht.
>
> Korrigierte Versionint main()
> {
>   int a = 5;
>   int *pA = &a;
>
>   foo( a++, pA );
> }
>
> definiert oder nicht definiert?

Gemäß C Standard ist dies nicht definiert, ich habe auch nichts anderes 
behauptet.
Lies mal die Binsenweisheit oben, die du neu aufgemacht hast und 
verstehe.

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:

> Gemäß C Standard ist dies nicht definiert,

Doch. Das ist definiert.
Es ist deswegen definiert, weil der Aufruf einer Funktion ein Sequence 
Point ist. Damit muss der Inkrement ausserhalb der Funktion inklusive 
aller Nebeneffekte bereits abgeschlossen sein, ehe die Funktion 
aufgerufen wird. Und damit ist definiert, welchen Wert *c innerhalb der 
Funktion ergeben MUSS.

> ich habe auch nichts anderes
> behauptet.

Leider falsch.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Kurze Frage zwischendurch:
Sind Profi und Random ein und dieselbe Person?

Denn wenn nicht, dann kann es sein, dass ich im Eifer der Antworten da 2 
Teilnehmer durcheinander gebracht habe. Sorry dafür. Ich habs erst jetzt 
bemerkt, dass da 2 verschiedene Antworter im Spiel sind.

: Bearbeitet durch User
von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Kurze Frage zwischendurch:
> Sind Profi und Random ein und dieselbe Person?
>
> Denn wenn nicht, dann kann es sein, dass ich im Eifer der Antworten da 2
> Teilnehmer durcheinander gebracht habe. Sorry dafür. Ich habs erst jetzt
> bemerkt, dass da 2 verschiedene Antworter im Spiel sind.

Als Mod solltest du das doch an der IP erkennen können. Eine 
Möglichkeit, die alle anderen hier nicht haben.

Und ich bin definitiv nicht Random.

Karl Heinz schrieb:
> Profi schrieb:
>
>> Gemäß C Standard ist dies nicht definiert,
>
> Doch. Das ist definiert.
> Es ist deswegen definiert, weil der Aufruf einer Funktion ein Sequence
> Point ist. Damit muss der Inkrement ausserhalb der Funktion inklusive
> aller Nebeneffekte bereits abgeschlossen sein, ehe die Funktion
> aufgerufen wird. Und damit ist definiert, welchen Wert *c innerhalb der
> Funktion ergeben MUSS.

Ja.

Das mit der Binsenweisheit, worauf ich hinaus wollte, war dennoch 
Unsinn.

von Karl H. (kbuchegg)


Lesenswert?

Profi schrieb:
> Karl Heinz schrieb:
>> Kurze Frage zwischendurch:
>> Sind Profi und Random ein und dieselbe Person?
>>
>> Denn wenn nicht, dann kann es sein, dass ich im Eifer der Antworten da 2
>> Teilnehmer durcheinander gebracht habe. Sorry dafür. Ich habs erst jetzt
>> bemerkt, dass da 2 verschiedene Antworter im Spiel sind.
>
> Als Mod solltest du das doch an der IP erkennen können.

Ja. Aber ich schau nicht bei jeder Antwort auf die IP. Und es soll auch 
schon vorgekommen sein, dass sich IP zwischendurch ändern.

> Das mit der Binsenweisheit, worauf ich hinaus wollte, war dennoch
> Unsinn.

Was war daran Unsinn?

Dass es trivial ist, dass bei
1
  foo( ++a );
und
1
  foo( a++ );
foo einen anderen Wert kriegt? Müssen wir das wirklich ausdiskutieren, 
warum das eine Binsenweisheit ist und warum das mit dem, worum es in 
diesem Thread eigentlich geht, nicht das geringste zu tun hat?
Das Thema ist NICHT: welchen Wert kriegt die Funktion übergeben. Das 
Thema lautet: was passiert mit a und welchen Wert hat a zum Zeitpunkt an 
dem der Funktionsaufruf tatsächlich erfolgt.

: Bearbeitet durch User
von Gerhard (Gast)


Lesenswert?

Karl Heinz schrieb:
> Hier geht es darum, dass in
>
> int a=0;
>
> void foo(int x) {
>   if( x == a + 1)
>     printf( "passt" );
> }
>
> int main() {
>   foo(a++);
> }
>
> Der Vergleich in der Funktion sicher auf TRUE lautet und der printf
> daher kommen muss. Wenn foo loslegt, ist der Inkrement von a bereits
> erfolgt. Nicht irgendwann, nicht nachdem die Funktion zurückkehrt,
> sondern vor dem Aufruf der Funktion.
>
> Das die Funktion je nachdem ob du einen Preincrement oder einen
> Postinkrement benutzt einen andern Wert kriegt, ist eine Binsenweisheit.
> Aber darum geht es nicht. Es geht darum, wann a seinen neuen Wert
> kriegt.

So sehr ich deine Beiträge schätze, obiges Beispiel stimmt so nicht 
(kopiert, übersetzt, "passt nicht"); die if clause ist FALSE weil x 0 
ist wegen des Postincrements und a+1 gleich 2 sind weil a incrementiert 
wird, bevor man in die Funktion springt.
Darauf hat Profi meiner Meinung nach im Beitrag #3966425 mit seinem 
Beispiel unter anderem Bezug genommen:

> Wenn der Wert per Copy by Value an die Funktion übergeben wird,
> dann ist der Wert den die Funktion erhält bei einem Preincrement bereits
> um 1 erhöht, was bei einem Postincrement aber nicht der Fall  ist.
> foo (int x)
> {
>   printf("x = %i\n", x);
> }
>
> int main()
> {
>   int i = 0;
>   foo (++i);
>   i = 0; // wir resetten i auf 0
>   foo (i++);
> }
>
> Ausgabe:
> x = 1
> x = 0
>

Das der eigentliche Faden nicht über post / pre Increment geht mag sein, 
dein Beispiel scheint mir davon unabhängig aber falsch zu sein.

Gerhard

von Profi (Gast)


Lesenswert?

Karl Heinz schrieb:
> Profi schrieb:
>> Als Mod solltest du das doch an der IP erkennen können.
>
> Ja. Aber ich schau nicht bei jeder Antwort auf die IP. Und es soll auch
> schon vorgekommen sein, dass sich IP zwischendurch ändern.

Bevor du so eine Frage in den Raum stellst, solltest du als Mod aber 
schon erstmal auf die IP schauen, denn die anderen können das noch 
weniger beantworten.
Und da Random nach mir noch mind. einmal geantwortet hat, dürfte er 
nicht mehr die gleiche IP haben, wenn er sie geändert haben sollte.
Und wenn ich Random sein würde, dann müsste sich meine IP nach einem 
Posting von Random ebenfalls geändert haben müssen, wenn ich Random wäre 
und vom gleichen Internetanschluss aus schreiben würde.

Eine Ausnahme wäre lediglich, wenn ich von zwei verschiedenen Rechnern 
aus schreiben würde, dann könnte ich mich sowohl als Random als auch 
Profi ausgeben und dabei die jeweilige IP beibehalten, dies wäre dann 
nicht überprüfbar, aber wie wahrscheinlich ist das und warum sollte man 
so etwas überhaupt tun?
Und falls Random, so wie ich auch, einen gewöhnlichen Internetanschluss 
für Privatpersonen nutzen sollte, der auf zwei Haushalte schließen 
lassen müsste, dann wäre das doch schon sehr sehr unwahrscheinlich.

Wäre Random z.B. bei der Telekom und ich bei Vodafon und würde Randoms 
IP auf Hamburg hinweisen und meine auf z.B. Stuttgart, dann wären hier 
mit Sicherheit zwei Wohnungen im Spiel und in beiden müsste ein Rechner 
laufen.
Wäre ich und Random dann die gleiche Person, dann würde das bedeuten, 
dass, obwohl ich zum Beispiel in Stuttgart physikalisch anwesend sein 
könnte, meinen Rechner in Hamburg rund um die Uhr unbeaufsichtigt laufen 
lassen müsste.
Das ist zwar nicht gänzlich auszuschließen, aber doch sehr 
unwahrscheinlich.




>
>> Das mit der Binsenweisheit, worauf ich hinaus wollte, war dennoch
>> Unsinn.
>
> Was war daran Unsinn?
>
> Dass es trivial ist, dass bei  foo( ++a );
> und  foo( a++ );
> foo einen anderen Wert kriegt? Müssen wir das wirklich ausdiskutieren,
> warum das eine Binsenweisheit ist und warum das mit dem, worum es in
> diesem Thread eigentlich geht, nicht das geringste zu tun hat?

Wenn es beiläufig erwähnt wird und das nicht korrekt gesagt wird, ist 
eine Richtigstellung IMO durchaus sinnvoll.

> Das Thema ist NICHT: welchen Wert kriegt die Funktion übergeben. Das
> Thema lautet: was passiert mit a und welchen Wert hat a zum Zeitpunkt an
> dem der Funktionsaufruf tatsächlich erfolgt.

Ich weiß.

von Karl H. (kbuchegg)


Lesenswert?

Gerhard schrieb:

> So sehr ich deine Beiträge schätze, obiges Beispiel stimmt so nicht
> (kopiert, übersetzt, "passt nicht"); die if clause ist FALSE weil x 0
> ist wegen des Postincrements und a+1 gleich 2 sind weil a incrementiert
> wird, bevor man in die Funktion springt.
> Darauf hat Profi meiner Meinung nach im Beitrag #3966425 mit seinem
> Beispiel unter anderem Bezug genommen:

Mea culpa.
OK, das erklärt es.

Das hätte eigentlich
1
  if( x + 1 == a )
lauten müssen bzw. war so gedacht.


> Das der eigentliche Faden nicht über post / pre Increment geht mag sein,
> dein Beispiel scheint mir davon unabhängig aber falsch zu sein.

Das allerdings ist richtig, dass das Beispiel falsch war.
Danke für den Hinweis.

Notiz an mich: Codebeispiele wieder vorher compilieren und testen.

: Bearbeitet durch User
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.