Forum: Compiler & IDEs Sinn hinter "a label can only be part of a statement and a declaration is not a statement"


von Nikolai (Gast)


Lesenswert?

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_t c = a - b;
5
6
// ...

muss man beispielsweise das schreiben:
1
// ...
2
3
int32_t c;
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?

von nicht“Gast“ (Gast)


Lesenswert?

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

von Christophe J. (chj)


Lesenswert?

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.

von Nikolai (Gast)


Lesenswert?

Danke für den Trick!


goto erhöht manchmal die Übersichtlichkeit.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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_t c = 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.

: Bearbeitet durch Moderator
von Peter D. (peda)


Lesenswert?

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.

von Sebastian L. (der_mechatroniker)


Lesenswert?

> 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).

von Peter D. (peda)


Lesenswert?

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:
1
  uint8_t x;
2
  blub();
3
  switch( y ){
4
    case 1:  x = baa(); break;
5
    default: x = foo(); break;
6
  }
7
  blab();
8
  return x;

von der mechatroniker (Gast)


Lesenswert?

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

von W.S. (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Klaus (Gast)


Lesenswert?

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

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
int x = 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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

W.S. schrieb:
> Also Rolf, deine Ansicht teile ich ganz und gar nicht.

Meinst du, uns würde das jetzt irgendwie wundern?

Ganz und gar nicht.

von Nikolai (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Nikolai (Gast)


Lesenswert?

Du kannst also nicht nachvollziehen, warum diese Syntax Käse ist? ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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“).

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
  int n;
2
3
  // ...
4
5
    n = foo();
6
    goto label;
7
8
  // ...
9
10
  n = bar();
11
12
label:        // Fehler
13
  int a[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
  int a[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 ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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 ;-)

:-))

von Markus F. (mfro)


Lesenswert?

Ihr werdet doch wohl noch irgendwo ein Semikolon übrig haben?

Wenn man schon goto verwendet, kommt's darauf auch nicht mehr an.

von W.S. (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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++.

von Rolf M. (rmagnus)


Lesenswert?

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""

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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:
 
1
int f (int a, int *p)
2
{
3
    return a/*p;
4
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Noch instruktiver und auch ab C99 und C++:
 
1
int inc (int a)
2
{
3
    return ++a;
4
}
 
vs.
1
int noinc (int a)
2
{
3
    return +/**/+a;
4
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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
 goto joerg;
2
 { char yalu[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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. :(

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
  int n;
3
4
  // ...
5
6
    n = foo();
7
    goto label;
8
9
  // ...
10
11
  n = bar();
12
13
label:        // Fehler
14
  int a[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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.