Hallo zusammen,
wenn ich in eine Schleife oder einen Quelltext-Teil an einer bestimmten
Stelle hereinspringen will, bietet sich switch-case an. Wie gehe ich
denn bei strukturierter Programmierung vor, wenn ich aus einem
Programmteil herausspringen will?
Als Beispiel habe ich mal einen UTF-8-Dekoder gemacht:
1
/** UTF8-Zeichenlaenge anhand des ersten Bytes ermitteln
2
* Die Laenge eines UTF-8-Zeichens (1 bis 4 Byte) ist im ersten Zeichen einkodiert.
3
* 0xxxxxxx -> 1 Byte
4
* 110xxxxx -> 2 Bytes
5
* 1110xxxx -> 3 Bytes
6
* 11110xxx -> 4 Bytes
7
*
8
* Die clz() instruction benoetigt einen Takt auf dem Cortex M3.
9
*
10
* @param[in] Zeiger auf erstes Byte eines UTF-8-Zeichens
11
* @return Anzahl der Bytes des UTF-8-Zeichens */
12
int_fast8_tutf8bytes(constchar*s0)
13
{
14
uint32_ta=*s0;
15
a<<=24;
16
int_fast8_tr=__builtin_clz(~a);
17
r=r==0?1:r;
18
returnr;
19
}
20
21
22
/** Konvertierung UTF8-Zeichen in Unicode
23
*
24
* @param[in] Zeiger auf erstes Byte eines UTF-8-Zeichens
Ich habe mich dafür entschieden, dass switch-break an dieser Stelle das
am wenigsten häßliche mir bekannte Konstrukt ist. Das ist schon beinahe
goto-esk.
Man könnte den zweiten Teil auch mit einer while-Schleife machen und
neben dem Schleifenzähler auch die Maske mitschieben.
Oder geht es noch besser?
Walter T. schrieb:> Oder geht es noch besser?
Das kommt ganz auf deine Definition von "gut" an. Ich würde bei dem Code
fragen: geht es noch schlechter?
Das ist auf halben Weg zum Obfuscation-C-Contest, ohne jeglichen
Mehrwert durch all die grotesken Konstrukte. Krampfhaftes vermeiden von
if ist weder cool noch hipp noch modern, noch irgendetwas anderes, wenn
if genau das ausdrückt, was es ausdrücken soll.
Oliver
Walter T. schrieb:> wenn ich in eine Schleife oder einen Quelltext-Teil an einer bestimmten> Stelle hereinspringen will, bietet sich switch-case an.
Tut es das?
> switch(1)> {> case 1:
Was zum Geier soll das denn sein?
> Ich habe mich dafür entschieden, dass switch-break an dieser Stelle das> am wenigsten häßliche mir bekannte Konstrukt ist. Das ist schon beinahe> goto-esk.
Man muss sich nicht zwanghaft irgendwelche kranken Konstrukte ausdenken,
nur um auf Biegen und Brechen goto zu vermeiden, weil man halt mal
indoktriniert wurde, dass das die Saat des Bösen sei.
Das hier ist einer der beiden Fälle, die ich kenne, in denen goto
durchaus sinnvoll ist.
Walter T. schrieb:> Die> Abbruchbedingung nur einmal zu prüfen wird wohl nicht hinzubekommen> sein.
Wenn du unbedingt ein switch willst, dann nimm ein switch (nBytes), und
ordne die Fälle absteigend ohne break an (fallthrough). Braucht etwas
Umformulierung der Berechnung, aber das kriegst du hin. Das ist dann
aber immer noch nicht "gut".
Oliver
Rolf M. schrieb:>> switch(1)>> {>> case 1:>> Was zum Geier soll das denn sein?
Verzweifelter Versuch dass er break verwenden kann, damit er kein
"böses" return mitten aus einer Funktion verwenden muss.
Was vermutlich geht (ich habe jetzt keine Lust mir die Masken für r
anzusehen), ist einfach eine Schleife, auch wenn die Berechnung etwas
unsymmetrisch ist
1
int_fast8_t nBytes = utf8bytes(s);
2
uint32_t r = 0;
3
mask1 = ...
4
mask2 = ...
5
6
for(i = 0; i < nBytes; i++) {
7
...
8
}
Nebenbei ist die Bezeichnung der Konvertierung falsch
> /** Konvertierung UTF8-Zeichen in Unicode
UTF-8 ist schon Unicode, im Transformation Format 8. Was er versucht ist
den Unicode Codepoint auszurechnen.
Walter T. schrieb:> So geht es auch:
Hng. Der strunzdumme Compiler merkt nicht, dass die vorherigen
Bedingungen schon enthalten sind und er eigentlich bis zum Ende springen
kann, ohne alles jeweils einzeln vergleichen zu müssen. Also müssen die
if-Anweisungen geschachtelt werden oder doch wieder Goto oder ein
Ersatz.
fizzbuzz schrieb:> if und if-else + andere Reihenfolge.
Stimmt. Eigentlich ist es ja total egal, in welcher Reihenfolge die
Bytes aus dem Array gelesen werden.
Walter T. schrieb:> Hng. Der strunzdumme Compiler merkt nicht, dass die vorherigen> Bedingungen schon enthalten sind und er eigentlich bis zum Ende springen> kann, ohne alles jeweils einzeln vergleichen zu müssen. Also müssen die> if-Anweisungen geschachtelt werden oder doch wieder Goto oder ein> Ersatz.
Ich bin mir nicht sicher was genau du meinst, aber hast du Optimierungen
aktiviert?
mh schrieb:>> Ich bin mir nicht sicher was genau du meinst, aber hast du Optimierungen> aktiviert?
Die folgenden Varianten sind funktionell identisch, weil die Vergleiche
aufsteigend sortiert sind:
1
if(byte0>=0xC0)
2
{
3
...
4
}
5
if(byte0>=0xE0)
6
{
7
...
8
}
9
if(byte0>=0xF0)
10
{
11
...
12
}
13
return
14
15
16
17
if(byte0>=0xC0)
18
{
19
...
20
if(byte0>=0xE0)
21
{
22
...
23
if(byte0>=0xF0)
24
{
25
...
26
}
27
}
28
}
29
return
Tatsächlich wird mit -O1 aber in der ersten Variante der Vergleich
dreimal vorgenommen, wenn er schon beim ersten Mal schiefgegangen ist.
Entsprechend wird dreimal gesprungen.
Walter T. schrieb:> Tatsächlich wird mit -O1 aber in der ersten Variante der Vergleich> dreimal vorgenommen, wenn er schon beim ersten Mal schiefgegangen ist.> Entsprechend wird dreimal gesprungen.
Welcher Compiler?
Sehr merkwürdig. Selbst beim neuesten ARM-GCC auf der höchsten
Optimierungsstufe sind die beiden nicht äquivalent:
https://godbolt.org/z/7M7Y9h89K
Bis hin zu dem Kuriosum, dass die ersten beiden Zeilen unterschiedlich
übersetzt werden, weil ein Block mit Textzeigern über die
130-Bytes-Grenze wandert.
Yalu X. schrieb:> mask <<= 5;
Clever!
> Wie wär's mit einer Schleife statt der If-Kaskade?
Wie gesagt, gut gesehen, gefällt mir aber nicht.
Ich mag es, wenn Code den Fällen der Spezifikation entspricht. Lässt
sich in funktionalen Sprachen sehr schön formulieren (algebraische
Datentypen, strukturelle Zerlegung etc.), ergibt in den alten,
imperativen Sprachen wie C eben if-else-if-else-Kaskaden. Diese Kaskaden
sind aber meiner Meinung nach leicht zu warten und zu verstehen, weil
sie die Fälle aufzählen und dem menschlichen Denken entsprechen.
Aber wie immer, das ist subjektiv, jeder hat da seine eigenen Prägungen.
@Walter:
Warum nicht einfach die Betriebssystem Funktion verwenden?
Bist ja nicht der erste, der das Problem hat...
... und deine C Kenntnisse sind noch nicht so weit, das du
das einfach und effizient lösen kannst.
Die ganze Optimiererei (int_fast8_t???, Assembler anschauen???)
macht doch 0 Sinn, wenn das Klump nicht funktioniert.
Denke nur mal an all die Spezialfälle, die nicht in der Spec stehen,
wenn
sich etwa ein Latin1 Char einschleicht...
Yalu X. schrieb:> Wie wär's mit einer Schleife statt der If-Kaskade?
Die ist es mittlerweile auch geworden.
Aber Respekt: Alle drei Varianten funktionieren.
Walter T. schrieb:> int_fast8_t r = __builtin_clz(~a);
Solche Obfuscationen macht man erst, wenn riesige Datenmengen in kurzer
Zeit zu verarbeiten sind.
Laß das ganze _fast und _builtin Gedöns weg und schreib lesbaren und
funktionierenden Code.
Peter D. schrieb:> Solche Obfuscationen macht man erst, wenn riesige Datenmengen in kurzer> Zeit zu verarbeiten sind.
Was wäre denn Deine nicht-merkwürdige Variante, führende Einsen zu
zählen? While-Schleife und hoffen, dass der Compiler das als Idiom
versteht?
(Bei den fast-Int bin ich nicht Deiner Meinung. An die habe ich mich so
gewöhnt, dass sie für mich mittlerweile "normaler" als "int" und "long"
aussehen.)
Peter D. schrieb:> Laß das ganze _fast und _builtin Gedöns weg und schreib lesbaren und> funktionierenden Code.
int_fast8_t ist ein Standard-typedef. Da ist nichts falsch dran.
Rolf M. schrieb:> Peter D. schrieb:>> Laß das ganze _fast und _builtin Gedöns weg und schreib lesbaren und>> funktionierenden Code.>> int_fast8_t ist ein Standard-typedef. Da ist nichts falsch dran.
Falsch nicht, aber der Nutzen ist sehr begrenzt und häufig ist es
kontraproduktiv. Das Problem ist das "fast". Was genau bedeutet es? In
welchen Situationen ist ein int_fast8_t schneller als ein int8_t? Gibt
es Situationen wo ein int_fast8_t langsamer ist als ein int8_t?
Da die fast-Varianten nur typedefs sind, kann man dem Compiler damit
schonmal ordentlich ausbremsen. Oder haben Compiler mittlerweile extra
Logik für diese Typen integriert? Also weiß der Compiler, dass ein
int_fast8_t einen 8Bit Wertebereich hat, auch wenn es ein typedef auf
einen int32 ist?
Rolf M. schrieb:> goto zu vermeiden, weil man halt mal> indoktriniert wurde, dass das die Saat des Bösen sei.
Hihi, ich hatte mal einen Anruf des Programmierers eines Kunden, der
völlig Entsetzt, mit bebender Stimme sagte: 'Du hast GOTO verwendet ...'
Ja. Stimmt. Der Kandidat hat 100 Punkte.
Ist Teil des C Spachstandards und war an der Stelle die übersichtlichste
Methode aus dem Loop zu entkommen. Für Glaubensfragen bitte an den
Esotheriker des geringsten Mißtrauens wenden.
Den Teil mit Inline ASM und den Jumps auf Sprungmarken fand er okay.
Ist ja ein mega Unterschied zu GOTO ...
Den Teil mit der Pointerarithmetik ohne Bereichsprüfung hat er nicht mal
gecheckt. Ist mir dann noch rechtzeitig aufgefallen.
mh schrieb:> In welchen Situationen ist ein int_fast8_t schneller als ein int8_t?
Kann dir beim ARM schnell passieren, denn da muss u.U. noch zusätzlich
maskiert werden, während der int_fast8_t 32 Bit breit ist und damit die
natürliche Maschinenwortbreite nutzen kann.
Packst du den gleichen Code aber auf einen AVR, kann der Compiler dafür
tatsächlich eine 8-Bit-Einheit nehmen.
Jörg W. schrieb:> mh schrieb:>> In welchen Situationen ist ein int_fast8_t schneller als ein int8_t?>> Kann dir beim ARM schnell passieren, denn da muss u.U. noch zusätzlich> maskiert werden, während der int_fast8_t 32 Bit breit ist und damit die> natürliche Maschinenwortbreite nutzen kann.
Mir ist klar, dass es Situationen gibt, in denen es Vorteile gibt.
Deswegen enthält mein Betrag mehr als die eine Frage. Ist der Vorteil
beim Maskieren größer als der Nachteil beim Kopieren wo 3 unnötige
Bytes angefasst werden müssen? Kann der Compiler nicht vielleicht auch
für den int8_t das 32Bit Register benutzen? Verhindert der int_fast8_t
alias int32 eine Optimierung weil der Compiler nicht mehr sehen kann,
dass der echte Wertebereich nur 8Bit groß ist?
Peter D. schrieb:> Walter T. schrieb:>> int_fast8_t r = __builtin_clz(~a);>> Solche Obfuscationen macht man erst, wenn riesige Datenmengen in kurzer> Zeit zu verarbeiten sind.
Witzig dabei auch, dass __builtin_clz(0) undefiniert ist...
mh schrieb:> Kann der Compiler nicht vielleicht auch für den int8_t das 32Bit> Register benutzen?
Ja, kann er nicht. int8_t sagt "nimm genau 8 Bit". int_fast8_t sagt
"nimm mindestens 8 Bit, gern auch mehr, wenn es das schneller macht".
Der Vollständigkeit halber: int_least8_t sagt: "nimm möglichst 8 Bit;
wenn du das nicht kannst, nimm halt mehr". Der Unterschied zu int8_t
ist, dass eine Maschine, die keine 8-Bit-Einheiten handhaben kann (da
gibt's wohl ein paar DSPs) halt zwar ein int_least8_t implementieren
kann, aber ein int8_t kann sie dann schlicht nicht anbieten. Code, der
so geschrieben ist, dass er per int8_t unbedingt genau 8 Bit haben
möchte, lässt sich dann nicht compilieren.
Interessanter finde ich aber trotzdem die Frage, was an den
__builtin-Funktionen verkehrt ist und was die bessere Alternative ist.
Mein naiver Ansatz war ja: Sie sind schon da. Sie sind dokumentiert.
Also kann man sie benutzen.
Wenn ich für den MSVC austauschbaren Code geschrieben hätte, hätte ich
sie wohl in eine Funktion clz() gewrappt, wohl oder übel eine
Compilerweiche benutzt und den anderen Zweig selbst implementiert. Mit
dem Nebeneffekt, noch einmal einen eigenen Header mehr einfügen zu
müssen.
Walter T. schrieb:> Interessanter finde ich aber trotzdem die Frage, was an den> __builtin-Funktionen verkehrt ist
Nix, wenn dich nicht stört, daß sie nicht portabel und (auch wenn Du
immer und ewig "nur" gcc benutzt) nur in einer hosted-Umgebung zur
Verfügung stehen.
Markus F. schrieb:> Nix, wenn dich nicht stört, daß sie nicht portabel und (auch wenn Du> immer und ewig "nur" gcc benutzt) nur in einer hosted-Umgebung zur> Verfügung stehen.
Kannst Du das näher erläutern? Ich verstehe den Begriff der
"hosted-Umgebung" in diesem Zusammenhang nicht.
Walter T. schrieb:> Ich verstehe den Begriff der "hosted-Umgebung" in diesem Zusammenhang> nicht.
-fhosted (default Option des Compilers)
Es wird eine Standardbibliothek vorausgesetzt, und der Compiler darf
entsprechend der standardmäßig definierten Bedeutung auch
Bibliotheksfunktionen optimieren. Beispielsweise kann er dabei
strlen("hi") durch die Konstante 2 ersetzen. Andererseits sind in diesem
Falle auch Prototypen für main() genormt, das muss entweder
1
intmain(void);
oder
1
intmain(int,char**);
sein. Das im Embedded-Bereich so gern geschriebene
1
voidmain(void);
verletzt theoretisch diese Regel (und gibt entsprechend eine Warnung).
Alternative: "freestanding environment", -ffreestanding
Hier definiert der Standard einen deutlich eingeschränkteren
Funktionsumfang, es ist keine Standardbibliothek vorausgesetzt, main()
darf man beliebig deklarieren (es darf auch ganz fehlen). Aber: es kann
halt auch keine Standardbibliothek optimiert werden.
Daher arbeiten selbst embedded-Systeme sehr oft mit -fhosted.
Jörg W. schrieb:> -fhosted (default Option des Compilers)
Diese Erklärung ist sehr schön, und sollte irgendwo oben angepinnt sein.
Hätte ich sie viel früher gelesen, hätte mir das viel Zeit gespart.
Aber ich finde nirgendwo ein Indiz dafür, dass die buildins in
"freestanding" nicht verfügbar wären.
mh schrieb:> Rolf M. schrieb:>> Peter D. schrieb:>>> Laß das ganze _fast und _builtin Gedöns weg und schreib lesbaren und>>> funktionierenden Code.>>>> int_fast8_t ist ein Standard-typedef. Da ist nichts falsch dran.>> Falsch nicht, aber der Nutzen ist sehr begrenzt und häufig ist es> kontraproduktiv.
Ja? Wann denn?
> Das Problem ist das "fast". Was genau bedeutet es? In welchen Situationen> ist ein int_fast8_t schneller als ein int8_t?
Eine allgemengültige Antwort gibt es dafür nicht. Aber bei manchen
Architekturen sind die Datentypen tendenziell unterschiedlich effizient
im Bezug auf Rechenzeit. Und der kleinste muss nicht gezwungenermaßen
auch der schnellste sein.
> Gibt es Situationen wo ein int_fast8_t langsamer ist als ein int8_t?
Kann es auch geben.
> Da die fast-Varianten nur typedefs sind, kann man dem Compiler damit> schonmal ordentlich ausbremsen.
Das gilt für int8_t genauso.
> Oder haben Compiler mittlerweile extra Logik für diese Typen integriert?
gcc hat das anscheinend aktuell nicht, soweit ich erkennen kann.
mh schrieb:> Mir ist klar, dass es Situationen gibt, in denen es Vorteile gibt.> Deswegen enthält mein Betrag mehr als die eine Frage. Ist der Vorteil> beim Maskieren größer als der Nachteil beim Kopieren wo 3 unnötige> Bytes angefasst werden müssen?
Werden sie sowieso. Das Prozessor-Register ist so oder so 32 Bit breit,
und der Speicher wird oft auch sowieso immer in 32-Bit-Einheiten
angesprochen. Der Unterschied ist dann nur, dass bei einem Kopieren
eines 8-Bit-Werts aus dem Speicher die anderen 32 Bytes weggeworfen
werden, bzw. beim Schreiben gar noch umständlich gelesen, dann das eine
Byte ausgestauscht, dann wieder zurückgeschrieben werden müssen.
> Kann der Compiler nicht vielleicht auch für den int8_t das 32Bit Register> benutzen?
Das muss er sowieso, weil der ARM gar keine 8-Bit-Register hat. Wenn du
aber auf 8 Bit festzurrst, muss er sicherstellen, dass da nachher der
gleiche Wert drin steht, als wäre das Register 8 Bit breit gewesen. Und
das kann zusätzlichen Aufwand bedeuten.
> Verhindert der int_fast8_t alias int32 eine Optimierung weil der Compiler> nicht mehr sehen kann, dass der echte Wertebereich nur 8Bit groß ist?
Nein.
Interessant. Das Schweigen der Experten sagt mehr als viele Worte
anderer.
Zu "__builtin -- lieber nicht" gibt es von denjenigen in diesem Thread,
von deren Kompetenz ich überzeugt bin, weder Zustimmung und Begründung
noch Ablehnung.
Es scheint also ein vages Unwohlsein zu erzeugen, auf dessen Quelle man
nicht genau mit dem Finger zeigen kann.
Walter T. schrieb:> Interessanter finde ich aber trotzdem die Frage, was an den> __builtin-Funktionen verkehrt ist und was die bessere Alternative ist.>> Mein naiver Ansatz war ja: Sie sind schon da. Sie sind dokumentiert.> Also kann man sie benutzen.
Du kannst sie ja auch benutzen. Aber erst am Ende, wenn Messungen einen
Bottleneck ergeben und es für diese Plattform wichtig ist. .
Im Ursprungscode sieht es hingegen fast so aus, als seien die Funktionen
um das clz herum gebaut worden. Natürlich ist keines der Codebeispiele
hier wirklich sinnvoll. Doch fällt gerade bei solchen Aufgaben der Code
am Ende in sich zusammen, wenn man ihn portabel formuliert und
verbessert. Nicht, wenn man ihn um eine fancy Funktion herum baut.
A. S. schrieb:> Im Ursprungscode sieht es hingegen fast so aus, als seien die Funktionen> um das clz herum gebaut worden.
Wenn der einschlägige Teil des Standards, den man vorliegen hat,
aussagt: "Die Anzahl der Einsen/die Stellung der ersten Null am Anfang
des ersten Bytes bestimmt, was dann gemacht wird" (Wikipedia).
Mein erster Gedanke: "OK, ich muss Einsen zählen. Moment mal. Für den
Zweierlogarithmus hatte ich eine nette Funktion, die Nullen zählt,
also..."
Wie würdest Du anfangen? Was machst Du ohne Bottleneck?
Das Fragen mag penetrant sein. Aber ich will verstehen, was für einen
Softwareentwickler "normal" ist. Im Internet findet man wenig
"normales". Man findet hauptsächlich laute Minderheiten. Ich meine: Wenn
wir unsere sexuelle Früherziehung im Internet genossen und uns daran
orientiert hätten, was "normal" ist...
Ich habe im Alltag keinen Kontakt zu Softwareentwicklern.
Walter T. schrieb:> Wenn der einschlägige Teil des Standards, den man vorliegen hat,> aussagt: "Die Anzahl der Einsen/die Stellung der ersten Null am Anfang> des ersten Bytes bestimmt, was dann gemacht wird" (Wikipedia).
Das sagt der Standard eben nicht, bzw nur mit Einschränkungen.
Ohne sich mit dem Unicode Standard auseinanderzusetzen, kommen
da nur halbgare Sachen raus, allzu oft auch Sicherheitslücken
(Stichwort Überlange UTF-8 Sequenzen, Codepoint <= 0x10ffff,
Ungültige Zeichenbereiche, CESU-8). Abgesehen vom Standard hast du
noch das Problem, dass es auch reichlich ungültiges UTF-8 gibt,
mit dem man aber in der Praxis umgehen muss.
Aber was willst du denn eigentlich machen?
Das ist doch erst mal die entscheidende Frage, die du nicht
beantwortest (so wie 90% der TE hier).
Ich habe so den Eindruck, dass du einfach jemanden zum Quatschen
brauchst, auch ok.
In der Zeit, die du hier verplemperst, hättest du dir auch
einige UTF-8 Libs auf github anschauen können, oder
in der Doku deiner uns unbekannten Entwicklungsumgebung suchen
können.
Optimieren ohne Ziel, und ohne Performancemessung ist doch reine
Zeitverschwendung. Wahrscheinlich liest deine Anwendung
gerade mal 100 Config Bytes, und das geht halt dann vielleicht
in 20 us statt in 30 us.
Aber schon dein Ansatz, einzelne Zeichen anzuschauen verhindert es,
das da irgendwas mit guter Performance rauskommt.
Du musst blockweise arbeiten, wenn es schnell gehen soll.
Ob du int8_t oder int verwendest, geht da im Rauschen unter,
aber alles kleiner als int wird in C erst mal auf int konvertiert.
Der Profi widmet dem Testen und der Behandlung von Fehlerzuständen
eher mehr Zeit, als der Implementierung der Grundfunktion.
Dann kommt erst die Optimierung und auch nur wenn es notwendig ist,
oder wenn Zeit ist. Bei dir ist es umgekehrt.
Als Referenz: die ziemlich gut optimierte Win32 MultiByteToWideChar
Funktion schafft 1.2 Zeichen / Takt für Ascii Zeichen,
und ca. 0.5 Zeichen / Takt für komlexere Zeichen (ohne SSE
Erweiterungen).
Eine moderne SSE Funktion schafft über 4 Zeichen / Takt.
Aber brauchst du wirklich UTF-8? Am Mikrocontroller?
Nimm doch einfach ASCII oder Latin1, und gut ist es.
Schau mal, das deine Anwendung läuft, und wenn du dann Chinesische
Zeichen brauchst, ist UTf-8 vielleicht gar nicht die beste Wahl.
udok schrieb:> Optimieren ohne Ziel, und ohne Performancemessung ist doch reine> Zeitverschwendung.
Dazu gerne in einem anderen Thread, den ich soeben eröffnet habe.
udok schrieb:> Aber was willst du denn eigentlich machen?
Ich dachte, das sei durch die umfangreiche Kommentierung des Quelltextes
im Eröffnungsbeitrag geklärt. Ich brauchte einen UTF-Dekoder. Performanz
ist auch nicht ganz nebensächlich, weil da wirklich jede einzelne
Zeichenkette durchgeht, die auf dem Display erscheint. Mit chinesischen
Zeichen habe ich es nicht. Aber mit dem griechischen Alphabet und ein
paar anderen Formelzeichen.
Aber das läuft ja alles seit Samstag.
Walter T. schrieb:> Aber ich finde nirgendwo ein Indiz dafür, dass die buildins in> "freestanding" nicht verfügbar wären.
Ich vermute, dass sie da auch verfügbar sind, aber wie heißt es so
schön? "If all else fails, read the documentation."
Die Doku schweigt sich dazu aus. Ich nehme an, es handelt sich um ein
beredtes Schweigen.
Aber welcher Frage ich immer noch kein Stück näher gekommen bin: Warum
wird hier sofort "premature optimization" geschriehen, sobald ein paar
Unterstriche vorkommen?
Walter T. schrieb:> Mein erster Gedanke: "OK, ich muss Einsen zählen. Moment mal. Für den> Zweierlogarithmus hatte ich eine nette Funktion, die Nullen zählt, also.
Vorzeitige Optimierung ist übel.
Solange Du die Aufgabe nicht durchdrungen hast, kann eine Optimierung
keinen Sinn machen.
Das ist so, als würde man ein Schema suchen, obwohl man erst 5 der 8
Sonderfälle gelesen hat. Wertlos, wenn man alle 8 implementieren will,
weil es auch einfacher werden kann.
Wenn es nur 5 pattern gibt, ist Plain Code kein schlechter start, also 5
ifs hier z.b.
Walter T. schrieb:> also leichter zu lesen als
Die beiden Schnipsel haben miteinander nichts zu tun. Ob Du hexzahlen
lesen kannst oder bitmuster (xxx_____ oder b11100000) nutzt, ist eine
Sache.
Ob Du die Bestimmung der Einsen rausziehst und dafür eine (gedankliche)
Übersetzung mehr hast, das andere.
Die wichtigen Fragen sind doch ganz andere: welchen Umfang willst Du
dekodieren? Wie gehst Du mit Zeichen außerhalb um, wie mit fehlerhaften
oder abgebrochenen (passiert sehr schnell mit strncpy + 0). Was ist als
Funktion(sset) gut nutzbar (deine erfordert drumherum nochmal den
gleichen Overhead)
Bis dahin habe ich nichts dagegen, dass auch zu programmieren (um es zu
testen, zu verstehen) aber nicht Tricky in Sütterlin auf Stein
gemeißelt, sondern lesbar und einfach.
Walter T. schrieb:> Was wäre denn Deine nicht-merkwürdige Variante, führende Einsen zu> zählen? While-Schleife und hoffen, dass der Compiler das als Idiom> versteht?
Nun, erstmal kann nicht jede Maschine ein __builtin_clz und 2. mußte ich
erstmal nachsehen, was das überhaupt macht. Und 3. ist Dein Code sowas
von hinten durch die Brust ins Auge, nur um es benutzen zu können. Ich
hab da ewig gebraucht, um durchzusehen.
Ich würde solche einfachen Sachen direkt hinschreiben, ohne komplizierte
Konstrukte:
1
/** UTF8-Zeichenlaenge anhand des ersten Bytes ermitteln
2
* Die Laenge eines UTF-8-Zeichens (1 bis 4 Byte) ist im ersten Zeichen einkodiert.
Peter D. schrieb:> case 0xC0:> case 0xD0: return 2;
Okay, wir haben wirklich sehr unterschiedliche Denkweisen, was als
"naheliegend" zu bezeichnen ist.
Insofern macht das die Diskussion über den ursprünglichen Anwendungsfall
hinaus interessant.
Peter D. schrieb:> Ich würde solche einfachen Sachen direkt hinschreiben, ohne komplizierte> Konstrukte:
Ich hab das oben ja genauso gemacht, nur ist das halt Tricky und nicht
wirklich das gewünschte. Du kannst 4 nicht von 5 einen unterscheiden und
bei Default fallen auch alle Fehler rein.
Walter T. schrieb:> Okay, wir haben wirklich sehr unterschiedliche Denkweisen, was als> "naheliegend" zu bezeichnen ist.
Naja, wer das hier schreibt:
1
switch(1)
2
{
3
case1:
beurteilt "andere" Denkweisen?
Kennst Du den uralten Witz mit Bart:
> Verkehrsfunk: "Achtung: Ein Falschfahrer auf der Autobahn!"> Autofahrer: "Ein Falschfahrer? Hunderte!"
Der Teil mit dem Heraussprung war ja auch der Teil, mit dem ich von
Anfang an unzufrieden war - Anlass für diesen Thread. Der Teil mit den
Einsen zählen normal. Bitte nicht mischen!