Hallo zusammen
Generell habe ich immer mal wieder Funktionen, die beim ersten Aufruf
etwas mehr tun, als bei allen späteren Aufrufen.
z.b. muss ein Wert berechnet werden, der später immer gleich bleibt oder
für ein Logfile wird der Header geschrieben usw..
Ich frage mich dann immer was wohl besser ist. gibt es vor- und
nachteile der folgenden zwei Möglichkeiten:
1
foo()
2
{
3
staticintinit=1;
4
if(init)
5
{
6
init=0;
7
//irgendwelches Zeug
8
}
9
//eigentlicher Funktionsteil
10
}
oder macht man generell besser zwei Funktionen:
1
intg_fooparam;
2
3
foo_init()
4
{
5
//init zeugs
6
g_fooparam=...;
7
}
8
9
foo()
10
{
11
//Funktion mit g_fooparam
12
}
Bisher habe ich immer Variante eins gewählt.
Danke für eure Meinung
Stefan
Das ist gehopst wie gehüpft.
Die erste Funktion ist minimal wartbarer, da alles zusammengefasst ist.
Die zweite Version könnte sinnvoll sein, wenn die nicht Init-Funktion
sehr häufig aufgerufen wird und Du um jeden Taktzyklus kämpfen musst.
Es kann auch sinnvoll sein die zweite Variante zu wählen, wenn Du stur
nach Chema-F arbeitest und vor Deinem eigentlichen Programm, alles was
-init heißt abarbeitest.
Ich bevorzuge die zweite Variante, da ich stur nach Chema Äff arbeite.
>foo()>{> static int init = 1;> if(init)> {> init = 0;> //irgendwelches Zeug> }> //eigentlicher Funktionsteil>}
wird komplett wegoptimiert zu
//eigentlicher Funktionsteil
da init sich nicht ändern kann....
Gruß J
JB schrieb:> "abgesehen..." jaja
Der Scope ist bezogen auf die gezeigten Zeilen irrelevant. Die Zuweisung
(Initialisierung) in "static int init = 1;" erfolgt nur einmal, in "int
init = 1;" jedesmal.
A. K. schrieb:> Der Scope ist bezogen auf die gezeigten Zeilen irrelevant. Die Zuweisung> in "static int init = 1;" erfolgt nur einmal, in "int init = 1;"> jedesmal.
Verstehe ich nicht.. Also ich weiss, dass es funktioniert. Mein Header
z.b. wird nur einmal geschrieben. alses was im
void foo()
{
static int a;
}
void baa()
{
static int b;
a = 10; //Zuweisung an a aus foo
}
int a; //Was passiert hier?
int main
{
int a = 10; //Und hier?
}
Danke für deine Mühe und Erklärung, man lernt halt nie aus bei C ;)
Bleib mal beim obigen Code und der obigen Aufgabe. Nur darauf bezog sich
meine Aussage. Aber wenn du es wirklich so drauf anlegst:
JB schrieb:>>foo()>>{>> static int init = 1;>> if(init)>> {>> init = 0;>> //irgendwelches Zeug>> }>> //eigentlicher Funktionsteil>>}>> wird komplett wegoptimiert zu>> //eigentlicher Funktionsteil
Die Aussage ist mit und ohne "static" falsch. Nur auf völlig
verschiedene Weise.
hmm, also das schockt mich jetzt etwas bzw. ich kanns fast nicht
glauben.
soll das heissen, meine
1
staticintinit;
sind alle global abrufbar?
Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine
funktion foo() und bar() usw.
schliesslich habe ich sicher 5 funktionen die genauso beginnen. und
probleme gabs meines wissens bis jetzt nicht.
Stefan A. M. schrieb:> static int init;
Nein.
> Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine> funktion foo() und bar() usw.
Ist auch so.
JB schrieb:> Heisst ich kann in einer Funktion (foo) eine Variable (init) lokal> definieren auf die ich im weiteren global zugriff habe?
Nein. Sie wird zwar global angelegt (nur einmal initialisiert und behält
ihren Wert von Funktionsaufruf zu Funktionsaufruf), aber du hast keinen
globalen Zugriff drauf, sondern nur lokal in der Funktion.
>> Ich hätte jetzt immer gedacht der scope dieser inits ist genau meine>> funktion foo() und bar() usw.>Ist auch so.
Dann erklär doch mal wie du die dann änderst außerhalb von foo?
A. K. schrieb:> Abgesehen vom Scope von "init" steht oben:>
1
>staticintinit=1;
2
>
3
>foo()
4
>{
5
>if(init)
6
>{
7
>init=0;
8
>//irgendwelches Zeug
9
>}
10
>//eigentlicher Funktionsteil
11
>}
12
>
Da diese Aussage offenbar von niemandem verstanden wird, mal
umformuliert:
Dieser Code entspricht dem analogen Ausschnitt des Code des ersten
Posting, mit Ausnahme der Sichtbarkeit von "init".
Ok jetzt hab ich's
weil
>...Sie wird zwar global angelegt (nur einmal initialisiert und behält>ihren Wert von Funktionsaufruf zu Funktionsaufruf), aber du hast keinen>globalen Zugriff drauf, sondern nur lokal in der Funktion.
wird nichts wegoptimiert. Danke!
Gruß Jonas Biensack
Stefan A. M. schrieb:> Hallo zusammen>> Generell habe ich immer mal wieder Funktionen, die beim ersten Aufruf> etwas mehr tun, als bei allen späteren Aufrufen.> z.b. muss ein Wert berechnet werden, der später immer gleich bleibt oder> für ein Logfile wird der Header geschrieben usw..>> Ich frage mich dann immer was wohl besser ist. gibt es vor- und> nachteile der folgenden zwei Möglichkeiten:>>
1
>foo()
2
>{
3
>staticintinit=1;
4
>if(init)
5
>{
6
>init=0;
7
>//irgendwelches Zeug
8
>}
9
>//eigentlicher Funktionsteil
10
>}
11
>
>
Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,
wenn von mehreren Threads / ATs verwendet.
Also: entweder pthread_once() oder ein atomic_flag mit
atomic_test_and_test().
Am besten von Anfang an richtig machen, denn solche Fehler findet man
nur sehr schwer später ...
Wilhelm M. schrieb:> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,> wenn von mehreren Threads / ATs verwendet.>> Also: entweder pthread_once() oder ein atomic_flag mit> atomic_test_and_test().>> Am besten von Anfang an richtig machen, denn solche Fehler findet man> nur sehr schwer später ...
99% vom code sind nicht Threadsafe - das ist auch normal so. Es macht
überhaupt keine sinn jede Funktion versuchen sicher zu machen.
Wilhelm M. schrieb:> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,> wenn von mehreren Threads / ATs verwendet.
Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs
präsentierte Variante ausweicht: eine getrennte Init-Funktion.
Thread-Locking für solchen Kleinkram wär schon ziemlich mit Kanonen auf
Spatzen geschossen.
A. K. schrieb:> Wilhelm M. schrieb:>> Nur als Hinweis: der Code enthält eine race-condition. Ist also falsch,>> wenn von mehreren Threads / ATs verwendet.>> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs> präsentierte Variante ausweicht: eine getrennte Init-Funktion.
Noch schlimmer ....
>> Thread-Locking für solchen Kleinkram wär schon ziemlich mit Kanonen auf> Spatzen geschossen.
Kleinkram? Wir wissen doch gar nicht um was es geht? Initialisierung
eines Pseudozufallszahlengenerators? Simulation mit 10.000 Threads?
Wow, nach dem ersten "wegoptimiert" Hijack kommt jetzt der Mutithreading
Hijack. ;-)
Stefan A. M. schrieb:> Ich frage mich dann immer was wohl besser ist. gibt es vor- und> nachteile der folgenden zwei Möglichkeiten:
Wilhelm M. schrieb:>> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs>> präsentierte Variante ausweicht: eine getrennte Init-Funktion.>> Noch schlimmer ....
Nö. Denn die Initialisierungsphase wird oft zu einem Zeitpunkt
stattfinden, in dem um gleiche Ressourcen konkurrierende Worker-Threads
noch nicht existieren.
> Simulation mit 10.000 Threads?
Eben. Wenn du etwas im Worker-Thread im laufzeitrelevanten Teil mit
Locks versiehst, das du locker heraustrennen könntest, dann kann es
passieren, dass dir das nutzlose Locking die halbe Leistung frisst.
A. K. schrieb:> Wilhelm M. schrieb:>>> Weshalb man spätestens in solchen Fällen besser auf die zweite anfangs>>> präsentierte Variante ausweicht: eine getrennte Init-Funktion.>>>> Noch schlimmer ....>> Nö. Denn die Initialisierungsphase wird oft zu einem Zeitpunkt> stattfinden, in dem um gleiche Ressourcen konkurrierende Worker-Threads> noch nicht existieren.
Eine Schnittstelle sollte zu allererst leicht richtig und schwer falsch
zu benutzen sein. Und den internen Zustand nach aussen zu transportieren
ist nie eine gute Idee ...
>>> Simulation mit 10.000 Threads?>> Eben. Wenn du etwas im Worker-Thread im laufzeitrelevanten Teil mit> Locks versiehst, das du locker heraustrennen könntest, dann kann es> passieren, dass dir das nutzlose Locking die halbe Leistung frisst.
atomic_flag ist lock-free.
Wilhelm M. schrieb:> Am besten von Anfang an richtig machen, denn solche Fehler findet man> nur sehr schwer später ...
Das Code, der nicht für parallele Threads mit konkurrierenden Ressourcen
geschrieben wurde, einen entsprechenden Update nicht übersteht, ohne ein
paar Federn zu lassen, ist wohl selbstverständlich.
Dass man deshalb nicht jedes Programm gleich thread-fest programmieren
muss, allerdings auch. Nicht bei jeder Anwendung lohnt sich der Zirkus.
Wilhelm M. schrieb:> atomic_flag ist lock-free.
Schonmal über die sukzessive Entwicklung der x86er auf Hardware-Ebene
drauf geachtet, was ein LOCK XCHG kosten kann?
Stilfrage: Um sicherzustellen, dass die Initialisierung nicht als
Seiteneffekt eines Übertrampelns der Variablen init doch mehrfach
aufgerufen wird (ja, sowas passiert, und laut Murphy nur im Feld, und es
dauert ewig, das zu finden), ziehe ich eine strenge Prüfung vor:
#define INITONCESIG 0x54543127
foo()
{
static int init = INITONCESIG;
if(init == INITONCESIG)
{
init = ~INITONCESIG;
//irgendwelches Zeug
}
//eigentlicher Funktionsteil
}
oder irgendwie sonst so, dass es schon mit Doppelmurphy zugehen muss, um
die Initialisierung doch mehrfach durchlaufen zu lassen...
mit der Strategie kann man übrigens auch das Verständnisproblem mit der
Einmalinitialisierung umgehen:
foo()
{
static int init;
if(init != INITONCESIG)
{
init = INITONCESIG;
//irgendwelches Zeug
}
//eigentlicher Funktionsteil
}
(was aber impliziert, dass init in die .bss gelegt wird; liegt sie in
einem vom Startupcode nicht angefassten Bereich, ist die
Wahrscheinlichkeit relativ hoch, dass sie nach einem Restart ohne power
up genau den Wert hat, bei dem sie aufgehört hat...)
A. K. schrieb:> Wilhelm M. schrieb:>> Am besten von Anfang an richtig machen, denn solche Fehler findet man>> nur sehr schwer später ...>> Das Code, der nicht für parallele Threads mit konkurrierenden Ressourcen> geschrieben wurde, einen entsprechenden Update nicht übersteht, ohne ein> paar Federn zu lassen, ist wohl selbstverständlich.>> Dass man deshalb nicht jedes Programm gleich thread-fest programmieren> muss, allerdings auch. Nicht bei jeder Anwendung lohnt sich der Zirkus.
Mir ist doch auch klar, dass es auf dieser Welt viel Code gibt, der
nicht MT-safe ist.
Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die
Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass
Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung
darstellt, und nicht die allg. Lösung ist. Und wie D.Knuth schon sagte:
premature optimization is the root of all evil.
Bottom line: entweder MT-safe oder extrem deutlich machen bzw.
unkompilierbar machen, wenn dem nicht so ist.
Und bitte: es muss nicht jeder einverstanden sein!
Mein erster Post in diesem Thread begann mit: Nur als Hinweis ...
Ruediger A. schrieb:>> foo()> {> static int init;> if(init != INITONCESIG)> {> init = INITONCESIG;> //irgendwelches Zeug> }> //eigentlicher Funktionsteil> }>
oops, ist natürlich Käse, weil diese Lösung auch wieder das
Übertrampelproblem der Originallösung hat... sorry 4 the noise.
Wilhelm M. schrieb:> Und wie D.Knuth schon sagte:> premature optimization is the root of all evil.
Ich habe zwar dafür grad keinen bessere Spruch drauf, aber Code
thread-safe aussehen zu lassen, ohne das in allen relevanten Situationen
getestet zu haben, ist sowas wie die Wurzel der Wurzel allen Übels. ;-)
> Und bitte: es muss nicht jeder einverstanden sein!
Hoffentlich! Wär echt langweilig.
Zur eigentlichen Frage:
die erste Version ist z.B. besser, wenn
- eine getrennte Initfunktion sonst nicht üblich ist
- das Init erst nachträglich notwendig wird (und 50 ähnliche Funktionen
ohne auskommen)
- es wirklich nur eine Funtkion ist (und kein aufwendiges Modul mit
dutzenden Funktionen)
die zweite Version ist
- thematisch "sauberer",
- erlaubt evt. die Neu-Initialisierung
- erlaubt ggf. unterschiedliche Parameter fürs Init
- spart auf den ersten Blick zur Laufzeit eine If-Abfrage (auf den
zweiten Blick wird es dadurch viel fragiler)
Darum ruhig beides verbinden:
a) explizite Init-Funktion(en)
b) Trotzdem die variante 1, bei der dann ggf. die Init-Funktion (von a)
aufgerufen wird. Ggf mit Trace-Infos, Debug-Ausgaben, Assert, ... . Dazu
muss das init natürlich auf File-Sichtbarkeit gehoben werden. Auch ist
ein "sinnvoller" default zu wählen, wenn Init parametriert möglich ist.
c) ich verwende die inverse logic:
1
staticintinit=0;
2
3
if(!init)
4
{
5
init=1;
6
}
7
]
In Deinem Fall liest sich init als imperativ (initialisiere!), in meinem
als Zustandsfrage (NOT initialisiert). Daher ist init zweideutig. Du
könntest Dein Flag z.B. doInit nennen, ich meines initialized. Ich
bevorzuge meine Variante, da 0 default ist.
Hmmmm, nun können wir anfangen Haare zu spalten...
Deine und meine (1.) Variante machen eine strengere Prüfung als der OP,
weil bei uns jeweils die Prüfbedingung bei genau einem von 2^32 (oder
wie gross immer ein int ist) zutrifft, beim OP bei (2^32)-1, d.h. im
Falle eines undefinierten Wertes (z.B. durch Übertrampeln) ist die
Wahrscheinlichkeit, die Initialisierung fälschlich mehrfach zu
durchlaufen, beim Originalcode wesentlich höher.
Aber: 0 ist als Matchwert für den Einmaldurchlauf eher Suboptimal, da im
Laufe eines Programmablaufs die 0 wesentlich häufiger geschrieben wird
als jeder Andere Wert. Ich ziehe eine Signatur vor, die extrem
unwahrscheinlich irgendwo anders vorkommt (also NICHT 0, NICHT
0xffffffff sowie nichts, was in den adressierbaren Bereich des boards
fällt).
Das ist Haarspalterei, aber die Haare sind sehr dick. ich weiss nicht
wie oft bei meinen Kunden genau diese Fälle aufgetreten sind, und diese
Dinge herunterzutracen sind enorm zeitraubend...
>> Hmmmm, nun können wir anfangen Haare zu spalten...> Aber: 0 ist als Matchwert für den Einmaldurchlauf eher Suboptimal, da im> Laufe eines Programmablaufs die 0 wesentlich häufiger geschrieben wird> als jeder Andere Wert. Ich ziehe eine Signatur vor, die extrem> unwahrscheinlich irgendwo anders vorkommt (also NICHT 0, NICHT> 0xffffffff sowie nichts, was in den adressierbaren Bereich des boards> fällt).
Dann nehmt doch bitte bool! Passt semantisch, int ist semantisch falsch
...
alles klar wie kloßbrühe
Thread safeness ist kein Thema bei mir. Jede Funktion wird nur von je
genau einem Thread benutzt.
Ich bleibe also bei meiner Lösung Nr. 1.
weil ich
a) möglichst wenig globale (sichtbare) Variablen will.
b) nicht vergessen will, das foo_init() aufzurufen.
Wenn ich den youtube-lessions zu Branch-Prediction glauben darf, wird
mein Prozessor sowieso sehr schnell merken, dass der init-teil von foo()
niemals wieder aufgerufen wird. Daher auch keine
Geschwindigkeitseinbusse.
Ausser der compiler verzichtet wegen eines grossen init-teils auf
inlining, was er sonst vielleicht machen würde.
>> Dann nehmt doch bitte bool! Passt semantisch, int ist semantisch falsch> ...
Hat zwar überhaupt nix mit meinem Punkt zu tun, aber auch egal. OP ist
zufrieden, was will man mehr.
Wilhelm M. schrieb:> Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die> Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass> Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung> darstellt, und nicht die allg. Lösung ist.
Dann musst du das aber auch im gesamten Interface durchziehen. Gibt ja
sicher noch andere Funktionen, die auf einen initialisierten Zustand
angewiesen sind...
Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.
Damit ist man dann recht schnell beim Themenkomplex /Lazy
Initialization/. Der wiederum ist eine Optimierung (!) der Problemlösung
und nicht die allgemeine Lösung. Die Optimierung besteht darin,
Ressourcen erst bei Gebrauch anzufordern (=zu initialisieren) -
möglicherweise spart man damit eine Initialisierung. Darum sollte man
das vermeiden und lieber eine explizite Initialisierungs-Funktion
bereitstellen.
Lazy-Init geht in C++ überaus elegant, indem man z.B. das pimpl-Idiom
implementiert und dabei die Zugriffs-Operatoren (* und ->) überläd.
Nase schrieb:> Wilhelm M. schrieb:>> Aber: wir sind doch seit langem in einer Welt, in der Multi-Cores die>> Regel und nicht die Ausnahme sind. Deswegen bin ich der Meinung, dass>> Code, der nicht MT-Safe ist, eine Optimierung (!) einer Problemlösung>> darstellt, und nicht die allg. Lösung ist.>> Dann musst du das aber auch im gesamten Interface durchziehen. Gibt ja> sicher noch andere Funktionen, die auf einen initialisierten Zustand> angewiesen sind...> Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.
Hoffentlich nicht, eine "Funktion" mit einem Zustand ist keine (reine)
Funktion mehr. Und bei reinen Funktionen hat man keinen Stress mit
Nebenläufigkeit.
> Damit ist man dann recht schnell beim Themenkomplex /Lazy> Initialization/. Der wiederum ist eine Optimierung (!) der Problemlösung> und nicht die allgemeine Lösung. Die Optimierung besteht darin,> Ressourcen erst bei Gebrauch anzufordern (=zu initialisieren) -> möglicherweise spart man damit eine Initialisierung. Darum sollte man> das vermeiden und lieber eine explizite Initialisierungs-Funktion> bereitstellen.
Sowas nennt man Konstruktor ...
> Lazy-Init geht in C++ überaus elegant, indem man z.B. das pimpl-Idiom> implementiert
Ach was ;-)
PImpl allein reicht aber nicht: CoW auch noch ...
> und dabei die Zugriffs-Operatoren (* und ->) überläd.
Und damit hat das gar nix zu tun ...
Aber PImpl hat noch viele andere Vorteile (die schon fast entscheidender
sind).
Ich würde in eine Funktion nur so wenig Wissen über den Kontext
implementieren, wie nötig.
Bsp.: Header hinzufügen vor dem 1. Eintrag.
Es gibt eine Funktion, die sich um das Einfügen eines Headers kümmert
und andere für das Einfügen weitere Einträge ->
Single-Responsibility-Prinzip.
AddHeader()
{
//Header hinzufügen
}
AddEntry(Logeintrag entry)
{
//Eintrag hinzufügen
}
Trivial, aber wichtig: ein Header ist kein Entry!
Warum sollte eine Funktion entscheiden, was, wann zu tun ist?
Irgendwann wäre vielleicht die max. Anzahl an Einträgen erreicht. Die
Einträgen werden persistiert und die Liste geleert. Und dann?
Im 1. Beispiel: Programmneustart, damit init wieder auf 0 gesetzt wird.
Ups ^^
Dein 2. Bsp. ist schon besser.
Wer entscheidet nun, welche Funtkion zu nehmen ist?
Meine Regel: nur derjenige, der das Wissen darüber in der Hand haben
muss.
Im Logeintrag Bsp. wäre das beim Anlegen eines Objekts der Fall, das die
Einträge speichert:
CreateLog()
{
//Speicher allozieren.
AddHeader();
}
Wer auch immer das Loggen starten möchte, muss CreateLog() aufrufen.
Hoffe, das bringt dich weiter.
Wilhelm M. schrieb:>> Im Prinzip wird damit jede Funktion um ein "if(init)" bereichert.>> Hoffentlich nicht, eine "Funktion" mit einem Zustand ist keine (reine)> Funktion mehr. Und bei reinen Funktionen hat man keinen Stress mit> Nebenläufigkeit.
Aha, und welchen Sinn hat dann die ganze Geschichte, die wir hier gerade
durchdiskutieren? Schon die statische "init"-Variable in den
anfänglichen Beispielen stellt einen Zustand dar.
Wilhelm M. schrieb:> Sowas nennt man Konstruktor ...
Nein, das ist ja explizit gerade nicht der Konstruktor. Es geht
explizit nicht um RAII.
Sowas kann man in anderem Kontext z.B. Singleton nennen, oder
Construction-on-first-usage.
Wilhelm M. schrieb:> PImpl allein reicht aber nicht: CoW auch noch ...
Nein, es geht auch explizit nicht um CoW.
Es geht darum, den Konstruktur erst beim Gebrauch zu rufen oder sogar
dann erst dynamisch Speicher anzufordern. CoW will man in diesem Fall ja
gerade nicht.
Wilhelm M. schrieb:> Und damit hat das gar nix zu tun ...
Doch, genau damit hat das zu tun. Denn genau dort kann man die MT-feste
Konstruktion vornehmen. Das hat den schönen Vorteil, dass man noch einen
Container drumherum hat.
Gut nachvollziehbares Beispiel: QGlobalStatic aus Qt.
MaWin schrieb:> Über die trivialsten "Probleme" werden the längsten Threads geschrieben.
joa krass. Erste Frage auf der Homepage, und in diesem Subforum an der
Spitze dabei.
Für mich ist die Antwort klar. Solange keine Thread safeness oder
re-init nötig wird, bleibt Variante 1 Favorit.
zum Thema bool: ich mag keine bools. in Windows sind das sowieso nur
int, dann kann ich gleich das nehmen. Ausser ich kriege vom Kollegen
matlab-code, dann sind es unsigned char.
Variante 2 wird vielleicht dann interessant, wenn sie inline-bar wird
und extrem oft aufgerufen wird.
Stefan a. m. schrieb:> MaWin schrieb:>> Über die trivialsten "Probleme" werden the längsten Threads geschrieben.> zum Thema bool: ich mag keine bools.
Das ist natürlich ein starkes Argument ...
> in Windows sind das sowieso nur> int, dann kann ich gleich das nehmen.
... und der Rest ist ein String ...
Im Ernst: an einer streng-typisierten Sprache sind Datentypen so
ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar
(auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und
anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel
zum Erfolg ...
Eine Funktion, die bei gleichen Aufrufen unterschiedliche Sachen macht?
Dass ist ganz klar ein no go. Damit landet man ganz schnell in der
Debugging Hölle. Die Init Funktion ist dagegen sauber und ordentlich.
Der Inhalt des If sollte eh in eine Funktion ausgelagert werden, damit
man das testen kann. Dieser kann man auch den Namen Init geben und
direkt aufrufen.
Dann weiß auch Anwender, der deine Funktionen benutzt, was da passiert.
Wilhelm M. schrieb:> Im Ernst: an einer streng-typisierten Sprache sind Datentypen so> ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar> (auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und> anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel> zum Erfolg ...
Es ist richtig, bool zu nehmen. C89 oder C99 kennt bool aber nicht.
Darum macht es hier bei foo auch keinen Sinn und dann ist int als
nativer Typ des Compilers richtig.
Man kann lange diskutieren, ob Dinge dadurch einfacher werden, dass man
auch bei Spatzen seine Kanonen zum Einsatz bringt. Wenn jemand bei einem
mittleren Projekt kein Bool hat, läuft irgend was falsch. Schlimmer
ist aber, wenn er 2 Bool-Typen hat, eines von dem Beispiel hier und
eines von irgendeiner lib.
Achim S. schrieb:> Wilhelm M. schrieb:>> Im Ernst: an einer streng-typisierten Sprache sind Datentypen so>> ziemlich das wichtigste. Für C-Verfechter ist das etwas schwer sichtbar>> (auch wenn das Beispiel natürlich auch C++ sein könnte), in C++ und>> anderen Sprachen (meist OO) sind domänen-spezifische DT der Schlüssel>> zum Erfolg ...>> Es ist richtig, bool zu nehmen. C89 oder C99 kennt bool aber nicht.> Darum macht es hier bei foo auch keinen Sinn und dann ist int als> nativer Typ des Compilers richtig.
Nicht (mehr) richtig (C99 / C11):
http://en.cppreference.com/w/c/language/arithmetic_types#Boolean_type
Kaj schrieb:> Achim S. schrieb:>> C89 oder C99 kennt bool aber nicht.> Schon mal was von stdbool.h gehoert?
Man, steht doch genau ein Betrag vor Dir ...