Hallo,
schon mehrfach habe ich mich nach dem Sinn hinter der o.g. Fehlermeldung
gefragt. Es kommt oft vor, dass ich direkt hinter einer Sprungmarke eine
neue (Hilfs-)Variable für eine Berechnung einführen muss.
Anstatt
1
// ...
2
3
hierher:
4
int32_tc=a-b;
5
6
// ...
muss man beispielsweise das schreiben:
1
// ...
2
3
int32_tc;
4
5
hierher:
6
c=a-b;
7
8
// ...
Ich empfinde das als unästhetisch und irreführend, weil ich
Programmteile lieber gruppieren und nicht ineinander verschachteln
möchte.
Warum "erlaubt" man die intuitive Syntax nicht einfach?
ER HAT GOTO GRSAGT, STEINIGT IHN ;)
Mal im ernst. Sieht nach c aus, scheint aber so richtig altes zu sein.
Nikolai schrieb:> Warum "erlaubt" man die intuitive Syntax nicht einfach?
GOTO ist nicht gerade intuitiv. Deswegen vielleicht
Nikolai schrieb:> schon mehrfach habe ich mich nach dem Sinn hinter der o.g. Fehlermeldung> gefragt.
Der Standard kennt nur labeled statements, keine labeled declarations.
Als Behelf kannst du das Label in der Form
1
hierher:;
schreiben - ist zwar nicht hübsch, aber funktioniert.
Christophe J. schrieb:> ist zwar nicht hübsch, aber funktioniert.
Hübscher ist es, danach einen Block anzufangen. Der ist syntaktisch
ein Statement, aber er kann (selbst im alten K&R-C) wieder eine
Deklaration enthalten.
1
hierher:
2
{
3
int32_tc=a-b;
4
// …
5
}
Ganz unabhängig von goto hat man das gleiche Problem innerhalb
von switch / case, denn auch dort hat man syntaktisch Labels.
Jörg W. schrieb:> Hübscher ist es, danach einen Block anzufangen.
Das hat auch den Vorteil, daß der Leser besser sieht, wie lange eine
Variable gültig ist.
Ich bevorzuge allerdings, alle Variablen am Anfang der Funktion zu
definieren. Eine Ausnahme ist der Schleifenzähler in for-Schleifen.
Dem Compiler ist es schnurz, eine späte Definition spart nichts ein. Er
legt den Stackframe für alle Variablen immer im Prolog an.
> Ich bevorzuge allerdings, alle Variablen am Anfang der Funktion zu> definieren.
Geht nicht immer, da man den (initialen) Wert an dieser Stelle oft noch
nicht kennt. Würde bedeuten, entweder eine Definition ohne
Initialisierung (Pfui!) oder eine Initialisierung auf einen Dummywert
(auch nicht viel schöner, je nach Ansicht sogar hässlicher, da man die
Funktionalität moderner Compiler, vor nicht initialisierten Variablen zu
warnen, bricht).
Sebastian L. schrieb:> Würde bedeuten, entweder eine Definition ohne> Initialisierung (Pfui!)
Was soll daran (Pfui!) sein?
Mache ich regelmäßig.
Ein Zuweisung in der Definition mache ich nur dann, wenn ich eine
Konstante zuweise.
Operationen vermische ich grundsätzlich nicht mit der Definition:
Das switch ist ein Beispiel für einen Fall, in dem es nun einmal nicht
anders geht. Ein anderes ist die Fallunterscheidung mit if-else.
Ich verwende Definitionen mit Initialisierung dann, wenn der Initialwert
- aus einer Berechung (Formel) kommt
- aus einem Funktionsaufruf kommt
- eine Konstanten ist
- aus einer anderen Variablen übernommen wird
Pfui ist etwas übertrieben, aber eine Definition ohne Initialisierung
hat drei Nachteile:
- Man muss aufpassen, dass in keinem Fall die Initialisierung
übersprungen wird. Wenn man einen Fehler versucht einzugrenzen, muss man
zusätzlich abklären, ob genau hier nicht der Fehler liegt. Gut, moderne
Compiler mit ihren Warnungen machen das etwas leichter.
- Man muss von der ersten Zuweisung ggf. nach oben scrollen, um zu
sehen, wie die Variable datentypmäßig definiert ist.
- Wenn man auch C++ programmiert, muss man sich diesen Stil eh
abgewöhnen, weil man da ja auch mit auf dem Stack allokierten Objekten
zu tun hat und sich nicht jedes Objekt, das mit dem Default-Konstruktor
der jeweiligen Klasse initialisiert wurde, nochmal "umbauen" lässt.
Das hat für mich nun einmal an Gründen ausgereicht, um mir das komplett
abzugewöhnen
Nikolai schrieb:> int32_t c;> hierher:> c = a - b;> // ...> Ich empfinde das als unästhetisch und irreführend, weil ich> Programmteile lieber gruppieren und nicht ineinander verschachteln> möchte.> Warum "erlaubt" man die intuitive Syntax nicht einfach?
Ich sehe da nichts Intuitives. Bei mir kommen Variablen in Funktionen
immer alle zusammen an den Anfang. Das ist übersichtlich und intuitiv.
Eben so, wie du das in dem von mir zitierten Beispiel getan hast ist es
gut und richtig.
Jaja, ich weiß, daß C-Programmierer am liebsten immer allles
zusammenziehen möchten: Typdefinition, Variablendeklaration und
Wertzuweisung in einem Klumpen - am liebsten noch mit einem dreifach
geschachtelten Fragezeichenoperator mittendrin. ich halte von sowas
überhaupt nichts.
W.S.
W.S. schrieb:> Das ist übersichtlich und intuitiv.
Du hättest dir besser vor deinem Posting den vorletzten Absatz
des Beitrags über deinem durchlesen sollen.
W.S. schrieb:> Ich sehe da nichts Intuitives. Bei mir kommen Variablen in Funktionen> immer alle zusammen an den Anfang. Das ist übersichtlich und intuitiv.
Dann kann es auch nicht passieren, das eine Variable mit gleichem Namen
in einem Block neu definiert wird, deren Wert dann am Ende des Blocks
magisch verschwindet.
MfG Klaus
> Operationen vermische ich grundsätzlich nicht mit der Definition:> uint8_t x;> blub();> switch( y ){> case 1: x = baa(); break;> default: x = foo(); break;> }> blab();> return x;
Wenn hier aus welchen Gründen auch immer der default-Zweig unter den
Tisch fällt (und das kann er; kein Compiler schreibt vor, daß es ihn
geben muss), hast Du eine nicht initialisierte Variable.
Mag sein, daß Deine Vorgehensweise in jedem switch() prinzipiell einen
default-Zweig unterbringt, aber nicht jeder schreibt derartigen Code.
Rufus Τ. F. schrieb:> hast Du eine nicht initialisierte Variable
Die mosert der Compiler aber an, und dann merkst du, dass du
vielleicht doch noch was komplett wichtiges vergessen hast.
Gewöhnst du dir an, immer
1
intx=0;
bereits in die Definition zu schreiben, dann nimmst du dem Compiler
die Möglichkeit, dir diesen netten Hinweis zu geben.
Aber ja, es ist natürlich wie immer, man muss die Warnungen auch
einschalten und angucken.
Jörg W. schrieb:> Die mosert der Compiler aber an,
Ich habe leider schon Fälle erlebt, bei denen das nicht klappt - sehr,
sehr vergurkter fremdgeschriebener Code, und ein Compiler von MS,
vielleicht nicht die günstigste Kombination.
Jörg W. schrieb:> dann nimmst du dem Compiler die Möglichkeit, dir diesen netten Hinweis> zu geben.
Das ist klar; einfach alles zu initialisieren ist definitiv das Zudecken
von Schlampigkeiten und keine Lösung.
Sinnvoll ist es, den eigenen Code nicht nur dem C-Compiler, sondern auch
gründlicheren Werkzeugen zur Kontrolle vorzuwerfen, da bieten sich
pc-lint bzw. flexelint an (wenn man das nötige Kleingeld hat) oder
cppcheck, die finden mehr problematische Dinge als übliche Compiler.
Jörg W. schrieb:> Gewöhnst du dir an, immer> int x = 0;> bereits in die Definition zu schreiben, dann nimmst du dem Compiler> die Möglichkeit, dir diesen netten Hinweis zu geben.
Richtig. Deshalb bin ich auch dafür, wenn möglich eine Variable erst
dann anzulegen, wenn es auch einen sinnvollen Wert zum reinschreiben
gibt. Einen Dummy-Wert reinzuschreiben, nur damit's halt initialisiert
ist, halte ich für eine schlechte Idee.
Ich finde es abgesehen davon auch übersichtlicher, wenn die Lebenszeit
einer Variable eben nur so lang ist, wie nötig, und das bezieht sich
nicht nur auf das Ende, sondern auch auf den Anfang.
Rolf M. schrieb:> Richtig. Deshalb bin ich auch dafür, wenn möglich eine Variable erst> dann anzulegen, wenn es auch einen sinnvollen Wert zum reinschreiben> gibt.
Das ist doch schon wieder keine wirkliche Lösung und es ist wieder mal
Spaghetticode auf C.
Wenn man all seine lokalen Variablen en bloc am Anfang einer Funktion
vereinbart und eben nichts dort vorinitialisiert, dann findet jeder
aktuelle und mir bekannte Compiler sehr zuverlässig bei dem allerersten
Versuch der Verwendung so einer Variablen heraus, ob man diese
Variable zuvor mit irgendwas beschrieben hat. Und genau darauf kommt es
eigentlich immer an.
Jede Idee mit lokal erzeugten Variablen zu arbeiten, die womöglich
andere aus dem Scope verdrängen und dem dadurch notwendigen Beachten
eben dieses Scopes in und nach dem betreffenden Block ist doch nur
eine Abart der Obfuscation.
Schadet dreimal mehr als es nützt.
Obendrein scheint die Mehrzahl der C-Programmierer es nicht zu
begreifen, was sie damit anrichten - siehe Überschrift des TO. Der hat
es nicht geschnallt, daß sein allererstes Statement nach der Marke
keinerlei Entsprechung im Maschinencode haben kann, denn ein "int32_t c"
kann kein Stück MC sein und die Zuweisung durch einen Ausdruck kann nur
danach kommen und nicht zuvor.
Also Rolf, deine Ansicht teile ich ganz und gar nicht.
W.S.
Mir ist schon klar, dass eine Deklaration alleine noch keinen Code
ergibt. Ich möchte aber aus Gründen der Übersichtlichkeit die Variable
erst einführen, wenn sie gebraucht wird, damit ich ihren Typ ohne zu
scrollen und ohne Hilfsmittel sofort sehen kann. In Pascal ist es ja
z.B. anders und wenn ich daran zurückdenke, dann war das immer sehr
unübersichtlich. Das war Spaghetticode! Verschiedene Variablen
verschiedener Code-Teile miteinander vermischt. In C gibt es ja zum
Glück keine Regel, es so machen zu müssen. Genauso wie man selbst wählen
kann, wie man Code eingerückt.
Meine Frage ist aber eigentlich immernoch offen: Warum wird das hier als
Fehler anzeigt und nicht als abschaltbare Warnung? Es gibt so viele üble
Dinger und Codevergewaltigungen, die Warnungen werfen und eigentlich
Fehler sein sollten. Diese Diskrepanz erschließt sich mir nicht.
Nikolai schrieb:> In Pascal ist es ja> z.B. anders und wenn ich daran zurückdenke, dann war das immer sehr> unübersichtlich.
Naja, auch in Pascal konnte man willkürliche begin/end-Blöcke
einführen und in diesen neue Variablen. Ist halt nur wortreicher
als {} in C, an deren Anfang neue Variablen ja auch schon immer
gingen.
Variablendefinitionen „zwischendrin“ musste C++ letztlich einführen,
da es bei OO einfach Situationen gibt, in denen man erst mal so weit
vorangekommen ist mit der Abarbeitung, dass man alle nötigen Angaben
für den Aufruf des Konstruktors zusammenhat. Damit hat es Sinn, die
Variable erst an dieser Stelle zu definieren, denn es gibt Klassen,
für deren Instanziierung ein Default-Konstruktor (ohne Initialwerte)
gar nicht sinnvoll oder machbar ist (bspw. wenn es "const"-Mitglieder
gibt, die nur in der Initialisierung einen Wert bekommen können).
Das dürfte eigentlich bei OO-Pascal nicht anders sein, aber das
kenne ich zu wenig.
(C hat das Feature dann in C99 von C++ übernommen.)
> Meine Frage ist aber eigentlich immernoch offen: Warum wird das hier als> Fehler anzeigt und nicht als abschaltbare Warnung?
Meinst du das, worauf sich der Thread bezieht?
Weil es eben nicht in die vorgegebene Syntax passt. switch (oder
goto) springt zu einem Label, und nur eine Anweisung kann einen
Label bekommen. Eine Definition (oder Deklaration) ist aber keine
Anweisung.
Nikolai schrieb:> Du kannst also nicht nachvollziehen, warum diese Syntax Käse ist? ;)
Käse hin, Käse her: siehe Nachbarthread. Wenn du versuchst, in
einer Sprache zu viel umzubauen, dann wirst du mit einer anderen
Sprache enden. Die wiederum musst du erstmal so unter die Leute
bringen, dass sie auch wirklich benutzt wird.
Man könnte ansonsten ja gut und gern das ganze switch/case-Geraffel
in seiner jetzigen Form als „Käse“ abtun und gleich mit über Bord
werfen (siehe „Duff's Device“).
Nikolai schrieb:> Du kannst also nicht nachvollziehen, warum diese Syntax Käse ist? ;)
Warum das in der ISO-Norm so festgelegt wurde, verstehe ich ebensowenig
wie du.
Dass einer Deklaration kein ausführbarer Code zugeordnet werden und sie
deswegen nicht angesprungen werden kann, ist für mich keine stichhaltige
Begründung. Denn auch ein Kommentar ergibt keinen ausführbaren Code, und
trotzdem darf er zwischen dem Label und dem eigentlichen Sprungziel
stehen. Man könnte in der Norm ja festlegen, dass sich ein Label immer
auf die jeweils nächste Anweisung bezieht, unabhängig davon, wieviel
nichtausführbarer Code dazwischen steht.
Vermutlich ist die bestehende Regelung ganz einfach historisch bedingt:
Früher mussten sämtliche Deklarationen am jeweiligen Blockanfang stehen,
so dass es keinerlei Veranlassung gab, ein Label vor eine Deklaration zu
setzen. Als dann später Deklarationen auch in der Blockmitte erlaubt
wurden, hat im Normungskomitee keiner laut genug nach einer Änderung der
Regelung bzgl. der Labels geschrien, also blieb alles beim Alten.
Hier ist ein Beispiel, wo es wirklich sinnvoll wäre, ein Label vor eine
Deklaration setzen zu können:
1
intn;
2
3
// ...
4
5
n=foo();
6
gotolabel;
7
8
// ...
9
10
n=bar();
11
12
label:// Fehler
13
inta[n];
14
15
baz(a,n);
16
17
// ...
18
}
Es erscheint die Fehlermeldung aus dem Threadtitel:
1
error: a label can only be part of a statement and a declaration is not a statement
Setzt man das Label aber zwei Zeilen tiefer (vor den Aufruf von baz),
meckert der Compiler ebenfalls, weil damit die Deklaration des
anschließend benutzten VLA übersprungen wird:
1
error: jump into scope of identifier with variably modified type
Das Problem liegt darin, dass
1
inta[n];
zwar eine Deklaration ist, mit dieser aber auch eine Aktion (nämlich die
Reservierung von Speicher auf dem Stack) verbunden ist, die wegen der
Abnhängigkeit vom Ergebnis von bar() nicht schon früher ausgeführt
werden kann. Deswegen sollte sie IMHO wie eine Anweisung angesprungen
werden können.
Wirklich ernst ist dieses Problem aber nicht, da man in 99,9% aller
Fälle sowieso auf das Goto verzichtet und in den verbleibenden 0,1% auf
einen der bereits genannten Work-Arounds zurückgreifen kann.
Jörg W. schrieb:> es gibt Klassen, für deren Instanziierung ein Default-Konstruktor> (ohne Initialwerte) gar nicht sinnvoll oder machbar ist (bspw. wenn es> "const"-Mitglieder gibt, die nur in der Initialisierung einen Wert> bekommen können).>> Das dürfte eigentlich bei OO-Pascal nicht anders sein, aber das kenne> ich zu wenig.
M.W. müssen in Object-Pascal wie in ISO-Pascal alle Variablen am
Prozedur- bzw. Funktionsanfang deklariert werden. Das von dir
beschriebene Problem wird dadurch umgangen, dass der Konstruktor (und
auch der Destruktor) an passender Stelle innerhalb des Prozedurcodes
explizit aufgerufen wird. Das ist zwar nicht sehr elegant, aber dadurch
konnte wenigstens das Goto ohne die hier diskutierten Einschränkungen
implementiert werden, und das ist ja viel wichtiger ;-)
Yalu X. schrieb:> Denn auch ein Kommentar ergibt keinen ausführbaren Code, und> trotzdem darf er zwischen dem Label und dem eigentlichen Sprungziel> stehen.
Ein Kommentar ist syntaktisch einfach „nichts“, der wird schon vom
Scanner weggeworfen. In der eigentlichen Syntax taucht der dann
gar nicht mehr auf.
Ja, geärgert habe ich mich über diese Restriktion auch schon. Wie
schon dargelegt, ich mach' dann einfach einen neuen Block auf.
> Das ist zwar nicht sehr elegant, aber dadurch> konnte wenigstens das Goto ohne die hier diskutierten Einschränkungen> implementiert werden, und das ist ja viel wichtiger ;-)
:-))
Yalu X. schrieb:>> Du kannst also nicht nachvollziehen, warum diese Syntax Käse ist? ;)>> Warum das in der ISO-Norm so festgelegt wurde, verstehe ich ebensowenig> wie du.>> Dass einer Deklaration kein ausführbarer Code zugeordnet werden und sie> deswegen nicht angesprungen werden kann, ist für mich keine stichhaltige> Begründung. Denn auch ein Kommentar ergibt keinen ausführbaren Code, und> trotzdem darf er zwischen dem Label und dem eigentlichen Sprungziel> stehen.
Gratulation!
Jetzt seid ihr schon zwei.
Dabei ist dieses ausnahmsweise mal ganz logisch. Tja, Yalu, auch
Zeilenschaltungen und Leerzeichen dürfen so ziemlich überall stehen,
inclusive Kommentare. Warum? Weil sie keine Bedeutung für den Compiler
haben, sondern eben nur Bedeutung für uns Menschen (beim Lesen - jaja!).
Mit einer Sprungmarke kann und soll man aber nur tatsächlichen Code
versehen, denn schlußendlich wird daraus ja eine Adresse im Code, zu der
der Prozessor hinspringen soll.
Eine Vereinbarung zwischen Codeschreiber und Compiler hingegen ist kein
Code und wird auch bis zum jüngsten Gericht keiner. Es ist eben nicht
anderes als ein Ansinnen an den Compiler "Mach mir mal ne Variable, ich
nenne die mal Joerg, den Typ sag ich dir ebenfalls dazu".
Man kann das auch ganz formal sehen: Code kommt ins Codesegment, also
dorthin, wo er ausführbar ist und in der Abfolge, die der Programmierer
haben will. Daten hingegen werden in andere Segmente gelegt wie
Datensegment, Stacksegment und so, und das sind in der Regel eben nicht
ausführbare Segmente, weswegen eine Sprungmarke dorthin keinen Sinn
ergibt.
Ganz stringent ist das bei der Harvard-Architektur.
Wolltest du also eine Marke auf den Stack legen und den Prozessor später
veranlassen, in selbigen hineinzuspringen?
Ist dir jetzt die Logik dahinter klar geworden?
(es ist ja nicht so, daß bei C alles nur unlogisch wäre.. ;-)
W.S.
W.S. schrieb:> Dabei ist dieses ausnahmsweise mal ganz logisch.
Logisch ist das schon, etwas anderes habe ich ja auch nicht behauptet.
Es ist nur unpraktisch.
Man hätte die Praktikabilität erhöhen können, ohne mit der Logik zu
brechen, wenn man in der C-Spezifikation festgelegt hätte,
Yalu X. schrieb:> dass sich ein Label immer auf die jeweils nächste Anweisung bezieht,> unabhängig davon, wieviel nichtausführbarer Code dazwischen steht.
Aber auch um die ursprüngliche Logik voll zu erfüllen, sollte ein Label
zumindest auch vor Definitionen zugelassen sein, die mit ausführbaren
Code verbunden sind, also vor dem Anlegen von VLAs in C und vor der
Instanziierung von Objekten in C++.
Yalu X. schrieb:> Aber auch um die ursprüngliche Logik voll zu erfüllen, sollte ein Label> zumindest auch vor Definitionen zugelassen sein, die mit ausführbaren> Code verbunden sind, also vor dem Anlegen von VLAs in C und vor der> Instanziierung von Objekten in C++.
Da würde ja schon das Anlegen einer lokalen Variable mit Initialisierung
reichen. Irgendwie muss der Initialisierungswert da ja reinkommen.
Umgekehrt kann der Compiler auch Teile des Code wegoptimieren, so dass
hinter der Sprungmarke auch etwas steht, das nicht direkt zu
ausführbarem Code führt. Aber ob hinter den Kulissen für etwas Code
erzeugt wird, sollte völlig egal sein. Genau dafür hat man ja eine
Hochsprache.
Dass W.S. jetzt ausgerechnet diese völlig unnötige Beschränkung als den
logischen Teil von C betrachtet, ist dann doch schon recht erstaunlich.
Vielleicht ist's aber doch auf das zurückzuführen:
Beitrag "Re: Sinn hinter "a label can only be part of a statement and a declaration is not a statement""
Jörg W. schrieb:> Ein Kommentar ist syntaktisch einfach „nichts“,
Nö :-)
Ein Kommentar wir wie white space behandelt — nimm z.B. folgenden C90
Code, der syntaktisch ok ist:
1
int f (int a, int *p)
2
{
3
return a//**/*p;
4
}
Nach Entfernen des Kommentars ist der Code nicht mehr syntaktisch
korrekt:
Johann L. schrieb:> Ein Kommentar wir wie white space behandelt
Jaja, ich weiß, aber er ist ein Element auf Ebene der lexikalischen
Analyse, welches (wie white space) nur die Tokens trennt.
Yalu X. schrieb:> Man hätte die Praktikabilität erhöhen können, ohne mit der Logik zu> brechen, wenn man in der C-Spezifikation festgelegt hätte,..>> dass sich ein Label immer auf die jeweils nächste Anweisung bezieht,>> unabhängig davon, wieviel nichtausführbarer Code dazwischen steht.
Um nochmehr Pfusch zu erzeugen?
Ach nö. Davon haben wir schon mehr als genug.
Du willst also sowas
Marke:
Deklaration von irgendwas
nochmal ne Deklaration
und nochmal... ad libitum
Anweisung;
und die Marke soll sich auf 'Anweisung' beziehen? Na dann schreib sie
doch direkt vor die Anweisung und die Welt ist bereits jetzt in Ordnung.
Allerdings ist mir klar, was dir eigentlich vorschwebt: Du willst von
außerhalb mitten in einen Block springen, wo du am Blockanfang lokale
Variablen deklariert hast.
Das ist maschinentechnischer Mist, denn du hast ganz vergessen, daß am
Blockbeginn ja der Stackframe durch die lokalen Variablen verändert
wird. Es fällt auch schon optisch auf, denn jeder der sowas liest:
1
gotojoerg;
2
{charyalu[99];
3
joerg:
4
yalu[1]='X';
5
...
6
...
7
}
sieht rein optisch, daß ja durch den Sprung char yalu[99]; schlichtweg
übersprungen wird. Ist nur optisch, trotzdem ein Ding der Unmöglichkeit,
eben wegen der Anleg- und Aufräumarbeiten bein Anfang und Ende des
Blocks.
W.S.
W.S. schrieb:> Allerdings ist mir klar, was dir eigentlich vorschwebt: Du willst von> außerhalb mitten in einen Block springen, wo du am Blockanfang lokale> Variablen deklariert hast.
Nein, will ich nicht. Was ich will bzw. wo ich ein (zwar nicht
schwerwiegendes, aber dennoch existentes) Problem sehe, habe ich oben
anhand eines Beispiels dargelegt.
Yalu X. schrieb:> Nein, will ich nicht.
Doch, willst du ja doch.
Du hast bloß aus Scham die notwendigen Blockgrenzen (also hier die
Klammern) verschwiegen. Aber die sind nötig für einen Block und der ist
nötig für eine Variablendeklaration - eben wegen Änderung am Stackframe.
Weißt du, wenn Kunst von Wollen käme, hieße sie Wunst. Aber sie kommt
vom Können, nicht vom Wollen. Ich wünsch mir manchmal auch ein Stückchen
vom Schlaraffenland, aber ich kann's wirklich nicht herbeizaubern -
obwohl ich es Wollen würde.
W.S.
W.S. schrieb:> eben wegen Änderung am Stackframe.
Zeige mir einen einigermaßen aktuellen Compiler, der Stackframes
pro Block einrichten würde. Die werden (sofern nötig) pro
Funktion eingerichtet, völlig unabhängig vom Scope der Variablen.
Der letzte Compiler, bei dem ich den Versuch erlebt habe, einen
Stackframe pro Block einzurichten, war ein alter „Dr. Keil“ für
m68k auf OS9 vor mehr als 25 Jahren. „Versuch“ deshalb, weil die
Krücke genau da einen Bug hatte (den ich mit dem Primitivdebugger
tagelang suchen musste), denn sie hat die Variable für einen
geschachtelten Block zwar als Stackframe eingerichtet, diesen aber
dann nicht wieder ausgeräumt. :(
W.S. schrieb:> Doch, willst du ja doch.
Jetzt machst du mir aber Angst. Ich will etwas, obwohl ich es gar nicht
will, und du kannst Gedanken lesen, wo gar keine sind? Das ist ja noch
verwirrender als ein Gestrüpp von 100 kreuz-und-queren Gotos.
> Du hast bloß aus Scham die notwendigen Blockgrenzen (also hier die> Klammern) verschwiegen. Aber die sind nötig für einen Block und der ist> nötig für eine Variablendeklaration - eben wegen Änderung am Stackframe.
Tut mir leid, ich habe doch tatsächlich die Blockanfangsklammer (nicht
jedoch die Blockendeklammer) vergessen. Hiermit reiche ich sie nach:
1
{// <-- Blockanfang
2
intn;
3
4
// ...
5
6
n=foo();
7
gotolabel;
8
9
// ...
10
11
n=bar();
12
13
label:// Fehler
14
inta[n];
15
16
baz(a,n);
17
18
// ...
19
}
Diese Klammer ändert aber überhaupt nichts daran, dass das Label (ohne
zusätzliche Tricks) so nicht platziert werden kann.