Wieso kommen da Zahlen in einem Array wo die nix zusuchen haben?
-
Servus ihr lieben ich habe folgendes problem. Ich habe ein problem das mir die buchstaben eines Streams einliest und dann die anzahl in einem array abspeichert. Der Code sieht folgendermaßen aus:
#include <stdio.h> int main() { char words[100]; int x; int a[26]; char z; int y; fflush(stdin); fgets(words,100, stdin); for (x=0;x<26;x++) a[x]=0; for (x=0; x<100; x++) { for (y=97;y<123;y++) { z=y; if (words[x]==z) { a[y-97]++; } } } for (x=0;x<26;x++) printf(" %d",a[x]); return 0; }
Es klappt eigentlich perfekt nur manchmal werden die arrayfelder trotz initialisierung mit 0 mit irgendwelchen zahlen besetzt bsp.
Eingabe: abcdefghijklmnopqrstuvwxyz
Ausgabe: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 2 1 1 1 1 1 1 1
mfg hicham
-
hicham schrieb:
Auch wirklich alle von 0 - 99?
-
Servus,
danke erstmal für die erste Antwort
also nein der array der die Anzahl der Buchstaben initialisiert ist folgender:
for (x=0;x<26;x++) a[x]=0;
Diese for-Anweisung durchläuft die Eingabe, die ja maximal 100 zeichen enthalten kann:
for (x=0; x<100; x++)
Mit dieser schleife wird dann sozusagen der Code buchstabe für buchstabe geprüft und in dem array um 1 inkrementiert:
for (y=97;y<123;y++) { z=y; if (words[x]==z) { a[y-97]++; } }
Das ist dann die Ausgabe:
for (x=0;x<26;x++) printf(" %d",a[x]);
Es ist echt komisch er zählt die buchstaben speichhert sie auch richtig ab das programm funktioniert aber diese zusätzlichen Zahlen in den arrays ist ein Mysterium. Wenn ich Zeile: 24 - 30 auskommentiere, ist alles dann mit 0 initialisiert.
Wenn ich das wieder reinmache dann initiallisiert er wenn ich nix eingebe alles null bis auf ein paar array-felder immer zufallszahle, mal 3 mal 2 mal 5.
Das zählen jedoch an sich funktioniert.ich hab kopfschmerzen! Bitte um hilfe!
cu
-
Belli schrieb:
hicham schrieb:
Auch wirklich alle von 0 - 99?
Ich habe halt keine ahnung bin noch so in den anfängen
-
Wenn du weniger als 100 Zeichen eingibst, dann hat dein words halt nur n Elemente belegt, das [n + 1] Element ist ein 0-Byte und der Rest deines Feldes ist undefiniert.
(n sei die Länge der eingegebenen Zeichenkette)
Da du aber nicht nur bis n zählst in deiner Schleife, sondern konstant bis 100,
zählst du auch in undefinierten Müll am Ende mit, welcher eben zufällig auch gültige Buchstaben enthalten kann.Im Übrigen ist die maximale Anzahl an Zeichen 99, da am Ende deines Puffers noch ein 0-Byte angebracht wird, um das Ende der Zeichenkette zu signalisieren.
Darum kümmert sich fgets automatisch (wenn du mehr eingibst, wird der Rest abgehackt).
Also brauchst du effektiv auch nicht von 0 - 99 zählen, sondern nur von 0 - 98,
da das letzte Byte niemals ein Zeichen zwischen 'a' und 'z' sein kann.Aber wie gesagt, zähl nur bis zum Ende der Zeichenkette und deine komischen Zahlen sollten verschwinden.
Strichwort: entweder strlen oder du bastelst dir selber einen optimierten for-loop (effizienter als erst die Länge zu ermitteln)
Ein Beispiel für den for-loop:
char buff[100]; int counter[26] = { 0 }; // mit 0en initialisieren... spart deinen zusätzlichen loop char * p; // wird zum iterieren benutzt... man könnte zwar auch einen int als index verwenden, das dürfte aber ineffizienter sein :/ // buff füllen... (code kannst du ja selber einfügen) for (p = buff; *p; ++p) // also so lange p nicht auf ein 0-Byte zeigt durchzählen { // da fgets ja ein 0-Byte ans Ende packt, sollten wir keinen Buffer-Overflow bekommen if (*p >= 'a' && *p <= 'z') // schauen, ob *p überhaupt eines der gesuchten Zeichen ist, dein loop ist da auch überflüssig ++(counter[*p - 'a']); // hochzählen }
Ich hoffe, meine Erklärungen waren verständlich
EDIT: wegen fflush(stdin) würde ich dir noch diesen Post nahelegen:
http://www.c-plusplus.net/forum/39349-full
-
Servus,
vielen vieln dank. Super erklärt ++ Code =). Das Gute ist das Programm macht jetzt genau was es machen soll
ich poste mal den Lösungsweg.
Jetzt habe ich aber dazu noch ein paar Fragen wie gesagt ich bin ein totaler Neuling. Ich versteh den GARNET
Du deklarierst ein zeiger char *P und dann benutzt du ne vorschleife wo du p=buff setzt? Bedienung ist der zeiger selbst? *p
Ich bin irgendwie total durcheinanderdie If-SChleife kann ich nachvollziehen echt schlau gelöst wusste net das er auch buchstaben runterzählen kann
Das versteh ich widerum nicht? das hat sicherlich alles mit diesem zeiger zutun...
++(counter[*p - 'a']); // hochzählen
Lösungsweg:
#include <stdio.h> int main() { char buff[100]; int counter[26] = { 0 }; // mit 0en initialisieren... spart deinen zusätzlichen loop char * p; // wird zum iterieren benutzt... man könnte zwar auch einen int als index verwenden, das dürfte aber ineffizienter sein :/ // buff füllen... (code kannst du ja selber einfügen) fgets(buff,100, stdin); for (p = buff; *p; ++p) // also so lange p nicht auf ein 0-Byte zeigt durchzählen { // da fgets ja ein 0-Byte ans Ende packt, sollten wir keinen Buffer-Overflow bekommen if (*p >= 'a' && *p <= 'z') // schauen, ob *p überhaupt eines der gesuchten Zeichen ist, dein loop ist da auch überflüssig ++(counter[*p - 'a']); // hochzählen } for (x=0;x<26;x++) printf(" %d",counter[x]); return 0; }
-
Mit dem * davor, greifst du beim Zeiger auf den Inhalt der Adress zu. Du bekommst also den Wert der Variablen, auf die der Zeiger zeigt.
Bei diesem Beispiel mit *p, wäre das zunächst mal buff[0].Bei dem if wird also der Inhalt geprüft.
Ungleich 0 ist war, 0 ist unwahr.
*p ist in diesem Fall also ein Kurzschreibweise für*p != 0
Mit dem ++p wird der Zeiger erhöht, so dass er auf das nächste Element verweist.Der Variablentyp char ist auch nur ein Ganzzahl-Typ (mit geringem Wertebereich)
Zeichen, also die in einfachen Hochkommas ' sind demnach auch Ganzzahlen.
Nicht zu verwechseln mit char-Arrays.
Der Trick an der Schreibweise ist auch, dass du dich nicht mehr um die Zeichencodierung kümmern musst.So, komen wir zu
++(counter[*p - 'a']); // hochzählen
Da steht zunächst mal
*p - 'a'
:
Wenn *p auf ein 'a' verweist, dann steht da 'a' - 'a' (bei ASCII 97 - 97) und das ist 0.
Dann hast du da noch dein Array counter.
Du greifst dann auf das Element 0 zu, (das Ergebnis zwischen den [] )
~~Und da ist noch das increment ++. Auch nicht anders als bei ++i.
Hier halt auf ein Arrayelement: ++counter[0].~~Kennst du schon.
Bei *p == 'b' bekommst du halt 1 statt 0 .Versuch doch mal das Programm in die Arrayschreibweise umzubauen.
Also ohne p.
-
char * p; // wird zum iterieren benutzt... man könnte zwar auch einen int als index verwenden, das dürfte aber ineffizienter sein :/
Das ist sehr zweifelhaft und irreführend für einen Anfänger
for (p = buff; *p; ++p) // also so lange p nicht auf ein 0-Byte zeigt anfängergerechter ist hier natürlich einfach int i; for( i=0; buff[i]!='\0' ; ++i )
if (*p >= 'a' && *p <= 'z') // schauen, ob *p überhaupt eines der gesuchten
Das ist unportabel, ein Anfänger prägt sich sowas Unportables ein was beim Lernen (und Lehren) nie zielführend sein kann.
int main() { char buff[100],*abc="abcdefghijklmnopqrstuvwxyz"; int x,counter[26] = { 0 }; /*mit 0en initialisieren... spart deinen zusätzlichen loop*/ fgets(buff,100, stdin); /* max. 99 Zeichen von Standardeingabe lesen */ for( x=0;buff[x]!='\0';++x ) { if( strchr(abc,buff[x])!=NULL ) ++counter[strchr(abc,buff[x])-abc]; /* Index für counter berechnet durch Position des aktuellen Zeichens im Stringliteral abc */ } for (x=0;x<26;x++) printf("%02d",counter[x]); return 0; }
-
Wutz schrieb:
char * p; // wird zum iterieren benutzt... man könnte zwar auch einen int als index verwenden, das dürfte aber ineffizienter sein :/
Das ist sehr zweifelhaft und irreführend für einen Anfänger
Nicht wirklich.
Index wird zwar, wie der Zeiger auch, nur inkrementiert.
Um allerdings auf das einzelne Zeichen zuzugreifen, benötigt der Zeiger lediglich eine Dereferenzierung.
Beim Index wird intern effektiv das gemacht:// foo ist ein Array beliebigen Types // n ist ein Index foo[n] == *(foo + n)
Man hat also statt einer einfachen Dereferenzierung immer eine Addition und eine Dereferenzierung.
Möglicherweise kann ein guter Compiler da was optimieren, aber ich würde mich da nicht zu sehr darauf verlassen.Wutz schrieb:
if (*p >= 'a' && *p <= 'z') // schauen, ob *p überhaupt eines der gesuchten
Das ist unportabel, ein Anfänger prägt sich sowas Unportables ein was beim Lernen (und Lehren) nie zielführend sein kann.
1. Was, bitte, ist daran unportabel?
2. Inwiefern istfor (y=97;y<123;y++) { z=y; if (words[x]==z) { a[y-97]++; } }
portabler als mein Code?
3. Wie sieht dein Verbesserungsvorschlag aus? Den kann ich nirgends finden
-
Servus nochmal echt nice danke nochmal für eure Hilfe ich hab hier was gelernt
hier ist der alternativ-code mit array:
#include <stdio.h> int main() { char buff[1000]; int counter[26] = { 0 }; int x=0; fgets(buff,1000, stdin); for (x=0;buff[x]!=0;x++) { if (buff[x] >= 'a' && buff[x] <= 'z')
}for (x=0;x<26;x++)
printf(" %d",counter[x]);return 0;
}
-
if( strchr(abc,buff[x])!=NULL ) ++counter[strchr(abc,buff[x])-abc]; /* Index für counter berechnet durch Position des aktuellen Zeichens im Stringliteral abc */
Das ist ja interessant, da kann ich sogar gezielt nach zeichen suchen lassen
Was ich jedoch nicht verstehe warum ist der andere Code unportable ?? Man benutzt doch keine spezifischen funktionen oder?
ich glaub ich melde mich mal im forum an damit ich wenigstens meine beiträge editieren kann =)))
wie markiere ich ein Thema als GELÖST? oder macht ihr das?
-
Hicham schrieb:
Was ich jedoch nicht verstehe warum ist der andere Code unportable ?? Man benutzt doch keine spezifischen funktionen oder?
In dem gezeigten Code wurde ausgenutzt, dass die Buchstaben in der Zeichentabelle direkt hintereinander und in alphabetischer Reihenfolge stehen. Dies ist keine Garantie des C-Standards (der garantiert es nur für die Ziffern). Dies ist zwar in einem sehr populären und verbreteten Zeichensatz (ASCII und alle die darauf basieren) der Fall, jedoch gibt es auch (eher historische, heuztutage ist eigentlich alles ASCII oder Unicode) echte Gegenbeispiele, z.B. EBCDIC.
Außerdem wäre es lehrreicher, wenn man direkt so programmiert, dass man beliebige Zeichen aus einer vorgegebenen Liste zählen kann, ohne sich durch solche Tricks auf das lateinische Alphabet zu beschränken.
wie markiere ich ein Thema als GELÖST? oder macht ihr das?
Das kannst du als Unregistrierter nicht (du müsstest die Überschrift des ersten Beitrags ändern), aber das ist auch gar nicht nötig. Die Leute lesen in der Regel sowieso alle Threads die interessant aussehen und geben ihren Senf dazu, egal ob es noch was zu sagen gibt oder nicht.
-
Trotzdem wäre es sinnvoller, hier im Forum wenigstens für Unregistrierte zu verbieten, Themen älter als sagen wir mal 1 Jahr wieder zu "reaktivieren".