Forum: PC-Programmierung C Informatikprojekt mit diversen Unterprogrammen und Hauptmenü


von Manuel (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,


ich möchte Euch mal mein kleines Projekt vorstellen, das ich mir zur 
Prüfungsvorbereitung entsprechend dem Inhalt der Informatik Teil 1 
Vorlesung heute Nachmittag zusammengebaselt habe.

Das Programm umfasst einen Buchstabenzähler, der die Anzahl der 
Großbuchstaben und Kleinbuchstaben erfasst.
Eine Funktion zur Inversion von Wörtern.
Und schließlich noch ein kleines Buchstabensuchspiel, das in einem 
eingegebenen Wort einen gewünschten Buchstaben sucht und ausgibt an 
welcher Stelle im Wort sich dieser befindet.
Das Programm ist durch ein kleines Hauptmenü gesteuert, mit dem sich 
alle Funktionen nacheinander aufrufen lassen und das Programm auch 
beenden kann.

Ich hoffe, ein paar Anregungen zu meinem Programmierstil, Verbesserungen 
und sonstige Ratschläge zu erhalten.

Außerdem soll es ein paar "Neueinsteigern" dienen, die sich mit der 
Sprache C befassen und Anregungen suchen.

Nur zur Info. Das Programm wurde mit MS Visual Studio 2010 erstellt.


MfG

Manuel

1
#include <stdlib.h>
2
#include <stdio.h>
3
#include <conio.h>
4
5
void buchstabensuche(char *p_ein, char *p_such)
6
{
7
  int i;
8
  for(i=0; i<=30; i++)
9
  {
10
    if((*(p_ein+i)==*p_such)||(*(p_ein+i)==(*p_such-32)))
11
      printf("Der %d. Buchstabe ist ein %c\n", i+1, *p_such);
12
  }
13
}
14
15
void buchstabenzaehler(char *p_zaehl)
16
{
17
  int i;
18
  int gB=0, kB=0;
19
  for(i=0; i<=50; i++)
20
  {
21
    if((*(p_zaehl+i)<='Z')&&*(p_zaehl+i)>='A')
22
      gB++;
23
    if((*(p_zaehl+i)<='z')&&*(p_zaehl+i)>='a')
24
      kB++;
25
  }
26
  printf("Anzahl der Grossbuchstaben:\t %d\n", gB);
27
  printf("Anzahl der Kleinbuchstaben:\t %d\n", kB);
28
  printf("Anzahl Buchstaben insgesamt:\t %d\n", kB+gB);
29
}
30
31
void wortinversion(char *p_wort)
32
{
33
  int i, j;
34
  char ausgabe[30];
35
36
  for(i=0; i<30;i++)
37
  {
38
    if((*(p_wort+i)<=175)&&*(p_wort+i)>=41)    //nahezu alle Zeichen + alle Zahlen und Buchstaben
39
      ausgabe[29-i]=*(p_wort+i);
40
    else
41
      ausgabe[29-i]=0;
42
  }
43
44
  for(j=0; j<30; j++)
45
  {
46
    if(ausgabe[j]!=0)
47
      printf("%c", ausgabe[j]);
48
  }
49
}
50
51
52
53
void main (void)
54
{
55
56
  char eingabe[20];    //Buchstabensuchspiel-Eingabewort
57
  char suche;          //Buchstabensuchspiel-Suchbuchstabe
58
  char wortzaehl[30];  //Buchstabenzählspiel
59
  char wortinv[30];    //Wortversion
60
61
  int sel=0;          //Hauptmenüauswahl
62
63
  do
64
  {
65
    printf("\nWillkommen im Hauptmenue\n\nWaehlen Sie:\n");
66
    puts("A - Buchstabensuchspiel");
67
    puts("B - Buchstabenzaehlspiel");
68
    puts("C - Wortinversion");
69
    puts("D - Ende");
70
    putchar('\n');
71
    sel = getch();
72
    system("cls");
73
74
    switch(sel)
75
    {
76
    case 'a':  
77
    case 'A':    
78
            printf("Buchstabenspiel\n");
79
            printf("Geben Sie ein Wort ein\n");
80
            scanf("%s", eingabe);
81
            fflush(stdin);
82
            printf("Geben Sie den zu suchenden Buchstaben ein\n");
83
            scanf("%c", &suche);
84
            fflush(stdin);
85
            buchstabensuche(eingabe, &suche);
86
            break;
87
    case 'b':
88
    case 'B':    
89
            printf("Buchstabenzaehlspiel\n");
90
            printf("Geben Sie ein Wort ein\n");
91
            scanf("%s", wortzaehl);
92
            fflush(stdin);
93
            buchstabenzaehler(wortzaehl);
94
            break;
95
  
96
    case 'c':
97
    case 'C':    
98
            printf("Buchstabeninversion\n");
99
            printf("Geben Sie ein Wort ein\n");
100
            scanf("%s", wortinv);
101
            fflush(stdin);
102
            wortinversion(wortinv);
103
            break;
104
    case 'd':
105
    case 'D':   
106
            printf("Programm wird beendet\n");
107
            break;
108
    default:   
109
            printf("Der eingegeben Buchstabe entspricht keinem der Menuepunkte");
110
            break;
111
    }
112
  }
113
  while((sel != 'D')&&(sel != 'd'));
114
115
116
117
  system("pause");
118
}

von Manuel (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch die .exe Datei, für diejenigen, die es ohne 
Entwicklungsumgebung testen möchten :)

von Achim_42 (Gast)


Lesenswert?

Ich hätte noch ein paar Kommentare eingefügt. Vor allem über den 
Funktionen.
Kurze Beschreibung, was die Funktion macht und welche Parameter 
übergeben werden.

von Achim_42 (Gast)


Lesenswert?


von atmeltierchen (Gast)


Lesenswert?

Kritik:

a. deine Variablen sind nich selbsterklärend. Ich hätte da schon keinen 
Bock mehr zu lesen.

b. Du verwendest magische Nummern. Das zerstört die Lesbarkeit komplett.

      printf("Der %d. Buchstabe ist ein %c\n", i+1, *p_such);

Was bedeutet "+1"?

c. Warum sind die Strings statisch?


 char eingabe[20];    //Buchstabensuchspiel-Eingabewort
  char suche;          //Buchstabensuchspiel-Suchbuchstabe
  char wortzaehl[30];  //Buchstabenzählspiel
  char wortinv[30];    //Wortversion

Nicht mehr als 20 Buchstaben? Ich wuerde mit malloc() oder welches sich 
auch immer anbietet, dynamisch allozieren.

d. Das hier ist Frickelcode

    if((*(p_wort+i)<=175)&&*(p_wort+i)>=41)

Pointer, Additionen, Vergleiche.. what!? Code soll sich selbst 
erklaeren, das tut er nicht.


Hätte ich den Code Reviewen müssen, hätt dich dir eine dicke Rechnung um 
die Ohren geschlagen für das Leid das man ertragen musste.

von Nase (Gast)


Lesenswert?

Viel scheint ja in der Vorlesung nicht rumgekommen zu sin.

* system("cls") ist blöd weil Windows/DOS,
* system("pause") auch
* ctype.h mit isupper() und islower() sind schon erfunden,
* Arrays indiziert man nicht mit *() sondern mit []...

von atmeltierchen (Gast)


Lesenswert?

Gibt es eigentlich ein C-Lernbuch das sich irgendwie im Stil an 
"Accelerated C++" orientiert?

Das Lernsystem dort ist ziemlich genial:

Bevor man sich an die Implementierungsdetails wagt, wird ohne 
Hinterfragen die Funktionen aus den Standard-Bibliotheken verwendet. 
Erst zum Schluss beginnt man diese von Hand nachzuimplementieren.

von mar IO (Gast)


Lesenswert?

Kritik an der Kritik

atmeltierchen schrieb:
> Kritik:
>
> a. deine Variablen sind nich selbsterklärend. Ich hätte da schon keinen
> Bock mehr zu lesen.

Das passt eigentlich soweit. Ich hatte damit keine Probleme.

> b. Du verwendest magische Nummern. Das zerstört die Lesbarkeit komplett.
>
>       printf("Der %d. Buchstabe ist ein %c\n", i+1, *p_such);
>
> Was bedeutet "+1"?

Wenn man sich die paar Codezeilen anschaut, dann versteht man es auch.

> c. Warum sind die Strings statisch?
>
>  char eingabe[20];    //Buchstabensuchspiel-Eingabewort
>   char suche;          //Buchstabensuchspiel-Suchbuchstabe
>   char wortzaehl[30];  //Buchstabenzählspiel
>   char wortinv[30];    //Wortversion
>
> Nicht mehr als 20 Buchstaben? Ich wuerde mit malloc() oder welches sich
> auch immer anbietet, dynamisch allozieren.

Kann man ruhig so lassen, aber auch anders lösen

> d. Das hier ist Frickelcode
>
>     if((*(p_wort+i)<=175)&&*(p_wort+i)>=41)
>
> Pointer, Additionen, Vergleiche.. what!? Code soll sich selbst
> erklaeren, das tut er nicht.

Daneben steht ein Kommentar. Das ist verständlich

> Hätte ich den Code Reviewen müssen, hätt dich dir eine dicke Rechnung um
> die Ohren geschlagen für das Leid das man ertragen musste.

Dazu sag ich mal nix...

Wenn Du nach Groß-/Kleinbuchstaben prüfst, dann kann man auch islower() 
und isupper() verwenden.

scantf() ist zwar schön und gut, aber kann einen Überlauf erzeugen. Das 
würde ich anders lösen.

von Manuel (Gast)


Lesenswert?

Erst mal Dankeschön für die schnellen Antworten

Nase schrieb:
> Arrays indiziert man nicht mit *() sondern mit []...

Arrays haben ja []. Bei den Pointern in den Unterfunktionen muss ich ja 
dann *() nehmen, da es ja nur auf die Anfangsadresse der Arrays zeigt.

Alternativen zu den system-Befehlen kenne ich leider bisher noch nicht 
,da in der Vorlesung immer nur die von mir verwendeten genutzt werden.

atmeltierchen schrieb:
> c. Warum sind die Strings statisch?

Der Grund, wieso sie statisch sind, liegt darin, dass dynamische 
Speicherverwaltung erst im 2. Semester vorgetragen wird und ich daher 
noch keinen Dunst davon habe.

Dies hier ist ja kein Softwareprojekt für ein Programm, mit dem Geld 
verdient werden soll, sondern lediglich ein Spiegelbild meiner 
Fähigkeiten, die ich nun nach einem Semester Informatik an einer 
Fachhochschule gesammelt habe.
Dass ich mehr hätte kommentieren müssen ist mir jetzt im Nachhinein auch 
bewusst geworden.

atmeltierchen schrieb:
> b. Du verwendest magische Nummern. Das zerstört die Lesbarkeit komplett.

Die magischen Nummern entsprechen in aller Regel ASCII-Codes. Die 1, die 
Du mir ankreiden würdest, soll heißen, dass "n" bei der Ausgabe "der n. 
Buchstabe" richtig berechnet wird. Das erste Zeichen im Array hat die 
[0], wobei in meiner Ausgabe eine 1 statt 0 stehen soll.

von atmeltierchen (Gast)


Lesenswert?

Ich will nicht nur kritisieren, sondern auch einmal schönen Code zeigen.

Exemplarisch.

1. Das Schachspiel "Stockfish" ist zwar in C++ und nicht in C, den 
wunderschönen Stil kann man aber ohne zögern übernehmen.


Beispiel:
https://github.com/mcostalba/Stockfish/blob/master/src/endgame.cpp
https://github.com/mcostalba/Stockfish/blob/master/src/movepick.cpp

Man versteht den Code auch auch nur eine Sekunde überlegen zu müssen.


2. FreeRTOS ist auch in den größten Teilen sehr schön geschrieben. Liegt 
auch daran, das es für FreeRTOS eine zertifizierte Varianter gibt. Da 
ist "schöner" Code absolute Pflicht.

https://github.com/blalor/FreeRTOS/blob/master/Source/timers.c
https://github.com/blalor/FreeRTOS/blob/master/Source/list.c

3. µC/OS-II ist auch sehr schön. Auch ein zertifiziertes OS mit schön 
geschriebenem Code welcher auch sehr gut kommentiert ist. Die Sourcen 
gibts allerdings nur, wenn man das Buch dazu ersteht.

von Manuel (Gast)


Lesenswert?

mar IO schrieb:
> Wenn Du nach Groß-/Kleinbuchstaben prüfst, dann kann man auch islower()
> und isupper() verwenden.
>
> scantf() ist zwar schön und gut, aber kann einen Überlauf erzeugen. Das
> würde ich anders lösen.

Das mit dem scanf()-Problem könnte ich anders lösen, erschien mir aber 
auf dem ersten Blick am verständlichsten, aber die Funktionen islower() 
und isupper() habe ich bisher noch nicht gekannt.

Der Inhalt meiner Informatik Vorlesung ist ohnehin etwas mager, da es 
nur ein Nebenfach darstellt. Funktionen die über die von mir im Programm 
verwendeten hinausgehen kommen erst in den nächsten Semestern.

von Manuel (Gast)


Lesenswert?

atmeltierchen schrieb:
> Ich will nicht nur kritisieren, sondern auch einmal schönen Code zeigen.

Dankeschön für die Anregung ;)
Die Links werde ich mir wohl dann gleich mal zu Gemüte führen.

Ich weiß, dass mein Programm wenig übersichtlich gestaltet wurde, da ich
a) kaum "richtigen" Code gesehen habe, der sich auf Höhe meiner 
Fähigkeiten befindet und
b) in meiner Vergangenheit mit Mikrocontrollern immer nur richtigen 
"Wildschweincode" produziert habe und ich mich jetzt erst langsam davon 
abgewöhnen kann

von Nase (Gast)


Lesenswert?

Manuel schrieb:
> Arrays haben ja []. Bei den Pointern in den Unterfunktionen muss ich ja
> dann *() nehmen, da es ja nur auf die Anfangsadresse der Arrays zeigt.

Aha. Und wo hast du das her?

von Achim_42 (Gast)


Lesenswert?

Hier auch noch ein Beispiel, wie man mit nicht ganz so übersichtlichem 
Code zum Gewinner wird:

http://www.ioccc.org/2013/birken/birken.c

Dabei bei "The International Obfuscated C Code Contest 2013" 
http://www.ioccc.org/years.html

von Dirk B. (dirkb2)


Lesenswert?

Du hast C-Strings nicht verstanden.
Ein C-String ist beim Zeichen mit dem Wert 0 zu Ende ('\0')

Du überprüfst alle Zeichen vom Array. Das können auch mehr sein als du 
eingegeben hast.
Das ist falsch.

Zudem ist deine Buchstabensuche auf 30 Zeichen fixiert und dein Zaehler 
auf 50. Das ist großer Mist.
Dein Array zum Suchen ist auch nur 30 Zeichen lang. Das ist gefährlich.

Und entweder Arrayschreibweise oder richtig mit Zeigern
1
void buchstabenzaehler(char *p_zaehl)  //Arrayschreibweise
2
{
3
  int i;
4
  int gB=0, kB=0;
5
  for(i=0; p_zaehl[i]!='\0'; i++)  // Bis zum Ende des Strings 
6
  {
7
    if((p_zaehl[i]<='Z')&&(p_zaehl[i]>='A'))
8
      gB++;
9
    if((p_zaehl[i]<='z')&&(p_zaehl[i]>='a'))
10
      kB++;
11
  }
12
  printf("Anzahl der Grossbuchstaben:\t %d\n", gB);
13
  printf("Anzahl der Kleinbuchstaben:\t %d\n", kB);
14
  printf("Anzahl Buchstaben insgesamt:\t %d\n", kB+gB);
15
}
16
17
void buchstabenzaehler(char *p_zaehl)  // Zeigerschreibweise
18
{
19
  int gB=0, kB=0;
20
  while (*p_zaehl)  // *p_zaehl != '\0'
21
  {
22
    if(*p_zaehl<='Z'&&*p_zaehl>='A')
23
      gB++;
24
    if(*p_zaehl<='z'&&*p_zaehl>='a')
25
      kB++;
26
    p_zaehl++;
27
  }
28
  printf("Anzahl der Grossbuchstaben:\t %d\n", gB);
29
  printf("Anzahl der Kleinbuchstaben:\t %d\n", kB);
30
  printf("Anzahl Buchstaben insgesamt:\t %d\n", kB+gB);
31
}

Das Zeichen zum suchen, solltest du auch als Zeichen übergeben und nicht 
als Zeiger

: Bearbeitet durch User
von Manuel (Gast)


Lesenswert?

Nase schrieb:
> Aha. Und wo hast du das her?

Ein Zeiger zeigt logischerweise auf die Adresse einer Variablen. 
Verwende ich ein Array ist die Anfangsadresse die des ersten Zeichens 
des Arrays z.B ptr=&char[0] ist das gleiche wie ptr=char .
Beim Dereferenzieren gehe ich dann eben genauso vor.

von mar IO (Gast)


Lesenswert?

Dirk B. schrieb:
> Zudem ist deine Buchstabensuche auf 30 Zeichen fixiert und dein Zaehler
> auf 50. Das ist großer Mist.
> Dein Array zum Suchen ist auch nur 30 Zeichen lang. Das ist gefährlich.

Zefix, das hab ich ja völlig übersehen... Sehr guter Kritikpunkt!

von Nase (Gast)


Lesenswert?

Manuel schrieb:
> Nase schrieb:
>> Aha. Und wo hast du das her?
>
> Ein Zeiger zeigt logischerweise auf die Adresse einer Variablen.
> Verwende ich ein Array ist die Anfangsadresse die des ersten Zeichens
> des Arrays z.B ptr=&char[0] ist das gleiche wie ptr=char .
> Beim Dereferenzieren gehe ich dann eben genauso vor.

Ja, und was spricht nun dagegen, die von allen C-Programmierern weltweit 
seit über 40 Jahren praktizierte Array-Indizierung mit eckigen Klammern 
zu verwenden?

von Karl H. (kbuchegg)


Lesenswert?

Manuel schrieb:
> Nase schrieb:
>> Aha. Und wo hast du das her?
>
> Ein Zeiger zeigt logischerweise auf die Adresse einer Variablen.
> Verwende ich ein Array ist die Anfangsadresse die des ersten Zeichens
> des Arrays z.B ptr=&char[0] ist das gleiche wie ptr=char .
> Beim Dereferenzieren gehe ich dann eben genauso vor.


Und
1
 *( a + i )

ist in C in allen Punkten identisch zu
1
  a[i]

Ganz im Gegenteil: die Indexoperation ist sogar exakt genau so 
definiert.
Tatsächlich wandelt daher der Compiler intern die Form a[i] als 
allererstes erst mal in die Form *(a+i) um. Alles was du also mit deiner 
komplexen Schreibweise gemacht hast ist, dem Compiler ein bischen Arbeit 
zu ersparen. Programmtechnisch bzw. an dem was hinten raus kommt, ändert 
sich dadurch aber nichts.

Damit könntest du
1
    if((*(p_zaehl+i)<='Z')&&*(p_zaehl+i)>='A')
in der völlig gleichwertigen Form
1
    if( p_zaehl[i] <= 'Z' && p_zaehl[i] >= 'A' )
formulieren und damit schon mal einiges an visueller Komplexität aus dem 
Code herausnehmen.

Aber eigentlich bevorzugt man als C-Programmierer die Form, in der 
komplett ohne Indexvariable gearbeitet wird, so wie Dirk das in seiner 
'buchstabenzaehler' zeigt. Warum? Ganz einfach. Wenn man keinen Index 
hat, muss man sich auch nicht über den Datentyp einer derartigen 
Indexvariablen Gedanken machen (der korrekt eigentlich ein size_t wäre).

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.