PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C / C++ switch case und continue = schlechter stil?


Gast
2010-07-26, 14:57:20
hallo,

sprache: C / C++

ist es ein schlechter programmierstil wenn man continue in einer switch case abfrage verwendet?

Oid
2010-07-26, 15:52:36
Ich hoffe ich hab die Frage richtig verstanden...

Antwort: Ja, weil´s einfach überflüssig ist? ^^

RMC
2010-07-26, 16:05:32
Wenn das switch innerhalb einer Schleife liegt kann ein continue schon Sinn machen.

Neomi
2010-07-26, 16:47:53
Ob etwas ein guter oder schlechter Stil ist, hängt immer vom Kontext ab. Wenn es in der jeweiligen Situation einfacher zu lesen und/oder zu erweitern ist als eine funktional äquivalente Alternative, ist es kein schlechter Stil. Nichtmal dann, wenn es um ein goto geht.

firfin
2010-07-26, 17:05:35
ok danke

noid
2010-07-26, 17:30:28
Ob etwas ein guter oder schlechter Stil ist, hängt immer vom Kontext ab. Wenn es in der jeweiligen Situation einfacher zu lesen und/oder zu erweitern ist als eine funktional äquivalente Alternative, ist es kein schlechter Stil. Nichtmal dann, wenn es um ein goto geht.

http://tigcc.ticalc.org/doc/keywords.html#goto

Genau, nur dass man auch bei den "erlaubten" gotos ein break; nehmen kann. (Mir bisher noch nicht aufgefallen wann das nicht gehen sollte)

Coda
2010-07-26, 18:08:19
Wenn man aus zwei Schleifen ausbrechen muss beispielsweise. Kommt oft genug vor.

Gast
2010-07-26, 18:35:50
genau aus diesem grund hatte ich es auch drin, aber gab schelte vom prof

Elemental
2010-07-26, 22:04:53
Wenn man aus zwei Schleifen ausbrechen muss beispielsweise. Kommt oft genug vor.

Da breake ich immer die innere Schleife und setzte einen bool, damit dann die äussere auch begreaked wird. :freak:



for(int i=0;i<10;i++)
{
bool bFound = false;

for(int y=0;y<10;y++)
{
if(...irgendwas...)
{
bFound = true;
break;
}
}

if(bfound == true)
{
break;
}
}

Coda
2010-07-26, 22:51:49
Ist natürlich auch viel lesbarer und effizienter als ein einfaches goto :rolleyes:

Die Verteufelung des Statements nimmt wenn man sowas sieht schon absurde Züge an. Schlechte Programmierer schreiben auch ohne goto grottige Programme.

RMC
2010-07-26, 23:12:36
Wenn man aus zwei Schleifen ausbrechen muss beispielsweise. Kommt oft genug vor.

Mit continue ausbrechen? Versteh jetzt grad nicht was du meinst. Ich seh das nur im Zusammenhang mit "überspringen" sinnvoll. Break bricht ja eigentlich aus.

ShadowXX
2010-07-27, 00:18:54
Wenn man aus zwei Schleifen ausbrechen muss beispielsweise. Kommt oft genug vor.
Dafür braucht man einen einzigen bool Wert den man dann auf false setzt.

um obiges beispiel zu nehmen:

bool bFound = false;

for(int i=0;!bFound && i<10;i++)
{
for(int y=0;!bFound && y<10;y++)
{
if(...irgendwas...)
{
bFound = true;
}
}
}


Hübscher als Goto und nicht kompliziert zu lesen (ich persönlich finde gerade gotos extrem unleserlich....continue meistens auch, aber manchmal ist ein continue die bessere Lösung).

Coda
2010-07-27, 00:23:42
Auch das ist ineffizienter und zudem finde ich das eher unleserlich. Rumgewürge nur um ein Statement zu vermeiden, bei dem einen gehirnwäscheartig eingeredet wird es wäre böse.

noid
2010-07-27, 08:48:18
Auch das ist ineffizienter und zudem finde ich das eher unleserlich. Rumgewürge nur um ein Statement zu vermeiden, bei dem einen gehirnwäscheartig eingeredet wird es wäre böse.

Du kannst auch eine Funktion nehmen und return nutzen. Imho noch das Hübscheste - und solange das hier keiner auf multiplen compilern und unterschiedlichen optimierungsleveln ausprobiert ist das run-time-mäßig eh unaussagekräftig³

Belehre uns eines besseren, falls du Lust hast das zu benchen. (ich nicht, ich habe des öfteren gemerkt, dass "effizienter" Code einfach nur Gefrickel von gestern war - gerade unter C :freak:)

PS: in der bash gibts break 2; - also sowas wäre unter C ja wirklich klasse.

Ganon
2010-07-27, 08:53:33
Auch ganz witzig gemacht in dem Zusammenhang:

"Will it optimize?"
http://ridiculousfish.com/blog/archives/2010/07/23/will-it-optimize/#fish_made_a_mess

:D

firfin
2010-07-27, 09:48:08
genau aus diesem grund hatte ich es auch drin, aber gab schelte vom prof

moment ich muss mich verbessern:

mein code hat zwei ineinander geschachtelte while schleifen, jede dieser while schleifen hat eine switch case abfrage.
es soll aber nur bei einer gültigen eingabe in die zweite while schleife gesprungen werden.

um es zu verdeutlichen:


while (cInput != 'q')
{
cInput = _getch();
switch (cInput)
{

case 'h':
machDies();
break;

default:
continue;
}

while (cInput != 'o' && cInput != 'q')
{
cInput = _getch();
switch (cInput)
{
case 'o':
break;
.......
...
}
}

}


vielleicht is das ganze auch nicht sehr elegant :frown:, aber in dem fall ist doch ein continue vertretbar? alternative bool (eleganter?)
weitere alternativen?

Ectoplasma
2010-07-27, 12:37:43
Typ *sucheTypIn2DMatrix(matrix)
{
for (int i = 0; i < 10; ++i)
{
for (int y = 0; y < 10; ++y)
{
if (...irgendwas...)
{
return matrix[i][y];
}
}
}
return 0;
}

sucheTypIn2DMatrix(matrix);



Ich packe soetwas auch lieber immer in eine neue Funktion/Methode.

Mal ein kleiner Hinweis. Die Schleifenvariablen i und y werden üblicherweise mit i++/y++ inkrementiert. Steht auch in fast jedem Buch so. Eigentlich ist diese Art der Inkrementierung nicht ganz sauber, weil dieser Operator den vorherigen Wert der Variable zurückliefert. Bei bestimmten Iteratoren kann das sogar inperformant sein. Besser ist es in Schleifen mit ++i/++y zu inkrementieren, falls i und y Iteratoren sein sollten. Dieser Operator liefert den aktuellen Wert der Variable zurück und nicht den vorherigen, was zu einem unnötigen temporärem Objekt führt. Und es gibt auch Compiler, die so ein Objekt nicht wegoptimieren.

mekakic
2010-07-27, 13:26:38
Wo man gerade bei seltenen C-Statements ist: wie kann man volatile denn richtig verwenden? Die Beschreibung auf der oben verlinkten Seite klingt so als wäre das ein Synchronisationsmittel, aber bei dem Gedanken kriege ich irgendwie Angst.

Gast
2010-07-27, 13:46:21
Da breake ich immer die innere Schleife und setzte einen bool, damit dann die äussere auch begreaked wird. :freak:



for(int i=0;i<10;i++)
{
bool bFound = false;

for(int y=0;y<10;y++)
{
if(...irgendwas...)
{
bFound = true;
break;
}
}

if(bfound == true)
{
break;
}
}

Wer sowas macht, der sollte nicht über goto urteilen.

Goto soll man nicht nutzen damit eben nicht solch ein Code geschrieben wird und auf komplizierte Art und Weise ein goto zu umgehen nur um nicht "goto" zu schreiben ist kein merk besser vom Stil.

Brillus
2010-07-27, 14:21:22
Wo man gerade bei seltenen C-Statements ist: wie kann man volatile denn richtig verwenden? Die Beschreibung auf der oben verlinkten Seite klingt so als wäre das ein Synchronisationsmittel, aber bei dem Gedanken kriege ich irgendwie Angst.
volatile heißt das sich die variable ändern kann ohne zutun des Thread/Programmes, und deswegen jedes mal neu eingelesen werden muss wenn auch ein read von der variablen da steht (und wohl auch geschrieben werden muss wenn da schreiben steht) also bestimmte Compileroptimierungen nicht gemacht werden drüfen.

Passieren kann sowas z.B. wenn man mehrere Threads hat(Und da sichergehen will das jeder Thread immer mit den neusten Werten einer Variable arbeitet). Oder wenn man Variablen hat die in wirklichkeit Hardware regester sind. Sowas gibt es beim programmieren von Mikroprozessoren z.B. es gibt ein Register das dir ansagt ob eine Hardwareeinheit gbeschäftig ist oder nicht, da bleibt dir dann zum synchronisieren nichts anderes übrig als die Variable sooft auszulesen bis sie den gewünschten Wert hat.


#define REGISTER (*((volatile int*)0xADRESSE))

...

while(REGISTER!=DESIREDVALUE);
...


^^so ungefähr sieht das dann aus wobei das Define normalerweise in einer Includedatei steht die man vom Hersteller des Mikroprozessors bekommt.

Coda
2010-07-27, 14:59:37
Wo man gerade bei seltenen C-Statements ist: wie kann man volatile denn richtig verwenden? Die Beschreibung auf der oben verlinkten Seite klingt so als wäre das ein Synchronisationsmittel, aber bei dem Gedanken kriege ich irgendwie Angst.
volatile hat überhaupt nichts mit Synchronisierung zu tun. Leider tut es das aber in Java, was zu großer Verwirrung führt.

If we were designing a language from scratch, we believe it would be clearly prefereable to give volatile semantics that allow such variables to be used for inter-thread communication. However, making such a change now would add performance costs to some correct existing code. And the existing body of code would make it difficult or impossible to properly issue diagnostics for such thread-communication uses.

Was du meinst gibt's erst in C++0x mit atomic.


Du kannst auch eine Funktion nehmen und return nutzen. Imho noch das Hübscheste - und solange das hier keiner auf multiplen compilern und unterschiedlichen optimierungsleveln ausprobiert ist das run-time-mäßig eh unaussagekräftig³

Belehre uns eines besseren, falls du Lust hast das zu benchen. (ich nicht, ich habe des öfteren gemerkt, dass "effizienter" Code einfach nur Gefrickel von gestern war - gerade unter C :freak:)

PS: in der bash gibts break 2; - also sowas wäre unter C ja wirklich klasse.
In performancekritischen Teilen von Code kann sowas durchaus Auswirkungen haben. Auch was Cache-Lokalität angeht etc.

Hier mal Linus dazu: http://kerneltrap.org/node/553/2131. Ist natürlich wieder seine berüchtigte "strong opinion", aber meistens steckt halt doch Wahrheit dahinter ;)

Der ganze Linux-Source ist voll mit gotos (vor allem zur Exception-Behandlung), und das ist die am schnellsten wachsende Software überhaupt. Und ich sehe keine Qualitätsprobleme damit und finde den Source auch lesbar.

noid
2010-07-27, 15:42:09
volatile hat überhaupt nichts mit Synchronisierung zu tun. Leider tut es das aber in Java, was zu großer Verwirrung führt.



Was du meinst gibt's erst in C++0x mit atomic.



In performancekritischen Teilen von Code kann sowas durchaus Auswirkungen haben. Auch was Cache-Lokalität angeht etc.

Hier mal Linus dazu: http://kerneltrap.org/node/553/2131. Ist natürlich wieder seine berüchtigte "strong opinion", aber meistens steckt halt doch Wahrheit dahinter ;)

Der ganze Linux-Source ist voll mit gotos (vor allem zur Exception-Behandlung), und das ist die am schnellsten wachsende Software überhaupt. Und ich sehe keine Qualitätsprobleme damit und finde den Source auch lesbar.

Naja, ich drück's mal so aus: dein Beispiel und das in der eMail-Liste waren die ersten gotos, die einigermaßen Sinn ergeben. Was andere mit goto in meinem weiteren Umfeld "erbrochen" haben kannst du dir nicht vorstellen.

Zumal das mit Optimierungen wirklich nicht auf den ersten Blick vorhersagbar ist, je nach Plattform,

Insofern ist die Regel "verwende keine goto" gut, Ausnahmen bestätigen diese.

RMC
2010-07-27, 16:51:17
Also zugegeben, es mag Fälle geben, wo man wirklich extrem auf die Performance schauen muss und wo dann sogar gotos etwas mehr Übersicht bieten. Aber sonst steckt nicht viel Wahrheit drin. Eine Verschachtelung durch ein goto zu ersetzen weil "is quite common to have conditionals THAT DO NOT NEST"...c'mon :rolleyes: Mehr Religion als Wahrheit.

Aber in 99% aller Fälle gängiger Programmierpraxis gibts keinen Grund sowas zu verwenden. Ja, ich weiß dass alles im Endeffekt ein goto ist, jedes IF, BREAK, CONTINUE, jede Schleife etc. Darüber brauchen wir nicht reden. Diese Konstrukte erfüllen alle einen bestimmten Einsatzzweck.

Ein absichtlich unbedingter Sprung aber hat keinen definierten Zweck. Er lässt sich soweit immer vermeiden. Wenn man gotos braucht ist das normalerweise an Anzeichen dafür, dass mit der Struktur etwas prinzipiell nicht stimmt und irgendwo der Wurm drin ist und spätestens an dem Punkt sollte man seine Herangehensweise überdenken.

Aus optischen Gründen gibts soo viele andere Einsatzmöglichkeiten Code schön und sauber zu gestalten als ihn mit gotos zu verschmutzen und sich dadurch gröbere Probleme einzuhandeln. Vorallem dann, wenn man nicht allein programmiert und das auch noch zur Gewohnheit wird.

Für mich gilt da auch die Regel "never use gotos"

Brillus
2010-07-27, 17:23:15
volatile hat überhaupt nichts mit Synchronisierung zu tun. Leider tut es das aber in Java, was zu großer Verwirrung führt.
Wie kommst du drauf das volatile ncihts mit synchronisaion zu tuen hat? Es sorg dafür das, richtig angewendet, mehere Threads auf den selben daten Areiten also synchronisation. Natürlich ist es low level aber wenn du 2 Threads sync. willst ohne das du ein richtiges OS hast brauchst du solche sachen.

(Kein richtiges OS heiß z.B ein Timer der einfach stur alle x ms den Thread round robin mäßig wechselt)

Ectoplasma
2010-07-27, 17:58:32
Wie kommst du drauf das volatile ncihts mit synchronisaion zu tuen hat? Es sorg dafür das, richtig angewendet, mehere Threads auf den selben daten Areiten also synchronisation. Natürlich ist es low level aber wenn du 2 Threads sync. willst ohne das du ein richtiges OS hast brauchst du solche sachen.

(Kein richtiges OS heiß z.B ein Timer der einfach stur alle x ms den Thread round robin mäßig wechselt)

Nein, mit C volatile kannst du nicht sicher zwei Threads synchronisieren. Lies dir das (http://en.wikipedia.org/wiki/Memory_barrier) einmal durch. Vorallem der Abschnitt "Out-of-order execution versus compiler reordering optimizations" ist dabei interessant.

Gnafoo
2010-07-27, 22:28:27
Mal ein kleiner Hinweis. Die Schleifenvariablen i und y werden üblicherweise mit i++/y++ inkrementiert. Steht auch in fast jedem Buch so. Eigentlich ist diese Art der Inkrementierung nicht ganz sauber, weil dieser Operator den vorherigen Wert der Variable zurückliefert. Bei bestimmten Iteratoren kann das sogar inperformant sein. Besser ist es in Schleifen mit ++i/++y zu inkrementieren, falls i und y Iteratoren sein sollten. Dieser Operator liefert den aktuellen Wert der Variable zurück und nicht den vorherigen, was zu einem unnötigen temporärem Objekt führt. Und es gibt auch Compiler, die so ein Objekt nicht wegoptimieren.

Bei normalen Integern, Pointern etc. ist es soweit ich weiß völlig egal, da optimiert der Compiler das sowieso weg. Iteratoren sind eine andere Geschichte, weil da üblicherweise Objekte dahinter stecken. Das erstellen der Kopie beim Postinkrement erfordert hier einen Aufruf des Copy-Konstruktors und den darf der Compiler afaik gar nicht wegoptimieren (da kann ja auch im Grunde genommen beliebiger Code drinstehen).

Wobei das wohl auch selten ein wirkliches Performanceproblem sein dürfte.

Coda
2010-07-28, 00:12:44
Wie kommst du drauf das volatile ncihts mit synchronisaion zu tuen hat?
Weil es nichts damit zu tun hat ;)

Es sorg dafür das, richtig angewendet, mehere Threads auf den selben daten Areiten also synchronisation.
Nein, das tut es eben NICHT. Das tut es in Java, aber nicht mit der Semantik die definiert wurde in C++. Lies was ich schreibe. C++ bekommt das was du meinst erst mit C++0x und atomic<>.

Insofern ist die Regel "verwende keine goto" gut, Ausnahmen bestätigen diese.
Jup. Was anderes will ich auch überhaupt nicht sagen.

Ectoplasma
2010-07-28, 08:17:04
@Gnafoo, da stand ja auch "nur ein kleiner Hinweis". Man muss es nicht so machen, man kann es aber ;).

Nein, das tut es eben NICHT. Das tut es in Java, aber nicht mit der Semantik die definiert wurde in C++. Lies was ich schreibe. C++ bekommt das was du meinst erst mit C++0x und atomic<>.

Wobei man aber sagen sollte, dass es C/C++ Compiler gibt, die genau die Java Semantik implementieren. GCC ist z.B. so ein Kandidat. Dennoch sollte man sich allgemein nicht darauf verlassen.

Coda
2010-07-28, 14:48:54
Das stimmt nicht. Auch in GCC garantiert volatile keinen atomaren Zugriff.

Ectoplasma
2010-07-28, 15:27:04
Das stimmt nicht. Auch in GCC garantiert volatile keinen atomaren Zugriff.

Ich habe eigentlich an die Vermeidung einer Out-Of-Order-Execution gedacht. Den atomaren Zugriff muss man natürlich selber bauen oder greift auf Bibliotheken wie Boost zurück. Mir war nicht klar, dass in Java volatile auch gleichzeitg atomar ist. Wenn das nämlich so sein sollte, dann wäre das nicht wirklich schön.

Edit:

Hab nocheinmal nachgeschaut. Volatile in Java ist nur auf Objektreferenzen atomar, nicht aber bei integralen Typen.

volatile int i;
...
i += 5 // ist nicht atomar

Coda
2010-07-29, 23:46:09
Ich habe eigentlich an die Vermeidung einer Out-Of-Order-Execution gedacht.
Was meinst du damit? Die OOO-Ausführung der CPU ändert nichts an der Load- und Store-Reihenfolge. Falls du meinst, dass der Compiler dann Loads- und Stores nicht umsortiert, hast du recht.

Hab nocheinmal nachgeschaut. Volatile in Java ist nur auf Objektreferenzen atomar, nicht aber bei integralen Typen.
Mit der JVM 1.5 ist das auch bei integralen Typen der Fall. volatile hat in Java eine Memory-Fence-Semantik:

A field may be declared volatile, in which case the Java memory model (§17) ensures that all threads see a consistent value for the variable.

i += 5 // ist nicht atomar
Natürlich nicht, das is ja auch ein Load, eine Addition und ein Store.

i = foo; ist aber sehr wohl atomar in Java, wenn i volatile deklariert wurde. Was es in C++ mit volatile nicht ist.

Ectoplasma
2010-07-30, 20:26:37
Natürlich nicht, das is ja auch ein Load, eine Addition und ein Store.

i = foo; ist aber sehr wohl atomar in Java, wenn i volatile deklariert wurde. Was es in C++ mit volatile nicht ist.

Aha ok. Trotzdem weiß ich nicht, was daran "natürlich" sein soll. Es gibt ja CPU's, die sehr wohl ein load/add/store atomar ausführen können. Die Hintergründe, warum man das nur auf Zuweisungen eingrenzt, bleiben für mich im Verborgenen. Aber vielleicht weißt du ja, warum das so ist.

Coda
2010-07-31, 02:04:02
Auch wenn es solche CPUs gäbe, muss sich Java am kleinsten gemeinsamen Nenner orientieren. Ansonsten müssten sie jeden Zugriff mit einem Mutex locken. Performance ade.

x86 kann das jedenfalls nicht. Mir fallen da auch nur GPUs ein die atomare Add-Instructions hätten.

Ectoplasma
2010-07-31, 09:19:47
Mit dem kleinsten gemeinsamen Nenner magst du Recht haben. Was die x86 CPUs angeht so dachte ich immer, dass man eine atomare read/test/modify/write Operation, mit einem LOCK Prefix bewerkstelligen kann (lock xadd etc.)

Aber das Prinzip mit dem kleinsten gemeinsamen Nenner leuchtet schon ein.

Coda
2010-07-31, 13:46:12
Stimmt. XADD kannte ich noch gar nicht :|

Da kann man sich das LOCK übrigens dann auch sparen. Das ist offenbar bei allen Instructions die mit X anfangen implizit. LOCK INC geht offenbar aber auch. Wieder was gelernt.

Ectoplasma
2010-08-01, 09:51:55
Xadd hatte ich nur gewählt, weil es ein typischer Vertreter für atomare Operationen ist. Das X steht übrigens nur dafür, dass vor der Addition die beiden Operanden miteinander vertauscht werden. Du kannst ein LOCK Prefix vor folgende Befehle setzen: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD und XCHG. XCHG ist aber tatsächlich ein Befehl, der schon implizit atomar ist.