Discussion:
getchar() liefert unplausible Werte
(zu alt für eine Antwort)
Johan Pfeiffer
2014-02-26 09:31:04 UTC
Permalink
Raw Message
Hallo,

ich lese mit getchar() Zeichen von stdin
und zähle Leerzeichen Tab und Zeilenende.

Wenn ich nun den nachfolgenden Text eingebe:

Das<TAB>ist eine Zeile Text.

müsste ich 1 TAB 3 Leerzeichen und eine Zeile erhalten.

Ich erhalte aber 2 TAB Zeichen, 259 Leerzeichen und 1 Zeile.

Nach meiner Doku ist getchar ein MAKRO welches das nächste Zeichen nach
Konvertierung in den Typ int aus stdin zurückgibt.
Daher kann ich das oben geschilderte Ergebnis nicht nachvollziehen.

Kann mir da jemand helfen?

Danke Jo
Achim Peters
2014-02-26 11:58:09 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
ich lese mit getchar() Zeichen von stdin
und zähle Leerzeichen Tab und Zeilenende.
Das<TAB>ist eine Zeile Text.
müsste ich 1 TAB 3 Leerzeichen und eine Zeile erhalten.
Ich erhalte aber 2 TAB Zeichen, 259 Leerzeichen und 1 Zeile.
Ich vermute, das liegt nicht an getchar(), sondern an Deinem Programm.

Da Du uns den Code aber vorenthältst, bleibt es bei einer Vermutung.

Bye
Achim
Johan Pfeiffer
2014-02-26 14:48:28 UTC
Permalink
Raw Message
Post by Achim Peters
Post by Johan Pfeiffer
ich lese mit getchar() Zeichen von stdin
und zähle Leerzeichen Tab und Zeilenende.
Das<TAB>ist eine Zeile Text.
müsste ich 1 TAB 3 Leerzeichen und eine Zeile erhalten.
Ich erhalte aber 2 TAB Zeichen, 259 Leerzeichen und 1 Zeile.
Ich vermute, das liegt nicht an getchar(), sondern an Deinem Programm.
Da Du uns den Code aber vorenthältst, bleibt es bei einer Vermutung.
Vielen Dank für die Antwort, ich wollte nicht so aufdringlich sein.
Ich find einfach keine Erklärung für die Ausgabe warum 2 TAB Zeichen und
wo sollen die 259 Leerzeichen herkommen?
Ich führe das Programm auf der Konsole aus und beende es mit Crtl Z.
Dann rufe ich es wieder und und wieder auf. Die Ausgabe ist immer gleich.
Ich dachte vielleicht ist das was im Eingabepuffer? Aber wo soll das
herkommen?

Hier nun der Quellcode:

#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
int c;
int leerz, tab, zeilen = 0;

while( (c = getchar()) != EOF) {

if (c == ' ')
++leerz;

if (c == '\t')
++tab;

if (c == '\n')
++zeilen;
}
printf("Leerzeichen: %d Tabulator: %d Zeilen: %d\n", leerz,tab,zeilen);
return 0;
}

Viele Grüße Jo
Johan Pfeiffer
2014-02-26 14:57:04 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Achim Peters
Post by Johan Pfeiffer
ich lese mit getchar() Zeichen von stdin
und zähle Leerzeichen Tab und Zeilenende.
Das<TAB>ist eine Zeile Text.
müsste ich 1 TAB 3 Leerzeichen und eine Zeile erhalten.
Ich erhalte aber 2 TAB Zeichen, 259 Leerzeichen und 1 Zeile.
Ich vermute, das liegt nicht an getchar(), sondern an Deinem Programm.
Da Du uns den Code aber vorenthältst, bleibt es bei einer Vermutung.
Vielen Dank für die Antwort, ich wollte nicht so aufdringlich sein.
Ich find einfach keine Erklärung für die Ausgabe warum 2 TAB Zeichen und
wo sollen die 259 Leerzeichen herkommen?
Ich führe das Programm auf der Konsole aus und beende es mit Crtl Z.
Dann rufe ich es wieder und und wieder auf. Die Ausgabe ist immer gleich.
Ich dachte vielleicht ist das was im Eingabepuffer? Aber wo soll das
herkommen?
#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
/* > int c;
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
*/

Wenn ich
int leerz = 0;
int tab = 0;
int zeile = 0;
schreibe funktioniert es korrekt!
Ich muss also jede Variable einzeln definieren (vorbesetzen).
Post by Johan Pfeiffer
while( (c = getchar()) != EOF) {
if (c == ' ')
++leerz;
if (c == '\t')
++tab;
if (c == '\n')
++zeilen;
}
printf("Leerzeichen: %d Tabulator: %d Zeilen: %d\n", leerz,tab,zeilen);
return 0;
}
Viele Grüße Jo
Juergen Ilse
2014-02-26 17:00:39 UTC
Permalink
Raw Message
Hallo,
Post by Johan Pfeiffer
Wenn ich
int leerz = 0;
int tab = 0;
int zeile = 0;
schreibe funktioniert es korrekt!
Ich muss also jede Variable einzeln definieren (vorbesetzen).
Nein, du kannst die Deklarationen auch zusammenpacken, aber du musst jede
Variable initialisieren, bevor du ihren Wert verwendest (der ++ Operatror
verwendet den vorherigen Wert der Variablen), sonst bekommst du u.U rein
zufaellige Ergebnisse. Die Definition dieser 3 Variablen kannst du (ein-
schliesslich Initialisierung) auch so schreiben:

int leerz = 0, tab = 0, zeile = 0;

In deiner urspruenglichen Fassung hast du nur eine der Variablen
mit 0 initialisiert. Du haettest die Variablen natuerlich auch
noch nach der Deklaration initialisieren koennen:

int leerz, tab, zeile;
leerz = 0;
tab = 0;
zeile = 0;

In dem Fall ist es auch gleichgueltig, an welcher Stelle die
Initialisierung passiert, solange es in deinem Programm vor
deiner Schleife (der ersten Benutzung des Wertes der Variablen)
passiert.

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
David Seppi
2014-02-27 17:49:03 UTC
Permalink
Raw Message
Post by Juergen Ilse
Du haettest die Variablen natuerlich auch
int leerz, tab, zeile;
leerz = 0;
tab = 0;
zeile = 0;
Geht dann natürlich auch so, falls man auf weniger Zeilen steht:

int leerz, tab, zeile;
leerz = tab = zeile = 0;
--
David Seppi
1220 Wien
Johan Pfeiffer
2014-02-27 19:42:13 UTC
Permalink
Raw Message
Post by Juergen Ilse
Post by Juergen Ilse
Du haettest die Variablen natuerlich auch
int leerz, tab, zeile;
leerz = 0;
tab = 0;
zeile = 0;
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja das ist es!
In C zu programmieren, ist doch wie Geige spielen, hat man doch alle
Freiheiten auf Brett und dann noch mit dem Bogen. :-)

C ist glasklar nur seh ich es noch nicht.

Vielen Dank für den Beitrag.
--
Grüße Jo
Juergen Ilse
2014-03-01 09:06:33 UTC
Permalink
Raw Message
Hallo,
Post by Juergen Ilse
Post by Juergen Ilse
Du haettest die Variablen natuerlich auch
int leerz, tab, zeile;
leerz = 0;
tab = 0;
zeile = 0;
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).

Tschuess,
Juergen Ilse (***@usenet--verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Johan Pfeiffer
2014-03-01 14:29:51 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Hallo,
Post by Juergen Ilse
Post by Juergen Ilse
Du haettest die Variablen natuerlich auch
int leerz, tab, zeile;
leerz = 0;
tab = 0;
zeile = 0;
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
--
Grüße Jo
Juergen Ilse
2014-03-01 16:28:56 UTC
Permalink
Raw Message
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
Ja.
Post by Johan Pfeiffer
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0. Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht. Das spielt aber auch
keine Rolle, da nicht der Wert der Variablen zeile sondern der Wert
der Zuweisung an tab zugewiesen wird. Das ist ein Unterschied, auch
wenn der Unterschied i.d.R. bei standatdkonformen Programmen nicht
unbedingt relevant ist.

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Johan Pfeiffer
2014-03-01 19:04:42 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
Ja.
Post by Johan Pfeiffer
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0. Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht. Das spielt aber auch
keine Rolle, da nicht der Wert der Variablen zeile sondern der Wert
der Zuweisung an tab zugewiesen wird. Das ist ein Unterschied, auch
wenn der Unterschied i.d.R. bei standatdkonformen Programmen nicht
unbedingt relevant ist.
Der übersetzte Quellcode wie folgt:
myCprog: leerz = tab = zeilen = 0;
00401212 33C0 xor eax,eax
00401214 8945F0 mov [ebp-$10],eax
00401217 8945F4 mov [ebp-$0c],eax
0040121A 8945F8 mov [ebp-$08],eax
0040121D EB1B jmp $0040123a

XOR setzt das Register EAX auf den Wert 0
dann wird mit Zieladresse, EAX und eben nicht Zwischenergebnis alles auf
den Wert 0 gesetzt.
nur zur Vollständigkeit - mit JMP (GOTO in Assembler) wird der
Programmabschnitt verlassen.

Das hat ja nichts mehr mit C zu tun. Es ist das produktive Ergebnis des
Compilers.
--
Grüße Jo
Achim Peters
2014-03-01 19:28:09 UTC
Permalink
Raw Message
On 01.03.2014 20:04, Johan Pfeiffer wrote:

[Assembler]
Post by Johan Pfeiffer
Es ist das produktive Ergebnis des
Compilers.
s/des/eines

Eine anderer Compiler oder sogar eine andere Version desselben Compilers
oder sogar dieselbe Version desselben Compilers auf einer anderen
Plattform kann da etwas ganz anderes draus erzeugen.

Bye
Achim
Juergen Ilse
2014-03-01 20:16:11 UTC
Permalink
Raw Message
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Post by Johan Pfeiffer
Post by Juergen Ilse
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0. Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht. Das spielt aber auch
keine Rolle, da nicht der Wert der Variablen zeile sondern der Wert
der Zuweisung an tab zugewiesen wird. Das ist ein Unterschied, auch
wenn der Unterschied i.d.R. bei standatdkonformen Programmen nicht
unbedingt relevant ist.
Es ist voellig Wumpe wie der von einem bestimmten Compiler mit
bestimmten Optionen uebersetzt fuer ein Maschinencoder generiert
wird. Niemand garantiert dir, dass der Code mit anderen Compiler-
optionen oder mit der naechsten Compiler-Version noch genauso
aussieht ...
Ein populaerer Fall, wo sich Entwickler auf so etwas (zu Unrecht)
verlassen haben, war der Linux-Kernel Version 2.0.35: mit gcc-2.7
kam ein lauffaehiger Kernel heraus, mit gcc-2.8 oder egcs-1.0
uebersetzt crashte das System beim booten, weil der Optimizer bei
diesen Compilern ein kleines bischen leistungsfaehiger geworden ist.
Post by Johan Pfeiffer
Das hat ja nichts mehr mit C zu tun. Es ist das produktive Ergebnis des
Compilers.
... das aber nicht fuer jeden Compiler oder bei gleichem Compiler
mit jeder Kombination von Compileroptionen genauso aussehen muss ...
Das Thema dieser Gruppe ist das Verhalten, dass der C-Standard vor-
schreibt, nicht das, was ein bestimmter Compiler bei uebersetzen mit
bestimmten Optionen vielleicht gerade erzeugt.

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Johan Pfeiffer
2014-03-02 08:35:42 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Hallo,
Post by Juergen Ilse
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Post by Johan Pfeiffer
Post by Juergen Ilse
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0. Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht. Das spielt aber auch
keine Rolle, da nicht der Wert der Variablen zeile sondern der Wert
der Zuweisung an tab zugewiesen wird. Das ist ein Unterschied, auch
wenn der Unterschied i.d.R. bei standatdkonformen Programmen nicht
unbedingt relevant ist.
Damit wird das Ergebnis des Ausdrucks von zeile=0 Bestandteil des
Ausdrucks tab = ... und dieses Ergebnis wiederum Bestandteil des
Ausdrucks leert = ...
das würde auch dem Ausdruck (leerz=(tab=(zeile=0))) entsprechen.

Es ist eben keine nacheineander durchgeführte Wertzuweisung von 0.

Ich denke ich hab das jetzt verstanden.
--
Grüße Jo
Thomas Richter
2014-03-02 10:07:24 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Hallo,
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Ja, äh... Hier verwechselst Du zwei Dinge: Die Priorität der Operatoren,
und die Reihenfolge, in der die sich aus der Priorität ergebenden
Seiteneffekt ausgeführt werden. Der Punkt ist eben, dass die Reihenfolge
nur bis auf Sequence-Points festgelegt ist. Alles, was dazwischen liegt,
kann in *beliebiger Reihenfolge* ausgeführt werden, vorausgesetzt die
sich ergebenden Werte entsprechen den Regeln der Semantik der Sprache.

Will sagen: Es ist in diesem Falle zwar festgelegt, dass "tab" den Wert
der Zuweisung "zeile = 0" erhält, aber nicht *wann* er diesen erhält.

Der Compiler kann natürlich diesen Wert schon erschließen (nämlich 0)
ohne die Zuweisung auszuführen (also den Seiteneffekt zuzusichern).
Alles, was der Standard hier zu sagen hat, ist dass alle drei
Seiteneffekte beim nächsten Sequence-Point - dem Semikolon - ausgeführt
sein müssen. Aber nicht in welcher Reihenfolge.
Post by Johan Pfeiffer
Damit wird das Ergebnis des Ausdrucks von zeile=0 Bestandteil des
Ausdrucks tab = ... und dieses Ergebnis wiederum Bestandteil des
Ausdrucks leert = ...
das würde auch dem Ausdruck (leerz=(tab=(zeile=0))) entsprechen.
Es ist eben keine nacheineander durchgeführte Wertzuweisung von 0.
Es ist eine Wertezuweisung des Wertes von 0. Nicht "nacheinander", weil
es keinen Sequence-Point gibt, und die Reihenfolge der Zuweisungen
ergibt sich nicht aus der Priorität der Operatoren. Diese legt lediglich
fest, welchen Wert die Zuweisung hat, aber nicht, wann diese ausgeführt
wird.

Grüße,
Thomas
Claus Reibenstein
2014-03-02 11:38:37 UTC
Permalink
Raw Message
Post by Thomas Richter
Seiteneffekt
Auch so ein Unwort, das aus schlampiger Übersetzung des englischen
Originals (side effect) entstanden ist.

"Nebenwirkung" wäre das korrekte deutsche Wort.

Gruß
Claus
Johan Pfeiffer
2014-03-02 14:17:32 UTC
Permalink
Raw Message
Post by Thomas Richter
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Hallo,
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Ja, äh... Hier verwechselst Du zwei Dinge: Die Priorität der Operatoren,
und die Reihenfolge, in der die sich aus der Priorität ergebenden
Seiteneffekt ausgeführt werden. Der Punkt ist eben, dass die Reihenfolge
nur bis auf Sequence-Points festgelegt ist. Alles, was dazwischen liegt,
kann in *beliebiger Reihenfolge* ausgeführt werden, vorausgesetzt die
sich ergebenden Werte entsprechen den Regeln der Semantik der Sprache.
Will sagen: Es ist in diesem Falle zwar festgelegt, dass "tab" den Wert
der Zuweisung "zeile = 0" erhält, aber nicht *wann* er diesen erhält.
Der Compiler kann natürlich diesen Wert schon erschließen (nämlich 0)
ohne die Zuweisung auszuführen (also den Seiteneffekt zuzusichern).
Alles, was der Standard hier zu sagen hat, ist dass alle drei
Seiteneffekte beim nächsten Sequence-Point - dem Semikolon - ausgeführt
sein müssen. Aber nicht in welcher Reihenfolge.
Ich hab das im Original nachgelesen. Die Reihenfolge ist nicht
festgelegt.

Semantics

An assignment operator stores a value in the object designated by
the left operand. An assignment expression has the value of the left
operand after the assignment, but is not an lvalue. The type of an
assignment expression is the type of the left operand unless the left
operand has qualified type, in which case it is the unqualified
version of the type of the left operand. The side effect of updating
the stored value of the left operand shall occur between the previous
and the next sequence point.

und das Wesentliche

The order of evaluation of the operands is unspecified.
Post by Thomas Richter
Post by Johan Pfeiffer
Damit wird das Ergebnis des Ausdrucks von zeile=0 Bestandteil des
Ausdrucks tab = ... und dieses Ergebnis wiederum Bestandteil des
Ausdrucks leert = ...
das würde auch dem Ausdruck (leerz=(tab=(zeile=0))) entsprechen.
Es ist eben keine nacheineander durchgeführte Wertzuweisung von 0.
Es ist eine Wertezuweisung des Wertes von 0. Nicht "nacheinander", weil
es keinen Sequence-Point gibt, und die Reihenfolge der Zuweisungen
ergibt sich nicht aus der Priorität der Operatoren. Diese legt lediglich
fest, welchen Wert die Zuweisung hat, aber nicht, wann diese ausgeführt
wird.
Ich zitiere aus der deutschen Ausgabe von K&R 2. Ausgabe Seite 20
wie folgt:
Die Anweisung
nl = nw = nc = 0;
weist allen drei Variablen den Wert 0 zu. Dies ist kein Sonderfall,
sondern eine Konsequenz der Tatsache, daß jede Zuweisung ein
Ausdruck ist, und somit einen Wert besitzt, und daß Zuweisungen von
rechts nach links ausgeführt werden. Wir könnten dies auch
so formulieren: nl = (nw = (nc = 0));

Damit habe ich gedanklich eine Reihenfolge verbunden die so nicht
festgelegt ist.

In der Originaldoku Ansi C und C99 von der GNU Webseite steht aber:
The order of evaluation of the operands is unspecified.
--
Grüße Jo
Stefan Reuther
2014-03-02 09:57:58 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Nein, ist sie nicht.

Es ist festgelegt, dass die Operatoren von rechts nach links gruppiert
werden, 'leerz = tab = zeile = 0' also als 'leerz = (tab = (zeile = 0))'
geklammert wird. Es ist aber eben NICHT festgelegt, in welcher
Reihenfolge das tatsächlich _ausgeführt_ wird. Deswegen ist ein Ausdruck
wie 'a[i] = i = j' nicht zulässig (undefiniertes Verhalten), auch wenn
der Compiler ihn meistens akzeptieren wird.


Stefan
Jens Schmidt
2014-03-02 11:21:26 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Juergen Ilse
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Nein, eben nicht. Die Abarbeitungsreihenfolge ist überhaupt nicht
festgelegt. Das ist eine as-if Regel. Es muss nur sichergestellt sein,
das bei Einhaltung der sonstigen Regeln, hier insbesondere
"keine mehrfache Änderung einer Variablen zwischen Sequenzpunkten",
das selbe Ergebnis herauskommt.

Hier mal ein extremes Beispiel:

------ schnipp ------
#include <stdio.h>

#define SPEICHER 100
static int speicher [SPEICHER];
static int adressen [SPEICHER];

int *f (int i)
{
static int frei = 0;

if (adressen [i] == 0)
adressen [i] = ++frei;
return &speicher [adressen [i]];
}

int g (void)
{
return 42;
}

void show (int i)
{
printf ("Variable %d liegt in speicher[%d] und hat Wert %d\n",
i, adressen [i], speicher [adressen [i]]);
}

int main (void)
{
*f(1) = *f(2) = *f(3) = g();
show (1);
show (2);
show (3);
return 0;
}
------ schnipp ------

Es gibt keine Regel, welcher der drei Aufrufe von f() in welcher
Reihenfolge erfolgt, und wie der Aufruf von g() damit zusammenhängt.
Genaugenommen kann das auch parallel erfolgen. Wir haben also
undefiniertes Verhalten im if()-Teil von f(), solange kein Lock eine
Serialisierung erzwingt.
Das einzige, was sicher ist, ist dass jeweils beide Funktionsaufrufe
links und rechts einer Zuweisung erfolgen, bevor die Zuweisung selber
ausgeführt wird.

Bei mir hier kommt übrigens das heraus:
Variable 1 liegt in speicher[1] und hat Wert 42
Variable 2 liegt in speicher[2] und hat Wert 42
Variable 3 liegt in speicher[3] und hat Wert 42
Die Aufrufe von f() erfolgten also von links nach rechts.

Ganz lustig wird das, wenn die Zuweisung nicht an ein Element von
speicher, sondern eins von adressen passiert.
--
Viele Grüße,
Jens Schmidt
Peter J. Holzer
2014-03-02 14:46:38 UTC
Permalink
Raw Message
Post by Jens Schmidt
------ schnipp ------
#include <stdio.h>
#define SPEICHER 100
static int speicher [SPEICHER];
static int adressen [SPEICHER];
int *f (int i)
{
static int frei = 0;
if (adressen [i] == 0)
adressen [i] = ++frei;
return &speicher [adressen [i]];
}
int g (void)
{
return 42;
}
void show (int i)
{
printf ("Variable %d liegt in speicher[%d] und hat Wert %d\n",
i, adressen [i], speicher [adressen [i]]);
}
int main (void)
{
*f(1) = *f(2) = *f(3) = g();
show (1);
show (2);
show (3);
return 0;
}
------ schnipp ------
Es gibt keine Regel, welcher der drei Aufrufe von f() in welcher
Reihenfolge erfolgt, und wie der Aufruf von g() damit zusammenhängt.
Soweit richtig.
Post by Jens Schmidt
Genaugenommen kann das auch parallel erfolgen. Wir haben also
undefiniertes Verhalten im if()-Teil von f(), solange kein Lock eine
Serialisierung erzwingt.
Nein. Diese Frage wurde seinerzeit in comp.std.c ausführlich diskutiert
und inzwischen im Standard geklärt:

| Evaluations A and B are indeterminately sequenced when A is sequenced
| either before or after B, but it is unspecified which.13)
[...]
| 13) The executions of unsequenced evaluations can interleave.
| Indeterminately sequenced evaluations cannot interleave, but can be
| executed in any order.
[...]
| There is a sequence point after the evaluations of the function
| designator and the actual arguments but before the actual call. Every
| evaluation in the calling function (including other function calls) that
| is not otherwise specifically sequenced before or after the execution of
| the body of the called function is indeterminately sequenced with
| respect to the execution of the called function.94)
[...]
| 94) In other words, function executions do not ‘‘interleave’’ with each
| other.

Die drei Aufrufe von f sowie der Aufruf von g können also in beliebiger
Reihenfolge aber nicht gleichzeitig/überlappend erfolgen.
Post by Jens Schmidt
Das einzige, was sicher ist, ist dass jeweils beide Funktionsaufrufe
links und rechts einer Zuweisung erfolgen, bevor die Zuweisung selber
ausgeführt wird.
Da bin ich mir nicht so sicher. Wir haben hier 6 Sequence-Points:

1) Vor dem Ausdruck.
2) nach der Evaluation von f und 1 und vor dem Aufruf von f(1)
3) nach der Evaluation von f und 2 und vor dem Aufruf von f(2)
4) nach der Evaluation von f und 3 und vor dem Aufruf von f(3)
5) nach der Evaluation von g und vor dem Aufruf von g()
6) nach dem Ausdruck.

Die Zuweisungen müssen zwischen 1 und 6 passieren. 2 bis 5 müssen
ebenfalls zwischen 1 und 6 passieren. Über die relative Reihenfolge der
Zuweisungen und der Sequence-Points 2 bis 5 wird nichts ausgesagt.

Natürlich werden hier die Ergebnisse von f() für die Zuweisung benötigt,
also müssen sie vorher aufgerufen werden, aber g() liefert einen
konstanten Wert, also könnte der Compiler IMHO das so umformulieren:

t1 = f(1);
*t1 = 42;
t2 = f(2);
*t2 = 42;
t3 = f(3);
*t3 = 42;
g();

Rein theoretisch. In der Praxis halte ich das für ziemlich
unwahrscheinlich (sehr wahrscheinlich hingegen ist, dass der Aufruf von
g() inline expandiert wird, was zu den ersten 6 Zeilen führen könnte).

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Jens Schmidt
2014-03-02 22:40:31 UTC
Permalink
Raw Message
Post by Peter J. Holzer
Post by Jens Schmidt
Genaugenommen kann das auch parallel erfolgen. Wir haben also
undefiniertes Verhalten im if()-Teil von f(), solange kein Lock eine
Serialisierung erzwingt.
Nein. Diese Frage wurde seinerzeit in comp.std.c ausführlich diskutiert
| Evaluations A and B are indeterminately sequenced when A is sequenced
| either before or after B, but it is unspecified which.13)
[...]
| 13) The executions of unsequenced evaluations can interleave.
| Indeterminately sequenced evaluations cannot interleave, but can be
| executed in any order.
[...]
| There is a sequence point after the evaluations of the function
| designator and the actual arguments but before the actual call. Every
| evaluation in the calling function (including other function calls) that
| is not otherwise specifically sequenced before or after the execution of
| the body of the called function is indeterminately sequenced with
| respect to the execution of the called function.94)
[...]
| 94) In other words, function executions do not ‘‘interleave’’ with each
| other.
Danke für die Aufklärung. Ich habe es jetzt selbst mal nachgelesen.
Post by Peter J. Holzer
Die drei Aufrufe von f sowie der Aufruf von g können also in beliebiger
Reihenfolge aber nicht gleichzeitig/überlappend erfolgen.
Post by Jens Schmidt
Das einzige, was sicher ist, ist dass jeweils beide Funktionsaufrufe
links und rechts einer Zuweisung erfolgen, bevor die Zuweisung selber
ausgeführt wird.
...
Natürlich werden hier die Ergebnisse von f() für die Zuweisung benötigt,
also müssen sie vorher aufgerufen werden, aber g() liefert einen
t1 = f(1);
*t1 = 42;
t2 = f(2);
*t2 = 42;
t3 = f(3);
*t3 = 42;
g();
Rein theoretisch. In der Praxis halte ich das für ziemlich
unwahrscheinlich (sehr wahrscheinlich hingegen ist, dass der Aufruf von
g() inline expandiert wird, was zu den ersten 6 Zeilen führen könnte).
Na ja, auch f ist recht einfach und könnte genauso inline expandiert
werden. Ich ersetze also mal
dass jeweils beide Funktionsaufrufe links und rechts einer Zuweisung
erfolgen
durch
dass jeweils die Seiteneffekte der Funktionsaufrufe links und rechts
einer Zuweisung erfolgen

Aber Dein theoretischer Fall oben sieht mir nach Verletzung der
'no interleave' Eigenschaft aus. Auch wenn nur das Wissen um das
konstante Resultat weit vor die eigentliche Ausführung bewegt wird.
--
Viele Grüße,
Jens Schmidt
Juergen Ilse
2014-03-02 11:40:33 UTC
Permalink
Raw Message
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
Post by Johan Pfeiffer
Post by David Seppi
leerz = tab = zeile = 0;
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Der Sprachstandard sagt es (aus gutem Grund) nicht so.
Die Rangfolge der Operatoren ist gleich.
Die Abarbeitungsreihenfolge ist von _rechts_ nach _links_ festgelegt.
Die Reihenfolge, in der die Werte in den einzelnen Variablen landen,
ist durch den Sprachstandard *nicht* festgelegt. Die Wertaenderung
der Variablen sind Nebeneffekte der Statements, und ueber die wird
vom Standard lediglich ausgesagt, dass sie vor dem naechsten Sequenz-
punkt abgeschlossen sein muessen. In welcher Reihenfolge sie zwischen
2 Sequenzpunkten abgeschlossen werden, ist ausdruecklich *nicht* vom
Standard festgelegt. Die Zuweisungen bilden auch keinen Sequenzpunkt.
Es wird eben in der Zeile nicht der Wert von zeile (der zu diesem Zeit-
punkt noch nicht genau bestimmt ist) an tab zu gewiesen sondern der
Wert der Zuweisung an Zeile (dessen Wert steht fest: er ist gleich
dem zugewiesenen Wert, also 0).
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0. Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht. Das spielt aber auch
keine Rolle, da nicht der Wert der Variablen zeile sondern der Wert
der Zuweisung an tab zugewiesen wird. Das ist ein Unterschied, auch
wenn der Unterschied i.d.R. bei standatdkonformen Programmen nicht
unbedingt relevant ist.
Damit wird das Ergebnis des Ausdrucks von zeile=0 Bestandteil des
Ausdrucks tab = ... und dieses Ergebnis wiederum Bestandteil des
Ausdrucks leert = ...
das würde auch dem Ausdruck (leerz=(tab=(zeile=0))) entsprechen.
Ja. Die Reihenfolge, in der letztendlich die Werte in den Variablen
landen, ist hingegen vom Standard *nicht* vorgeschrieben, da alle
Zuweisungen zwischen den selben bei Sequenzpubkten liegen.
Post by Johan Pfeiffer
Ich denke ich hab das jetzt verstanden.
Das freut mich.

Tschuess,
Juergen Ilse (***@usenet-verwaltung.de)
--
Ein Domainname ist nur ein Name, nicht mehr und nicht weniger.
Wer mehr hineininterpretiert, hat das Domain-Name-System nicht
verstanden.
Peter J. Holzer
2014-03-01 21:29:25 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
Ja.
Post by Johan Pfeiffer
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
In welcher? Der des Compilers? Kann sein, dass dieser Compiler das so
macht, und dass es der Compilerhersteller für sinnvoll befunden hat,
dieses Verhalten zu dokumentieren. Im allgemeinen gibt es zwischen zwei
Sequence-Points kein "und dann". Es bleibt dem Compiler überlassen, was
er in welcher Reihenfolge macht. Oder ob er vielleicht sogar Dinge
gleichzeitig macht.
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0.
Wobei dieser allerdings wiederum (per definitionem) gleich ist dem Wert,
der zeile zugewiesen wird, also dem, den zeile beim nächsten
Sequence-Point haben wird.
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht.
Über den Wert von Variablen zwischen zwei Sequence-Points kann man keine
Aussage treffen. Die sind wie Schrödingers Katze.

(Jedenfalls für "C". Für eine konkrete Implemeniterung kann man das
natürlich meistens schon).
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Das spielt aber auch keine Rolle, da nicht der Wert der Variablen
zeile sondern der Wert der Zuweisung an tab zugewiesen wird. Das ist
ein Unterschied, auch wenn der Unterschied i.d.R. bei
standatdkonformen Programmen nicht unbedingt relevant ist.
Fällt Dir ein Beispiel eines standardkonformen Programms ein, wo der
Unterschied feststellbar wäre? Mir nicht.
Post by Johan Pfeiffer
myCprog: leerz = tab = zeilen = 0;
00401212 33C0 xor eax,eax
00401214 8945F0 mov [ebp-$10],eax
00401217 8945F4 mov [ebp-$0c],eax
0040121A 8945F8 mov [ebp-$08],eax
0040121D EB1B jmp $0040123a
Der Code spiegelt genau das wieder, was Juergen geschrieben hat.
Post by Johan Pfeiffer
XOR setzt das Register EAX auf den Wert 0
dann wird mit Zieladresse, EAX und eben nicht Zwischenergebnis alles auf
den Wert 0 gesetzt.
Den letzten Halbsatz kann ich nicht parsen.

Aber das Zwischenergebnis wird jeweils in eax gespeichert und es wird
der Reihe nach den drei Variablen zugewiesen (wobei aus dem Codestück
nicht hervorgeht, welche der drei Stellen Stack leerz, tab und zeile
entspricht). Würde der Compiler wirklich den Wert der vorher
zugewiesenen Variable zuweisen, dann müsste er Code wie den folgenden
erzeugen:

xor eax,eax
mov [ebp-$10],eax
mov eax, [ebp-$10]
mov [ebp-$0c],eax
mov eax, [ebp-$0c]
mov [ebp-$08],eax

Natürlich erkennt der Optimizer, dass das unnötig ist.

Ähnliches Beispiel:


#include <stdint.h>

uint32_t foo(void) {
uint8_t a;
uint16_t b;
uint32_t c;

c = a = b = 0x12345678;
return c;
}

gcc 4.7.2 macht daraus ohne Optimierung folgenden Code:

foo:
.LFB0:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movw $22136, -2(%ebp)
movb $120, -3(%ebp)
movzbl -3(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
leave
ret

also das Äquivalent von

b = 0x5678;
a = 0x78;
c = a;
return c;

Interessante Mischung. Für zwei der Zuweisungen berechnet der Compiler
den Wort zur Compilezeit und weist ihn direkt zu, für die dritte nimmt
er den Wert zuvor zugewiesenen Variable.

(Mit Optimierung wirft der Compiler natürlich die ganzen unnötigen
Variablen und zuweisungen weg und macht daraus das Äquivalent von
„return 0x78“)

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Johan Pfeiffer
2014-03-02 10:56:25 UTC
Permalink
Raw Message
Post by Peter J. Holzer
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Hallo,
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by Juergen Ilse
int leerz, tab, zeile;
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
Ja.
Post by Johan Pfeiffer
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
So stehts in der Doku.
Das ist so von mir falsch geschrieben.
Post by Peter J. Holzer
In welcher? Der des Compilers? Kann sein, dass dieser Compiler das so
macht, und dass es der Compilerhersteller für sinnvoll befunden hat,
dieses Verhalten zu dokumentieren. Im allgemeinen gibt es zwischen zwei
Sequence-Points kein "und dann". Es bleibt dem Compiler überlassen, was
er in welcher Reihenfolge macht. Oder ob er vielleicht sogar Dinge
gleichzeitig macht.
Das Ergebnis der Ausdrücke wird _nacheinander_ entsprechend der
Rangfolge verarbeitet.
Würde das gleichzeitig gehen - nein, es ist eine Abarbeitungsreihenfolge
festgelegt.
Post by Peter J. Holzer
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Nein. tab erhaelt den (Rueckgabe-) Wert der Zuweisung zeile=0, un der
ist wiederum 0.
Wobei dieser allerdings wiederum (per definitionem) gleich ist dem Wert,
der zeile zugewiesen wird, also dem, den zeile beim nächsten
Sequence-Point haben wird.
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Entsprechend geht es weiter: leerz erhaelt den Wert
der Zuweisung des Wertes an tab, und der ist ebenfalls 0. Zwischen
den Zuweisungen gibt es keinen Sequenzpunkt, das heisst, es ist nicht
zwingend gesagt, dass zum Zeitpunkt der Zuweisung des Wertes an tab
in der Variablen zeile bereits der Wert 0 steht.
Über den Wert von Variablen zwischen zwei Sequence-Points kann man keine
Aussage treffen. Die sind wie Schrödingers Katze.
(Jedenfalls für "C". Für eine konkrete Implemeniterung kann man das
natürlich meistens schon).
Das hab ich jetzt verstanden - danke.
Post by Peter J. Holzer
Post by Johan Pfeiffer
Post by Johan Pfeiffer
Das spielt aber auch keine Rolle, da nicht der Wert der Variablen
zeile sondern der Wert der Zuweisung an tab zugewiesen wird. Das ist
ein Unterschied, auch wenn der Unterschied i.d.R. bei
standatdkonformen Programmen nicht unbedingt relevant ist.
Fällt Dir ein Beispiel eines standardkonformen Programms ein, wo der
Unterschied feststellbar wäre? Mir nicht.
Post by Johan Pfeiffer
myCprog: leerz = tab = zeilen = 0;
00401212 33C0 xor eax,eax
00401214 8945F0 mov [ebp-$10],eax
00401217 8945F4 mov [ebp-$0c],eax
0040121A 8945F8 mov [ebp-$08],eax
0040121D EB1B jmp $0040123a
Der Code spiegelt genau das wieder, was Juergen geschrieben hat.
Post by Johan Pfeiffer
XOR setzt das Register EAX auf den Wert 0
dann wird mit Zieladresse, EAX und eben nicht Zwischenergebnis alles auf
den Wert 0 gesetzt.
Den letzten Halbsatz kann ich nicht parsen.
Es wird immer der Wert von EAX zugewiesen MOV Ziel, Quelle
und eben nicht das Ergebnis des Ausdrucks x=y.
Das ist aber Compilersache und hat nichts mit C zu tun.
Post by Peter J. Holzer
Aber das Zwischenergebnis wird jeweils in eax gespeichert und es wird
der Reihe nach den drei Variablen zugewiesen (wobei aus dem Codestück
nicht hervorgeht, welche der drei Stellen Stack leerz, tab und zeile
entspricht). Würde der Compiler wirklich den Wert der vorher
zugewiesenen Variable zuweisen, dann müsste er Code wie den folgenden
xor eax,eax
mov [ebp-$10],eax
mov eax, [ebp-$10]
mov [ebp-$0c],eax
mov eax, [ebp-$0c]
mov [ebp-$08],eax
Natürlich erkennt der Optimizer, dass das unnötig ist.
Ja das habe ich völlig unverstandlich bzw. nicht zu parsend geschrieben.
Die Beiträge haben mir viel zum Verstehen weitergeholfen.
Post by Peter J. Holzer
#include <stdint.h>
uint32_t foo(void) {
uint8_t a;
uint16_t b;
uint32_t c;
c = a = b = 0x12345678;
return c;
}
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movw $22136, -2(%ebp)
movb $120, -3(%ebp)
movzbl -3(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
leave
ret
also das Äquivalent von
b = 0x5678;
a = 0x78;
c = a;
return c;
Interessante Mischung. Für zwei der Zuweisungen berechnet der Compiler
den Wort zur Compilezeit und weist ihn direkt zu, für die dritte nimmt
er den Wert zuvor zugewiesenen Variable.
(Mit Optimierung wirft der Compiler natürlich die ganzen unnötigen
Variablen und zuweisungen weg und macht daraus das Äquivalent von
„return 0x78“)
Assembler ist glasklar - mein C Compiler erzeugt _ohne_ Optimierung:
und das kann sich sehen lassen!

myC1-8.c.20: leerz = tab = zeilen = 0x12345678;
00401222 66BA7856 mov dx,$5678
00401226 668955F4 mov [ebp-$0c],dx
0040122A 8855F7 mov [ebp-$09],dl
0040122D 0FBEC2 movsx eax,dl
00401230 8945F8 mov [ebp-$08],eax
00401233 EB1C jmp $00401251
--
Grüße Jo
Peter J. Holzer
2014-03-02 15:20:38 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Peter J. Holzer
In welcher? Der des Compilers? Kann sein, dass dieser Compiler das so
macht, und dass es der Compilerhersteller für sinnvoll befunden hat,
dieses Verhalten zu dokumentieren. Im allgemeinen gibt es zwischen zwei
Sequence-Points kein "und dann". Es bleibt dem Compiler überlassen, was
er in welcher Reihenfolge macht. Oder ob er vielleicht sogar Dinge
gleichzeitig macht.
Das Ergebnis der Ausdrücke wird _nacheinander_ entsprechend der
Rangfolge verarbeitet.
Würde das gleichzeitig gehen - nein, es ist eine Abarbeitungsreihenfolge
festgelegt.
Nein, eben nicht. Zwischen zwei Sequence-Points ist keine Reihenfolge
festgelegt. Ich werde das nicht noch einmal wiederholen, das wurde Dir
jetzt ungefähr ein Dutzend mal von verschiedenen Leuten gesagt. Wenn Du
uns nicht glaubst, dann lies bitte im Standard nach.
Post by Johan Pfeiffer
Post by Peter J. Holzer
Post by Johan Pfeiffer
myCprog: leerz = tab = zeilen = 0;
00401212 33C0 xor eax,eax
00401214 8945F0 mov [ebp-$10],eax
00401217 8945F4 mov [ebp-$0c],eax
0040121A 8945F8 mov [ebp-$08],eax
0040121D EB1B jmp $0040123a
Der Code spiegelt genau das wieder, was Juergen geschrieben hat.
Post by Johan Pfeiffer
XOR setzt das Register EAX auf den Wert 0
dann wird mit Zieladresse, EAX und eben nicht Zwischenergebnis alles auf
den Wert 0 gesetzt.
Was soll "Zieladresse, EAX und eben nicht Zwischenergebnis" heißen?
EAX ist das Zwischenergebnis. Der Code da oben ist äquivalent zu

t = 0;
zeilen = t;
tab = t;
leerz = t;

Dass es nur ein t gibt, ist hier schon eine Optimierung.
leerz = tab = zeilen = 0;
wäre eigentlich äquivalent zu:

t1 = 0;
zeilen = t1;
t2 = t1;
tab = t2;
t3 = t2;
leerz = t3;

aber es ist hier offensichtlich, dass t1 bis t3 alle den gleichen Wert 0
haben, im Gegensatz zu meinem Beispiel, wo die Zwischenergebnisse
unterschiedliche Werte haben und entsprechend berechnet werden müssen.

An Hand des generierten Codes zu argumentieren ist aber schwierig bis
sinnlos, da (wie schon gesagt) der Code nur zeigt, was ein bestimmter
Compiler macht (und nicht, was der Standard erlaubt bzw. vorschreibt)
und zweitens oft unterschiedliche Interpretationen zum gleichen Code
führen können.
Post by Johan Pfeiffer
Post by Peter J. Holzer
c = a = b = 0x12345678;
[...]
Post by Johan Pfeiffer
Post by Peter J. Holzer
movw $22136, -2(%ebp)
movb $120, -3(%ebp)
movzbl -3(%ebp), %eax
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
[...]
Post by Johan Pfeiffer
und das kann sich sehen lassen!
myC1-8.c.20: leerz = tab = zeilen = 0x12345678;
00401222 66BA7856 mov dx,$5678
00401226 668955F4 mov [ebp-$0c],dx
0040122A 8855F7 mov [ebp-$09],dl
0040122D 0FBEC2 movsx eax,dl
00401230 8945F8 mov [ebp-$08],eax
00401233 EB1C jmp $00401251
Kann ich jetzt nicht sagen, ob das besser oder schlechter ist (und die
Qualität von unoptimiertem Code ist sowieso uninteressant), aber hat das
etwas mit dem Diskussionsgegenstand zu tun?

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Claus Reibenstein
2014-03-02 11:35:22 UTC
Permalink
Raw Message
Das ist absolut irrelevant. Der Compiler kann das übersetzen, wie er
will, so lange das Ergebnis dem entspricht, was der Standard fordert.
Post by Johan Pfeiffer
Das hat ja nichts mehr mit C zu tun. Es ist das produktive Ergebnis des
Compilers.
Das Produkt _eines_ Compilers mit _bestimmten_ Einstellungen auf _einer_
Plattform.

Gruß
Claus
Claus Reibenstein
2014-03-02 11:30:29 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by David Seppi
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
So ist es in der Sprachdefinition festgelegt ...
Post by Johan Pfeiffer
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
... und so wirkt es sich letztendlich aus.
Post by Johan Pfeiffer
So stehts in der Doku.
In welcher Doku? An welcher Stelle?

Gruß
Claus
Johan Pfeiffer
2014-03-02 14:21:50 UTC
Permalink
Raw Message
Post by Claus Reibenstein
Post by Johan Pfeiffer
Post by Juergen Ilse
Post by David Seppi
leerz = tab = zeile = 0;
Ja, was deswegen funktioniert, weil eine Zuweisung wie zeile=0 selbst
wieder einen Wert hat (naemlich den Wert der zugewiesen wurde, in diesem
Fall also 0).
So ist es in der Sprachdefinition festgelegt ...
Post by Johan Pfeiffer
Es sind meherere Zuweisungen, mit der Reihenfolge _von rechts nach links._
So erhält zeile den Wert 0, danach erhält tab den Wert von zeile und
erst dann erhält leerz den Wert von tab.
... und so wirkt es sich letztendlich aus.
Post by Johan Pfeiffer
So stehts in der Doku.
In welcher Doku? An welcher Stelle?
Ich zitiere aus der deutschen Ausgabe von K&R 2. Ausgabe Seite 20
wie folgt:
Die Anweisung nl = nw = nc = 0;
weist allen drei Variablen den Wert 0 zu.
Dies ist kein Sonderfall, sondern eine Konsequenz der Tatsache,
daß jede Zuweisung ein Ausdruck ist,
und somit einen Wert besitzt, und daß Zuweisungen von
rechts nach links ausgeführt werden.
Wir könnten dies auch so formulieren: nl = (nw = (nc = 0));

Damit habe ich gedanklich eine Reihenfolge verbunden die so nicht
festgelegt ist.

In der Originaldoku Ansi C und C99 von der GNU Webseite steht aber:
The order of evaluation of the operands is unspecified.
--
Grüße Jo
Stefan Ram
2014-02-26 14:59:30 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
Hier werden »leerz« und »tab« nicht initialisiert.
Johan Pfeiffer
2014-02-26 15:10:49 UTC
Permalink
Raw Message
Post by Stefan Ram
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
Hier werden »leerz« und »tab« nicht initialisiert.
Ja vielen Dank für den Hinweis.

Ich kann die Variablen auf einer Zeile gemeinsam deklarieren,
aber beim Initialisieren kann dies nur _einzeln_ erfolgen.

Bei der Wertzuweisung spricht man von Initialisierung?
Die Deklaration ist eine formale Bekanntmachung?

Viele Grüße Jo
Stefan Ram
2014-02-26 15:21:46 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Bei der Wertzuweisung spricht man von Initialisierung?
Unter »Initialisierung« versteht man im allgemeinen die
erste Festlegung des Wertes einer Variablen.

(»An initializer specifies the initial value stored in an object.«)
Post by Johan Pfeiffer
Die Deklaration ist eine formale Bekanntmachung?
Ich habe einige der dazugehörigen Fachbegriffe auf der
folgenden Seite notiert:

http://www.purl.org/stefan_ram/pub/c_konstanten_de

.
Stefan Reuther
2014-02-26 17:48:42 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Stefan Ram
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
Hier werden »leerz« und »tab« nicht initialisiert.
Ja vielen Dank für den Hinweis.
Ich kann die Variablen auf einer Zeile gemeinsam deklarieren,
aber beim Initialisieren kann dies nur _einzeln_ erfolgen.
Korrekt.

Jetzt ist übrigens eine gute Gelegenheit, dich mal mit den Compiler-
optionen zum Thema Warnungen auseinanderzusetzen. gcc warnt jedenfalls
mit '-O -W', dass die Variablen uninitialisiert sind.

Und wenn du gleich dabei bist: '#include <tchar.h>' und 'int _tmain(int
argc, _TCHAR* argv[])' sind Microsoftismen. Ersteres kann ersatzlos
entfallen, zweiteres heißt in C 'int main(int argc, char* argv[])'.
Post by Johan Pfeiffer
Bei der Wertzuweisung spricht man von Initialisierung?
Die Deklaration ist eine formale Bekanntmachung?
Die Deklaration ist eine formale Bekanntmachung, kann man so sagen. Der
1999er Sprachstandard schreibt: "A declaration specifies the
interpretation and attributes of a set of identifiers." Passt also in
etwa. Du machst damit die Eigenschaft der Bezeichner bekannt. Da die
angegebene Zeile auch Speicherplatz für die Variablen reserviert, ist es
gleichzeitig eine Definition.

Gleichzeitig mit der Definition kannst du der Variable einen Wert geben.
Das ist dann eine Initialisierung.

Später kannst du natürlich neue Werte zuweisen. Das ist zwar eine
Wertzuweisung, aber keine Initialisierung.


Stefan
Johan Pfeiffer
2014-02-27 19:48:27 UTC
Permalink
Raw Message
Post by Stefan Reuther
Post by Johan Pfeiffer
Post by Stefan Ram
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
Hier werden »leerz« und »tab« nicht initialisiert.
Ja vielen Dank für den Hinweis.
Ich kann die Variablen auf einer Zeile gemeinsam deklarieren,
aber beim Initialisieren kann dies nur _einzeln_ erfolgen.
Korrekt.
Jetzt ist übrigens eine gute Gelegenheit, dich mal mit den Compiler-
optionen zum Thema Warnungen auseinanderzusetzen. gcc warnt jedenfalls
mit '-O -W', dass die Variablen uninitialisiert sind.
Danke das ist eine guter Hinweis.
Post by Stefan Reuther
Und wenn du gleich dabei bist: '#include <tchar.h>' und 'int _tmain(int
argc, _TCHAR* argv[])' sind Microsoftismen. Ersteres kann ersatzlos
entfallen, zweiteres heißt in C 'int main(int argc, char* argv[])'.
Ich arbeite an einem Windows 7 System, da ich auch für einen MC
entwickle und leider keine durchgängige Werkzeuge auf Unix habe.
Schade!
Post by Stefan Reuther
Post by Johan Pfeiffer
Bei der Wertzuweisung spricht man von Initialisierung?
Die Deklaration ist eine formale Bekanntmachung?
Die Deklaration ist eine formale Bekanntmachung, kann man so sagen. Der
1999er Sprachstandard schreibt: "A declaration specifies the
interpretation and attributes of a set of identifiers." Passt also in
etwa. Du machst damit die Eigenschaft der Bezeichner bekannt. Da die
angegebene Zeile auch Speicherplatz für die Variablen reserviert, ist es
gleichzeitig eine Definition.
Gleichzeitig mit der Definition kannst du der Variable einen Wert geben.
Das ist dann eine Initialisierung.
Später kannst du natürlich neue Werte zuweisen. Das ist zwar eine
Wertzuweisung, aber keine Initialisierung.
Vielen Dank für die Beiträge.
--
Grüße Jo
Florian Leitner
2014-02-28 11:14:47 UTC
Permalink
Raw Message
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Post by Johan Pfeiffer
Ich arbeite an einem Windows 7 System, da ich auch für einen MC
entwickle und leider keine durchgängige Werkzeuge auf Unix habe.
Schade!
Hier ist ein möglicher "Ausweg" für C Entwickler auf Windows; Habe
Pelles C selbst allerdings http://www.smorgasbordet.com/pellesc/ nie
probiert, und ungefähr so viel Ahnung von Windows wie
Windows-Entwickler von POSIX-Systemen... Aber es ist 100% Freeware,
d.h. auch kommerziell einsetzbar (außer Eure Rechtsabteilung besteht
auf einer Apache 2 Lizenz wegen dem extra Patent-Paragraphen...)

http://www.smorgasbordet.com/pellesc/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.22 (Darwin)
Comment: GPGTools - https://gpgtools.org
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iQIcBAEBCgAGBQJTEG+nAAoJEETsmGA8yb6xn5cP/2jwJpDdydH62vIhX+TO36ve
pwpp/rEUu+g8TlcF+gUgf3x6fbucuHrbdsANlVyE88XbaywyJmqeLcRcfxfpMJvi
Whp/ZXUII4QAa/Z4SChk7F2zNWZsyQo0AyyBz3t9QQgjc3Kpzf6ZI6pwt5e+mS3v
anu/iSA62sUu5IITq+HRkK4MEjtHBLxHKkaoatZ0wCn10RRi8ApFnbkCug6aZx+D
TL2ALZauVWbwy9+xnduM/BelDNslXY2zTZCIMyqKQX7Jcn4UlTYTioqI2yNvENf2
W70MMdnvjW6xJrZ7SYjar6sQVZ7pdHX7MLxL43nMwi305wKgpXPgiqBbvV2B9Czu
jFtb1+E1+Hn3RXRhB2xfJs8YeZ5dUGS4AxilawOBqkQnlvm/zvFE8GZVFiF8wefS
oOzR+kEPAKpTFDZhIlzQYbfLwjsQ/rZTeFkdpk253bUKyYM2nMT8WLkyhSLLM+K2
I/+XsM6HBqD8UbDOx2rECZlvM00m1JNWCescstNUl9Q6NI9gsry+4QkAYtTgiMS2
xZdp9YIhcES0BF5S4gIt8NLlENe/A9mRHGcGH20dSJgGHL58frfXSJ424zUlma6G
Dw6iqijk2S+MSJu612pY5Wm76EhGwqPvnzrVy6c7qvmK7Aj0CC+opTIfPrpkYJeo
JOGMTiKlraJHodD50yse
=tcit
-----END PGP SIGNATURE-----

--- news://freenews.netfront.net/ - complaints: ***@netfront.net ---
Johan Pfeiffer
2014-03-01 07:58:12 UTC
Permalink
Raw Message
Post by Florian Leitner
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
Post by Johan Pfeiffer
Ich arbeite an einem Windows 7 System, da ich auch für einen MC
entwickle und leider keine durchgängige Werkzeuge auf Unix habe.
Schade!
MC steht hier für Mikrocontroller und es wird nicht nur ein spezieller
Typ sein.
Post by Florian Leitner
Hier ist ein möglicher "Ausweg" für C Entwickler auf Windows; Habe
Pelles C selbst allerdings http://www.smorgasbordet.com/pellesc/ nie
probiert, und ungefähr so viel Ahnung von Windows wie
Windows-Entwickler von POSIX-Systemen... Aber es ist 100% Freeware,
d.h. auch kommerziell einsetzbar (außer Eure Rechtsabteilung besteht
auf einer Apache 2 Lizenz wegen dem extra Patent-Paragraphen...)
Die Werkzeuge für Software und Hardware sind für Windows bereits
vorhanden. Focus ist protabler C Code, um auf verschiede Plattformen ein
möglichst umfangreiches Grundgerüst zu haben und nur Details spezifisch
entwickeln zu müssen.
--
Grüße Jo
Stefan Reuther
2014-02-28 17:45:02 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Stefan Reuther
Und wenn du gleich dabei bist: '#include <tchar.h>' und 'int _tmain(int
argc, _TCHAR* argv[])' sind Microsoftismen. Ersteres kann ersatzlos
entfallen, zweiteres heißt in C 'int main(int argc, char* argv[])'.
Ich arbeite an einem Windows 7 System, da ich auch für einen MC
entwickle und leider keine durchgängige Werkzeuge auf Unix habe.
Schade!
Falls MC für Microcontroller steht: auch der wird mit <tchar.h> und
_tmain nicht viel anfangen können. Egal, ob der Compiler unter Windows
oder *ix läuft.


Stefan
Johan Pfeiffer
2014-03-01 08:03:35 UTC
Permalink
Raw Message
Post by Stefan Reuther
Post by Johan Pfeiffer
Post by Stefan Reuther
Und wenn du gleich dabei bist: '#include <tchar.h>' und 'int _tmain(int
argc, _TCHAR* argv[])' sind Microsoftismen. Ersteres kann ersatzlos
entfallen, zweiteres heißt in C 'int main(int argc, char* argv[])'.
Ich arbeite an einem Windows 7 System, da ich auch für einen MC
entwickle und leider keine durchgängige Werkzeuge auf Unix habe.
Schade!
Falls MC für Microcontroller steht: auch der wird mit <tchar.h> und
_tmain nicht viel anfangen können. Egal, ob der Compiler unter Windows
oder *ix läuft.
MC steht für Mikrocontroller und dort wird es ein
void main(void) mit der Absicht, dass main nie verlassen wird
und wenn doch dann gibts ein absolutes JUMP 0000h.
--
Güße Jo
Achim Peters
2014-02-26 22:29:30 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Post by Achim Peters
Post by Johan Pfeiffer
Ich erhalte aber 2 TAB Zeichen, 259 Leerzeichen und 1 Zeile.
Ich vermute, das liegt nicht an getchar(), sondern an Deinem Programm.
Da Du uns den Code aber vorenthältst, bleibt es bei einer Vermutung.
ich wollte nicht so aufdringlich sein.
Das ist ok. Aufdringlich wäre es, wenn Du 200+ Zeilen posten würdest,
die nichts mit dem Problem zu tun haben.
Post by Johan Pfeiffer
int leerz, tab, zeilen = 0;
Na ja, da hätte ich jetzt beinahe 10€ drauf gewettet, aber ich wollte
nicht vorgreifen und Dich nicht mit Mutmaßungen verwirren.

Die Antwort(en) dazu hast Du bereits erhalten.

Bye
Achim
Stefan Ram
2014-02-26 12:10:26 UTC
Permalink
Raw Message
Post by Johan Pfeiffer
Kann mir da jemand helfen?
http://www.purl.org/stefan_ram/pub/kompilett
Johan Pfeiffer
2014-02-26 15:04:31 UTC
Permalink
Raw Message
Post by Stefan Ram
Post by Johan Pfeiffer
Kann mir da jemand helfen?
http://www.purl.org/stefan_ram/pub/kompilett
Vielen Dank für die Antwort. Der Link ist ja echt gut.

Ich hatte im Quelltext die betreffenden Variablen nicht definiert!

Viele Grüße Jo
Johan Pfeiffer
2014-02-27 19:50:15 UTC
Permalink
Raw Message
Vielen Dank für eure wertvollen Beiträge.
--
Grüße Jo
Loading...