Hallo
in einem Programm muss ich a ca. 10 mal abfragen auf wahr oder falsch.
Habe es bisher so gemacht:
1
if(a==1)
2
{dannmachnr1;}
3
4
if(a==2)
5
{dannmachnr2;}
6
7
if(a==3)
8
{dannmachnr3;}
9
10
if(a==4)
11
{dannmachnr4;}
12
....
Eine andere Möglichkeit ist:
1
if(a==1)
2
{dannmachnr1;}
3
elseif(a==2)
4
{dannmachnr2;}
5
elseif(a==3)
6
{dannmachnr3;}
7
elseif(a==4)
8
{dannmachnr4;}
9
....
Bei 10 Abfragen wird das ganz schön lang. Wahrscheinlich muss ich das
auf 16 Abfragen verlängern. Wird noch länger.
Wie kann ich das ganze einfacher gestalten?
a wird zwischen 0 und 64 liegen.
C, 16MHz, At128
achim
switch/case entspricht exakt dem "else if" Konstrukt. Wenn "dann mach nr
1" auch a verändert, dann sind deine beiden geposteten Konstrukte
verschieden.
Sobald du mit if () einen bestimmten Wert von a erkannt und
behandelt hast, solltest du die Abfrage-Liste (mit dem
viel-gefürchteten GOTO) verlassen, um für einen neuen Wert
von a bereit zu sein.
Alles andere führt zu Unübersichtlichkeit, oder Fehlern...
Bei If else if else, oder switch case break / default
ist das eben (ohne böses GOTO) schon im Code eingebaut.
Verkürzen könnte man das Ganze nur, wenn Redundanzen vorliegen,
also der Wert von a in Gruppen zusammengefasst werden kann:
if ( a > 7 && a < 19 )
....
Mathias M. schrieb:> switch/case entspricht exakt dem "else if" Konstrukt. Wenn "dann> mach nr 1" auch a verändert, dann sind deine beiden geposteten> Konstrukte verschieden.
Nicht ganz. Ein switch/case Konstrukt entspricht nur dem else if wenn
das "break" in jedem case vorhanden ist.
Der Kompiler macht aber aus switch/case/break das selbe wie if/else.
Rene H. schrieb:
> Der Kompiler macht aber aus switch/case/break das selbe wie if/else.
Was soll der Compiler denn auch anderes machen?
So ein µC/µP hat Vergleichsbefehle und (bedingte) Sprungbefehle.
Und wenn das Gleiche passieren soll, muss (optimiert) auch der
gleiche Code rauskommen.
Bei grösseren Konstrukten kann die Laufzeit schon ein Problem werden. Es
wird eben von oben nach unten stur verglichen. Wenn das stört, kann man
eine Vorauswahl treffen und die Sache auf mehrere switch/case-Blöcke
aufteilen.
In Assembler hätte man da deutlich schnellere Alternativen mit einer
Sprungtabelle
JP (HL) (Z80)
IJMP (AVR)
etc
In C geht das mal ausnahmsweise nicht so effizient.
Mal eine Frage am Rande: Sind die Aktionen in den 16 Zweigen tatsächlich
völlig unabhängig voneinander oder gibt es da Gemeinsamkeiten? Genauer
gefragt, kann man die 16 Aktionen derart in Teilaktionen zerlegen, dass
die Anzahl benötigter Teilaktionen merklich geringer wird? Könnten die
Teilaktionen dann immer in der gleichen Reihenfolge ausgeführt werden?
Und wohin "returnt" die void machx() Funktion?
Wahrscheinlich wieder in die Vergleichsroutine -
und dann kommt entweder ein JUMP (C: break) zum Ende der
Vergleichsroutine, oder sie wird genauso stur weiter
abgearbeitet...
Das hatten wir doch schon...
A. H. (ah8) schrieb:
> kann man die 16 Aktionen derart in Teilaktionen zerlegen, dass
Hatte ich auch schon mit:
"... der Wert von a in Gruppen zusammengefasst werden kann..."
Nützt aber nix, wenn der TO nicht mehr reinschaut.
Ich geh jetzt auch schlafen.
A. H. schrieb:> Mal eine Frage am Rande: Sind die Aktionen in den 16 Zweigen tatsächlich> völlig unabhängig voneinander oder gibt es da Gemeinsamkeiten?
Wenn es Gemeinsamkeiten gibt, findet der Compiler sie auch. Du wirst
staunen, was da in dem resultierenden Asm bei rauskommen kann. Das merkt
man immer sehr schön daran, wenn man in irgendeinem Case-Zweig einen
Breakpoint setzen möchte, dieses aber nicht geht.
Das kriegt man händisch sehr selten besser hin. Und wenn man nicht weiß,
wie der Compiler tickt, gar nicht.
Einfach den ganzen Krempel nacheinander hinschreiben, um den Rest
kümmert sich der Compiler.
Natürlich nur bei eingeschalteter Optimierung.
Jakob schrieb:> Hatte ich auch schon mit:> "... der Wert von a in Gruppen zusammengefasst werden kann..."
Nicht ganz. Ich möchte ja nicht a in Gruppen zusammenfassen, sondern die
aus a resultierenden Aktionen in Gruppen zerlegen. Also etwa
Mein grosses V. schrieb:> Wenn es Gemeinsamkeiten gibt, findet der Compiler sie auch. ...
Ja, das will ich jetzt nicht abstreiten und ich bin auch absolut Deiner
Meinung, dass es in den meisten Fällen wohl das Beste wäre, den Code
einfach so stehen zu lassen (Optimierung hin oder her). Aber die Frage
lautete nun mal, ob man das einfacher gestalten kann und ich glaube
nicht, das der TO mit man den Compiler meinte. :-)
Wobei jede Antwort natürlich erst einmal darauf hinaus läuft, es
anders zu machen. Ob anders wirklich auch einfacher heisst steht
dann noch auf einem anderen Blatt.
Easier : No.
Optimizing speed : Yes.
I program in Bascom, but I think the idea of what is done, and how to
translate it back to your programming language is do-able. OK,. I assume
16 steps. By the following you have a max. of 4 steps, rather than
performing all 16 steps checking the value of "A":
1
If A < 9 then '1 .. 8
2
If A < 5 then '1 .. 4
3
If A < 3 then '1, 2
4
If A < 2 then
5
Call Routine_1()
6
Else
7
Call Routine_2()
8
End If
9
Else '3, 4
10
If A < 4 then
11
Call Routine_3()
12
Else
13
Call Routine_4()
14
End If
15
End If
16
Else ' > 4 '5 .. 8
17
If A < 7 then '5, 6
18
If A < 6 then
19
Call Routine_5()
20
Else
21
Call Routine_6()
22
End If
23
Else '7, 8
24
If A < 8 then
25
Call Routine_7()
26
Else
27
Call Routine_8()
28
End If
29
End If
30
End If
31
Else '9 .. 16
32
If A <= 12 then
33
If A < 11 then '9, 10
34
If A < 10 then
35
Call Routine_9()
36
Else
37
Call Routine_10()
38
End If
39
Else '11, 12
40
If A < 12 then
41
Call Routine_11()
42
Else
43
Call Routine_12()
44
End If
45
End If
46
Else '13 .. 16
47
If A < 15 then
48
If A < 14 then '13, 14
49
Call Routine_13()
50
Else
51
Call Routine_14()
52
End If
53
Else '15, 16
54
If A < 16 then
55
Call Routine_15()
56
Else
57
Call Routine_16()
58
End If
59
End If
60
End If
61
End If
I admit, it is more code, but this will be executed faster in a
microcontroller. If I'm not mistaken, a "Switch" or "Case" will be
translated back into a multiple "If-Else"-construction. You can verify
this by trying both and check the size of the final binairy file.
Next to it, it's good to know if your microcontroller is executing a
"smaller then"-command faster than "larger then"-command. Sounds odd,
but some microcontrollers operate by these unknown / hidden "features".
Also in such case you can win some time.
Furthermore, I'm pretty sure, your routines for each value of "A" show
some similarities. This can also be done by coding some general routines
which has to be run through, depending for the value of "A".
P.S. I'm sorry for writing in English. I do speak better English than
German.
--
Thou shalt use the [ pre ] [ /pre ] tags.
-rufus
As he wrote, it looks more complicated, but is faster to execute. The
typical "divide and command" approach.
AND it´s harder to debug and become more and more complicate to
maintain. If know 110% your argument won´t grow to more than 16 it´s
okay, but when you need to change the tree it becomes painfull...
BUT, i´m not even sure, if this is faster than a switch case. Also the
TO didn´t wrote about speed, just about readability. For example if the
compiler mirros the switch-case to a if-else-default structure the
divide-and-command approach is faster. But, if the compiler translates
to relative or absolute jumps, the select-case would be faster. Maybe
the translation depends on the optimization grade, if one calls a bunch
of single instructions within the case, if one calls a single function,
two+ following functions or if the called functions are defined as
inline.
If the main goal is readability i would stick to select-case-default.
If you feel, you need some kind of a speed up, you can define the
functions as inline (but results in bigger code size). "Maybe" this has
a second effect which might bring an additional acceleration:
if the functions in the cases are inline, the compiler could treat the
whole content of each case separately. So in his virtual mind he wraps a
pair of brackets around the content and says, "Okay, this is a specific
destination to jump to" and translates to a set of jump addresses. So
the compiler might translate the select-case differently because of the
"inline". If the functions are NOT inline, and the compiler optimizes to
code size, It might end in the if-else translation behind the scenes.
May be another approach could be to use an array of function pointers.
The idea is, one defines a set of functions, which would reflect the
areas of the select-case bodies or those of the if-else brackets.
So the argument one would use in the select-case would be used in the
array to reference the destination function to jump to.
Within that destination function one would write the actions to execute
or other function calls.
cons:
- reference argument must be checked before applied to the array or it
must be safe that argument dosn´t exceed the array
- argument must be a continuous value (?)
- names of functions get hidden behind pointers
pros:
- one can define a destination function more than once in the
func-point-array(applies to "if((arg == 2) || (arg == 6)) doJob2();"
- "i think" the execution time becomes deterministic, since one doesn´t
compare the argument to other values instead of using it directly to
jump somewhere. So one isn´t depended to the role-out-scheeme of the
compiler.
- "i don´t think" there is much for the compiler to interpret, or let´s
say it will be a smaller difference compared to the decision if to
translate a select-case to a bunch of if´s or jumps. So one knows more
exactly what´s going on in the controller. I have seen this approach in
RTOS´s as callback solution.
For the BASCOM example above, where sixteen different functions are
being called for sixteen distinct values, a table-driven approach using
function pointers would surely be faster -- except that a decent
compiler's optimization could be expected to break it down to exactly
that.
--
Für das BASCOM-Beispiel oben, das anhand 16 verschiedener Werte 16
verschiedene Funktionen aufruft, dürfte die Verwendung einer Tabelle mit
Funktionspointern effizienter sein. Wobei man sowieso davon ausgehen
sollte, daß die Optimierung eines anständigen Compilers mehr oder
weniger das gleiche daraus bastelt.
H.Joachim S. schrieb:> Bei grösseren Konstrukten kann die Laufzeit schon ein Problem werden. Es> wird eben von oben nach unten stur verglichen. Wenn das stört, kann man> eine Vorauswahl treffen und die Sache auf mehrere switch/case-Blöcke> aufteilen.>> In Assembler hätte man da deutlich schnellere Alternativen mit einer> Sprungtabelle> JP (HL) (Z80)> IJMP (AVR)> etc>> In C geht das mal ausnahmsweise nicht so effizient.
"Gute" C(++) Compiler machen genau dies auch bei switch case
Anweisungen, sofern das möglich ist. Das konnte schon Borland C++ 3.1.
Hier mal ein Beispiel, was der C++ Compiler von Rad Studio Seattle
daraus macht:
Jasson J. schrieb:> As he wrote, it looks more complicated, but is faster to execute. The> typical "divide and command" approach.
There are several ways to implement switch statements. If a compiler
isn't too stupid, one of the variants is a binary search tree like the
one you described.
A decent compiler analyzes attributes of the cases, like count,
distance, distribution pattern etc, and together with the speed/space
tradeoff hint it choses the algorithm to use. If it is really decent, it
may even split the cases into a few groups handled in a different way.
Different algorithms have different advantages and drawbacks. Often
people assume that 10 consecutive numbers should always result in a
table driven approach. However if you look at the code produced, you may
see a significant degree of fixed overhead associated with it. Bounds
checking and table access may result in a number of clock cycles which,
on average, is higher as a sequence of decrement/branch-if-zero or
compare-and-branch instructions, if the number of cases is low, e.g.
just 10.
This decision may result in different choices on different machines,
because they are not all the same in this respect. Especially if you
take branch prediction effects into account. And together with
statistics collected in test runs with a specially prepared executable,
the compiler may perhaps find out that there is a dedicated preference
for a few cases in actual use.
> AND it´s harder to debug and become more and more complicate to> maintain. If know 110% your argument won´t grow to more than 16 it´s> okay, but when you need to change the tree it becomes painfull...
That's one of the reasons why it is preferrable to let the compiler do
the optimization instead of doing manually. Adding a few cases may
completly throw over your manual decision tree, while a compiler isn't
hindered by yesterdays choices.
H.Joachim S. schrieb:> In Assembler hätte man da deutlich schnellere Alternativen mit einer> Sprungtabelle
Erstens kann der Compiler das auch. Wenn er kann. Und du vergisst dabei,
dass ein Tabellenverfahren erst einmal auf obere & untere Grenze prüfen,
die Tabellenadresse berechnen und deren Inhalt laden muss.
Tatsächlich kann die Laufzeiteffizienz von (z.B. 8051)
compare and branch
compare and branch
compare and branch
compare and branch
...
oder (viele)
subtract lower bound
branch if lower
branch if equal
subtract difference to next ordered case
branch if equal
subtract difference to next ordered case
branch if equal
subtract difference to next ordered case
branch if equal
...
bei nicht zu vielen Fällen ziemlich gut sein. Nachrechnen kann da
nützlicher sein als anfängliches Bauchgefühl.
> In C geht das mal ausnahmsweise nicht so effizient.
Prinzipiell nicht, oder bloss weil der Autor des Compiler nicht auf
diese Idee kam?
Ich verstehe die Diskussion nicht so recht.
Der TO fragt konkret nach "== x"-Abfragen - für mich schlicht
"switch-case" und nichts anderes. Keine > oder < Abfragen, nur "==".
Mir ist kein besseres Konstrukt in C bekannt.
Rene H. schrieb:> Der Kompiler macht aber aus switch/case/break das selbe wie if/else.
Nö, in der Regel optimiert er switch/case deutlich besser (binärer
Suchbaum). Und er erkennt automatisch, ab wann eine Sprungtabelle bei
aufeinanderfolgenden Case effektiver ist. Auch optimiert er gleiche
Sequenzen vor den breaks weg.
Oftmals ergibt sich aber eine riesige Optimierung, wenn der
Programmierer nur etwas Gehirnschmalz investiert und prüft, worin sich
die Cases ähnlich sind. Dann kann man die Cases oft völlig wegoptimieren
zu einer Funktion mit Argumenten. Und wenn sich die Argumente nicht aus
dem Case berechnen lassen, nimmt man sie eben aus einem Array.
Ein Beispiel findet man in meinem DCF77 Code.
Falls die "mach nr #" komplizierter sind, würde sich ein Array mit
Funktionspointern anbieten, dann sparst Du Dir das ganze
if/else-Konstrukt für den Preis eines Funktionsaufrufs. Wie das geht,
wurde z.B. hier diskutiert:
http://stackoverflow.com/questions/252748/how-can-i-use-an-array-of-function-pointers
Fall "mach nr #" nur ein einfacher Ein- oder Zweizeiler ist, dann ist
switch/case/break sicher das Mittel der Wahl. Bei den meisten Compilern
erzeugt das den gleichen oder ähnlichen Maschinencode wie eine
if/else-Kaskade, ist aber schöner zu lesen, je nach Geschmack.
Also wenn ich die Frage des TO: „Wie kann ich das ganze einfacher
gestalten?“ richtig interpretiere, dann geht es darum, wie man das
Problem einfacher notieren kann. Es geht also um die Gestaltung des
Quelltextes und das wäre zunächst einmal völlig unabhängig von der
Frage, welchen Zielcode der Compiler daraus gegebenenfalls konstruiert
oder auch nicht und wie optimal dieser sein mag.
Leider ist nicht ganz klar, was einfacher in diesem Zusammenhang
konkret heißt, so dass ich einfacher zunächst einfach mal als kürzer
interpretieren würde.
Dazu muss man nun feststellen, dass es für den allgemeinen Fall kaum
eine kürzere Form geben wird als eine Serie von if bzw. else if
Klauseln. Selbst ein switch wäre nicht wesentlich kürzer. Zwar spart
man den Vergleichsoperator und die Klammern um die Bedingung und
eventuell das compound statement – letzteres auch nur unter der
Voraussetzung, dass es keine Variablen gibt, die bezüglich eines Zweiges
lokal sein sollen – dafür aber wird ein zusätzliches break nötig,
dessen Existenz obendrein vom Compiler nicht geprüft werden kann.
Letzteres gilt zwar auch für jedes else aber bei Test auf Gleichheit
bei disjunkten Vergleichswerten wird ein fehlendes else im Gegensatz
zu einem fehlenden break keine Änderung der Semantik bewirken und
Unterschiede in der Performance dürften durch den Compiler wegoptimiert
werden. Darüber hinaus gestatten if Anweisungen komplexere
Vergleichsbedingungen, so das eine Serie von if Klauseln bei
vergleichbarer Länge die robustere und letztlich auch flexiblere Lösung
wäre.
Auch eine Sprungtabelle macht den Code im allgemeinen Fall keinesfalls
kürzer, sondern ganz Gegenteil, länger, denn nun braucht man pro Zweig
zwei Code-Einheiten: einmal den Eintrag in die Sprungtabelle und einmal
den Funktionskopf der Funktion, in der die Aktionen eines Zweiges
zusammengefasst werden sollen. Das wäre nur dann kürzer, wenn die
Funktionen für die einzelnen Zweige, aus welchen Gründen auch immer,
bereits existieren, was aber schon wieder einen Spezialfall darstellt.
Eine Verkürzung des Codes ließe sich meines Erachtens nach nur dann
erreichen, wenn durch Umkodierung der Vergleichswerte die Zahl der
Zweige substantiell verkleinert werden kann. Zum Beispiel ließe sich der
Code:
Das bringt, wie gesagt, natürlich nur etwas, wenn die Anzahl der Zweige
dadurch substantiell kleiner wird und hängt obendrein von einigen
Randbedingungen ab. Selbst dann würde ich die Anwendung einer solchen
Technik noch von der gleichzeitigen Verbesserung der Lesbarkeit abhängig
machen und die wäre nur gegeben, wenn das so umstrukturierte Programm
die Struktur des zu Grunde liegenden Problems besser widerspiegelt.
Ob es das tut lässt sich ohne Kenntnis des Problems und der Aktionen in
den Zweigen aber nicht entscheiden.
Rufus: thank you for modifying my message.
The main problem we face together here, is additional but missing
information. There's no info about the used microcontroller, if each
value (A) requires a different follow-up routine, and what
microcontroller is used (i.e. available flash-memory).
So far we guess what the best solution might be, and we won't be able to
find a consensus. Like said, we're all providing information (with our
best intentions and available knowledge), but it's the Topic starter who
should supply more detailed information about his specific project. I
think it would be a better (kick)start.
Hallo
du sprichst von Randbedingungen. In den unterschiedlichen ifs kann z.B.
so was drin stehen. Es hängt immer von der Abfrage nach a ab:
1
if(a==1)// wenn Key 0 gedrückt
2
{
3
b=0;
4
var&=~(0b00010000);// LED ein
5
}
oder sowas
1
if(a==32)// wenn Key 5 gedrückt
2
{// Einknopfbedienung
3
b=5;// Anzeige Display
4
if(touch_ready)// wenn touch ready auf 1 ...
5
{
6
if(h==0)// wenn Hilfvariable auf 0 ...
7
{
8
h=1;// Hilfsvariable auf 1
9
var&=~(0b10000000);// LED 1+4 ein
10
}
11
else
12
{
13
h=0;
14
var|=(0b10000000);// LED 4 aus
15
}
16
touch_ready=0;// setze touch rady auf 0
17
}
18
}
19
else
20
{
21
touch_ready=1;// touch ready auf 1 setzen
22
}
Wahrscheinlich ist die Version mit if() die passende Version. Je nach
Grund und Rahmenbedingung gibt es verschiedene Lösungen dazu. Ob eine
andere Lösung immer ein Vorteil bringt, ist wahrscheinlich zu
bezweifeln.
Danke für eure Hilfe
achim
Concerning my first message about the "divide and command"-approach
versus the "case/switch"-solution, I'd like to clarify some things.
Assume, you got a process, running the value A from 1 to 16.
By using "divide and command"-approach, each value will be found in 4
steps.
This means, (16 x 4=) 64 steps total.
Now, if you go for "case/switch" or the preliminary suggested
"if"-solution, you get the following :
If A = 1, result will be found in 1 step.
If A = 2, result will be found in 2 steps.
If A = ....
If A = ....
If A = 15, result will be found in 15 steps.
If A = 16, result will be found in 16 steps.
In other words, the total steps will be 1 + 2 + .. 15 + 16 = 136 steps.
However, I think it's wise to re-consider the routines, which should be
run after the found value of "A" has been found. Best practice is to
write the whole code in "if-else"-construction. After done, it's time to
analyze the written code.
1) At this point it's time to figure out if you can detect some
patterns, which can be translated into condensed math-formulae.
2) Next step is to see if the available program you write the code in,
offers additional features to condense this code.
3) If both solutions do not help, it's time to re-consider the
electronic schematic of your device.
But, it's more wise to start with step 3), when designing an schematic,
including the location of pins at your microcontroller (and ofcourse,
available memory).
Achim S. schrieb:> a wird zwischen 0 und 64 liegen.
Wahrscheinlich meinst du zwischen 0 und 63 ?
> Bei 10 Abfragen wird das ganz schön lang. Wahrscheinlich muss ich das> auf 16 Abfragen verlängern. Wird noch länger.> Wie kann ich das ganze einfacher gestalten?
Wie du auf 10 oder 16 Abfragen kommst, ist mir unklar - werden da nur
bestimmte Werte bearbeitet, andere sind 'else', oder meinst du dass
mindestens 10 oder 16 Abfragen nötig sind, um den Wert zu bestimmen
und entsprechende Aktion durchzuführen ?
Mach doch mal ein Array mit Sprungadressen, etwa so:
{KeyPress_00, // * Key0 -> LEDn ein
KeyPress_01, // * Key1
foo, foo, foo // * 2-4 no action
...
...
KeyPress_32, // * Key5 -> LEDchk -> Check4Touch
...
...
KeyPress_63 // * mach irgendetwas
}
Mit zusätzlichen Bemerkungen in der Tabelle wird es übersichtlicher
und einfacher zu bearbeiten - schneller ist es auf jeden Fall.
Bernard B. schrieb:> Concerning my first message about the "divide and command"-approach
The algorithm is officially called "binary search" and is fairly well
known in computer science. And it was already used in implementations of
switch statements at least 30 years ago.
> Now, if you go for "case/switch" or the preliminary suggested
Since efficiency is your concern, you have to look at the code produced.
You cannot simply guess the complexity of the resulting code by looking
at the source code.
> In other words, the total steps will be 1 + 2 + .. 15 + 16 = 136 steps
If you place your 1..16 iteration within the function itself, or at
least close enough, you could as well end in machine code not doing any
of those decisions, e.g. requiring 0 steps. If the optimizer recognizes
the correlation between iteration and decisions. ;-)
Achim S. schrieb:> Hallo> in einem Programm muss ich a ca. 10 mal abfragen auf wahr oder falsch.> Habe es bisher so gemacht:>
1
>if(a==1)
2
>{dannmachnr1;}
3
>
4
>if(a==2)
5
>{dannmachnr2;}
6
>
7
>if(a==3)
8
>{dannmachnr3;}
9
>
10
>if(a==4)
11
>{dannmachnr4;}
12
>....
13
>
> Eine andere Möglichkeit ist:>
1
>if(a==1)
2
>{dannmachnr1;}
3
>elseif(a==2)
4
>{dannmachnr2;}
5
>elseif(a==3)
6
>{dannmachnr3;}
7
>elseif(a==4)
8
>{dannmachnr4;}
9
>....
10
>
Das ist logisch nicht dasselbe. Wenn "mach..." die Variable a ändern
kann, prüft der erste Code die gegen den geänderten Wert, der zweite
nicht.
> Bei 10 Abfragen wird das ganz schön lang. Wahrscheinlich muss ich das> auf 16 Abfragen verlängern. Wird noch länger.> Wie kann ich das ganze einfacher gestalten?
Die Möglichkeit mit switch()..case wurde schon genannt. Wenn "mach nr.
1", "mach nr. 2" ... "mach nr. n" dieselbe Funktionssignatur haben,
kannst man aber auch shiqq mit Funktionszeigern arbeiten:
Wenn Deine Variable "a" mehr oder weniger alle Werte zwischen 0 und n
annehmen kann, kannst Du Dir den Typen "Int2Code_t" aber auch sparen und
mit Array-Indizes arbeiten und / oder bei Lücken (also Werten, die Deine
Variable a nicht annehmen kann) eine Dummy-Funktion einsetzen: