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 ?
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 ?
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
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;
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
inta=20;
2
intb;
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
inta=20;
2
intb;
3
b=++a;
4
printf("%i\n",a);
5
printf("%i\n",b);
Ergebnis:
a = 21
b = 21
Nun zur Frage von ebm:
1
inta=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.
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?
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.
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.
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
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.
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.
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
voidcalc(intb,intc)
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
inta=8;
2
intb=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.
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. :-)
@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.
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
voidfoo(intx){
2
inty=x;
3
}
4
5
main(){
6
inta=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
inta=0;
2
3
voidfoo(intx){
4
inty=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 );
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
inta=0,b;
2
b=a++?a++:5;
a=1, b=5
und
1
inta=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
inta=3;
2
if(a++==4&&a++==5)
3
intxxx=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:
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
Random ... schrieb:> Moment mal ... **visualStudioAnwerf**
moment mal
Hier gehts nicht um Preinkrement oder Postinkrement
Hier geht es darum, dass in
1
inta=0;
2
3
voidfoo(intx){
4
if(x==a+1)
5
printf("passt");
6
}
7
8
intmain(){
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.
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.
> 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 &&
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.
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 :-)
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
Wertvonaholen
2
um1erhöhen
3
Ergebniszwischenspeichern
4
5addieren
5
Zwischengespeichertesergebnisnachaschreiben
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.
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.
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
dieseshier
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
derhier
und erst dann dieses Argument
1
calc(a++,a++)
2
^
3
|
4
dashier
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.
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
>inta=0;
3
>
4
>voidfoo(intx){
5
>if(x==a+1)
6
>printf("passt");
7
>}
8
>
9
>intmain(){
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(intx)
2
{
3
printf("x = %i\n",x);
4
}
5
6
intmain()
7
{
8
inti=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.
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
inta=1;
2
j=a+++a;
hierfür erwarte ich:
j = 1 + 1;
a = a+1;
hier ists ganz klar undefiniert:
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.
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.
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?
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.
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.
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.
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.
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
voidfoo(intb,int*c)
2
{
3
}
4
5
intmain()
6
{
7
inta=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.
>> 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.
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.
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
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.
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.
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.
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.
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.
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
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ß.
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.