Hallo,
ich möchte in dieser Funktion gern prüfen ob der übergebene Wert einem
bekannten Kommando entspricht.
Diese sind in 'commands' abgelegt, einem Array aus Pointern auf
char-Arrays.
1
intcommandindex(char*input)// gibt Integerwert für command zurück, 0 wenn nicht gefunden
Da sich die Anzahl der Kommandos ändern kann (im Original ca. 60) möchte
ich sie nicht fest eintragen sondern berechnen (Speicher und Zeit sind
kein Problem).
Soweit funktioniert auch alles, damit das aber sowohl auf einem PC
(Zeiger sind 4 Byte gross) als auch im Atmel Studio für 8 Bit AVR(Zeiger
sind 2 Byte gross) klappt bin ich bisher auf keine bessere Lösung
gekommen als die Größe des Arrays für die Schleife so zu berechnen:
(sizeof commands)/(sizeof &commands[0])
Das sieht nicht elegant aus, gibt es da eine bessere Lösung?
Generell bin ich auch für Tips dankbar wie man die Funktion anders
realisieren kann, lerne da gern was dazu.
Lutz S. schrieb:> if (strcmp(input,commands[i])==0) r = i;
im Prinzip kannst hier dann mit "break;" die for-Schleife abbrechen. Du
hast ja einen Treffer gefunden. Alle weiteren Iterationen sind doch
nicht mehr nötig oder?
Und wenn du die Größe von deinem Array nicht berechnen willst, dann
kannst du ja das Ende vom Array irgendwie markieren ("end").
Und solange durch Array iterieren bis du einen Treffer oder eben das
markierte Ende erreicht hast.
Und wenn du die Daten noch sortierst (oder in sortierter Reihenfolge
selbst einträgst), dann kannst du schneller suchen.
leo schrieb:> Du kannst dir auch bsearch(3) anschauen.
Lutz S. schrieb:> // gibt Integerwert für command zurück, 0 wenn nicht gefunden
Du kannst auch r+1 zurück geben, dann sparst du das "" oder nimmst -1
für nicht gefunden
>> Das sieht nicht elegant aus,>> ... und ist auch flasch.>> (sizeof commands)/(sizeof commands[0])
..zweckmäßigerweise baut mach sich dafür dann auch noch gleich ein
#define wie z.B. ARRAY_SIZE, dann gibts weniger Tippfehler und die
Semantik wir auch gleich klar.
Lutz S. schrieb:> Das sieht nicht elegant aus
Stimmt zwar, hat aber den Vorteil, dass es schon zur Kompilierzeit
berechnet werden kann.
leo schrieb:> ... und ist auch flasch.
Macht in dem Fall aber keinen Unterschied, da man in beiden Fällen die
Größe eines Zeigers bekommt. Genausogut könnte man auch sizeof(char*)
schreiben.
Gruß,
Bernd
zitter_ned_aso schrieb:> der leere String am Anfang wird ja nicht benutzt. Vielleicht wäre das> ein möglicher Indikator für's Arrayende.
Dann müsste man ein strlen() machen. Ein NULL ist da eindeutiger
(ähnlich dem '\0' beim String)
Bernd B. schrieb:> Genausogut könnte man auch sizeof(char*)
wenn man (sizeof arrayname)/(sizeof arrayname[0])
oder (sizeof arrayname)/(sizeof *arrayname)
nimmt, braucht man sich keine Gedanken um den Typ machen.
Egal ob int, char, char* oder struct. Das passt immer.
Die Längenberechnung mit sizeof funktioniert nur, wenn der
Array-Bezeichner noch nicht zur einem Zeigertyp zerfallen (decay-ed)
ist. Daher ist diese Längenberechnung etwas "wackeling" ...
Besser mit Sentinel arbeiten. Etwa:
Eigentlich könntest du doch deine Befehle durchnummerieren und per
ID-Nummer identifizieren. Ganz ohne String und strcmp(...).
Nur C (und wahrscheinlich kritikwürdig ;-)
zitter_ned_aso schrieb:> Eigentlich könntest du doch deine Befehle durchnummerieren und per> ID-Nummer identifizieren. Ganz ohne String und strcmp(...).
Ich denke, er will die commands aus den Strings (UI) ableiten.
zitter_ned_aso schrieb:> command=42;
So ein Schwachsinn kann man wohl nur in C prodizieren ...
Wilhelm M. schrieb:> Ich denke, er will die commands aus den Strings (UI) ableiten.
Ja, so ist es.
Aber das Problem interessiert mich auch ganz allgemein. So eine Liste
mit Zeigern auf Strings ist ja für vieles praktisch und dass die
Bestimmung der Anzahl der Elemente da gar nicht so einfach ist hatte
mich erstaunt.
Ich habe jetzt verschiedene Anregungen umgesetzt, danke an alle. Die
Berechnung der Anzahl der Elemente ist etwas geändert und die Auswertung
bricht nach dem Treffer ab. Ausserdem entfällt die Hilfsvariable r:
1
intcommandindex(char*input)// gibt Integerwert für command zurück, 0 wenn nicht gefunden
Lutz S. schrieb:> Ja, so ist es.>> Aber das Problem interessiert mich auch ganz allgemein. So eine Liste> mit Zeigern auf Strings ist ja für vieles praktisch und dass die> Bestimmung der Anzahl der Elemente da gar nicht so einfach ist hatte> mich erstaunt.
Zitat: B. Stroustrup:
"Rohe C-Arrays sind so dumm, dass sie ihre eigene Länge nicht wissen."
Folgendes Beispiel: nehemn wir an, Du möchtests das strcmp() auf eine
Menge von String anwenden, wie in Deinem Problem. Du schreibst Dir dafür
ein Funktion find() (s.u.). Find arbeitet hier mit einem Wächterelement
(sentinel), daher raucht find nicht explizit die Länge des Arrays
wissen, sondern der Abbruch der Schleifer ergibt sich in der schleife
aus dem letzten Element. Das geht natürlich nur für DT, die einen
ungültigen Wert in ihrem Wertebereich haben, etwa die Zeigertypen
haben ja 0 dazu.
Die andere Variante find_wrong() wendet eine falsch (hier bekommt man
übrigend auch eine Warnung vom Compiler) Berechnung der Arraygröße an.
Den bei der Parameterübergabe an find_wrong() zerfällt der
Arraybereichner commands zu einem Zeiger. Daher wird bei sizeof(array)
die größe eines Zeigers bestimmt. sizeof(array[0]) berechnet auch die
Größe eines Zeigers (const char*). Daher ist der Quotient immer 1.
Vielleicht hast Du nun meine Bemerkung von oben verstanden.
Es gibt viele Lösungen aus diesem Dilemma:
1) Vermeide C (geht wohl bei Dir nicht, da Du AVR im Auge hast)
2) sentinel benutzen
3) zu jedem Array die Länge übergeben (zwei Parameter der Funktion
daraus machen.
4) Ein struct benutzen
5) ...
Wächterelement (sentinel) geht natürlich nur bei DT, die das zulassen
(s.o.).
BTW: das ist der Grund, warum die andere Signatur von main(int, const
char**) so aussieht.
Danke für deine Erläuterungen, Wilhelm.
Ja, in dem Fall möchte ich wirklich C verwenden. Das Programm läuft auf
einem kleinen ATTiny. Auf dem PC teste ich gern mal kleinere Schnipsel
dazu.
Hier noch eine Variante ohne Größenbestimmung, mit Sentinel. Diesmal mit
do while:
1
intcommandindex(char*input)// gibt Integerwert für command zurück, 0 wenn nicht gefunden
Lutz S. schrieb:> Ja, in dem Fall möchte ich wirklich C verwenden. Das Programm läuft auf> einem kleinen ATTiny. Auf dem PC teste ich gern mal kleinere Schnipsel> dazu.
Gut, dann packe die Stringkonstanten ins PROGMEM, sonst hast Du bald
kein RAM mehr.
> Hier noch eine Variante ohne Größenbestimmung, mit Sentinel. Diesmal mit> do while:>>
1
>intcommandindex(char*input)// gibt Integerwert für command zurück, 0
Das ist eine Verschlimmbesserung, weil:
1) für das Sentinel mehr Platz (ein ganzer String) verbraucht wird als
nötig
2) je Iteration 2x strcmp()
3) nicht idiomatisch C.
4) im eigentlichen Sinne ist das kein Wächterlement.
Das war mehr so eine Fingerübung auf dem PC. Im AVR würde ich die
Variante oben mit der for-Schleife und der Berechnung nehmen.
Aber deine Anmerkungen sind natürlich richtig.
Lutz S. schrieb:> Im AVR würde ich die> Variante oben mit der for-Schleife und der Berechnung nehmen.
Ich hoffe, Du hast verstanden, warum das ggf. ein Problem darstellt
(s.o.).
Meinst du das?
'Die Längenberechnung mit sizeof funktioniert nur, wenn der
Array-Bezeichner noch nicht zur einem Zeigertyp zerfallen (decay-ed)
ist. Daher ist diese Längenberechnung etwas "wackeling" ...'
D.h. so lange ich commands nicht an eine Funktion übergebe besteht kein
Problem durch die Berechnung?
Die letzte Variante habe ich jetzt noch einmal überarbeitet:
1
intcommandindex(char*input)// gibt Integerwert für command zurück, 0 wenn nicht gefunden
Lutz S. schrieb:> D.h. so lange ich commands nicht an eine Funktion übergebe besteht kein> Problem durch die Berechnung?
Ja.
> Die letzte Variante habe ich jetzt noch einmal überarbeitet:>>
1
>intcommandindex(char*input)// gibt Integerwert für command zurück, 0
Lutz S. schrieb:> Wo kann man solche Konventionen nachlesen?
Eigentlich im K&R.
Dort werden die Ideen aus erster Hand vermittelt.
Da 0 ein gültiger Index für ein Array ist, macht es Aufwand diese zu
umgehen.
Da muss ich meine Nase mal wieder in K&R stecken.
Steht noch hier, kam mal 64 DM ;-)
... und wurde in dem Fall auch gleich fündig, Seite 68: 'Da in C
Vektoren bei Position 0 beginnen sind Indexwerte 0 oder positiv und ein
negativer Wert wie -1 ist praktisch um Mißerfolg anzuzeigen.'
Lutz S. schrieb:> Da muss ich meine Nase mal wieder in K&R stecken.>> Steht noch hier, kam mal 64 DM ;-)> ... und wurde in dem Fall auch gleich fündig, Seite 68: 'Da in C> Vektoren bei Position 0 beginnen sind Indexwerte 0 oder positiv und ein> negativer Wert wie -1 ist praktisch um Mißerfolg anzuzeigen.'
s.a. POSIX-API
Nebenan sprichst du dich doch genau dafür aus.
Lutz S. schrieb:> D.h. so lange ich commands nicht an eine Funktion übergebe besteht kein> Problem durch die Berechnung?
Es besteht kein Problem, solange du die Längenberechnung auf das Array
selbst anwendest und nicht auf einen Zeiger. Bei Übergabe an eine
Funktion wird automatisch ein Zeiger draus, daher funktioniert es
innerhalb der Funktion nicht.
Rolf M. schrieb:> Wilhelm M. schrieb:>> ssize_t find(const char* const* it, const char* pattern) {>> Wenn du hier const so intensiv einsetzt, warum dann nicht konsequent?>>
>> Nebenan sprichst du dich doch genau dafür aus.
Ja, absolut ...
> Lutz S. schrieb:>> D.h. so lange ich commands nicht an eine Funktion übergebe besteht kein>> Problem durch die Berechnung?>> Es besteht kein Problem, solange du die Längenberechnung auf das Array> selbst anwendest und nicht auf einen Zeiger. Bei Übergabe an eine> Funktion wird automatisch ein Zeiger draus, daher funktioniert es> innerhalb der Funktion nicht.
Richtig.
Allerdings ist die Anzahl der Beiträge hier sehr hoch, wo das irgendwann
mal zum Problem wird. Den oft fehlt das Verständnis dafür, was der
Unterscheid zwischen einem Array-Bezeichner und einem Zeiger ist bzw.
wann dieser Zerfall stattfindet. Aus diesem Grunde halte ich es für
sinnvoll, diese wackelige Konstrukt ganz zu vermeiden.
BTW: der einzige Weg, das Zerfallen zu verhindern, ist mit templates zu
arbeiten. Denn hier kann der Compiler die Länge als NTTP ableiten.
Wilhelm M. schrieb:>> ssize_t find(const char* const* const it, const char* const pattern) {>> >>> Nebenan sprichst du dich doch genau dafür aus.>> Ja, absolut ...
Große Unterlassungssünde meinerseits!
Bei den anderen Funktionen hatte ich das ja auch gemacht ...
Du solltest auf eine kopfgesteuerte Schleife ausweichen, die
funktioniert noch, auch wenn die Liste leer ist.
Hier sieht man es sofort, da die Liste direkt darüber definiert ist.
Aber so eine Funktion ist ja ganz praktisch.
Die kann man universeller machen, indem man (den Zeiger auf) die Liste
mit übergibt.
Ja, das Programm für das ich die Funktion geschrieben habe will die
Kommandos von 1 bis x indiziert. 0 war ja für 'nicht gefunden'
vorgesehen.
Falls man das von 0 will muss der Post-Inkrement weg und i eine Zeile
tiefer erhöht werden.
Aber ansonsten gefällt mir das mittlerweile gut, viel gelernt von euch.
Wilhelm M. schrieb:> Stand schon da:
Ja. Aber Lutz hatte die do-while Variante gewählt (gezeigt).
Ein Vorteil der for bzw. while-Schleife wurde dort auch nicht genannt.
Besonders an Wilhelm noch einmal vielen Dank für die geduldigen
Erläuterungen.
Mir ist erst jetzt bei nochmaliger Durchsicht aufgegangen warum die
Schleife
for(size_t i = 0; it[i]; ++i)
am Ende der Liste terminiert.