Forum: PC-Programmierung C - Char array löschen


von Müller (Gast)


Lesenswert?

Hallo liebes Forum,

wir haben in der Technikerschule(ET) mit C angefangen, da ich schon 
Grundkenntnisse habe, also die ganzen Programmierstrukturen(while,for,if 
etc..) und mich das ganze sehr interessiert, habe ich meinen Lehrer nach 
einer anspruchsvolleren Aufgabe gefragt. Und zwar sollte ich eine 
einfache Login Maske bauen, die folgende Funktionen haben soll:
- Eingabe darf nur Zahlen und Buchstaben(klein+groß) enthalten, 
ansonsten Fehler ausgeben
- Ist Eingabe korrekt, soll User + Passwort getrennt abgefragt werden 
und entsprechend ein Fehler ausgegeben werden(Falscher User, Falsches 
Passwort)
- Bei einem Fehler soll der User die Möglichkeit haben, das ganze zu 
wiederholen.
- Wenn alles korrekt ist, soll eine Erfolgreich Meldung ausgegeben 
werden, und das Programm abgebrochen werden.
- Zuerst soll der Username abgefragt werden, dann das Passwort


Mein Programm funktioniert auch mehr oder weniger, denke ich. Aber ich 
habe ein größeres Problem. Ich definiere char arrays mit 20 Elementen 
und lese über fgets die Eingaben ein.

Sobald ich aber bei einer Eingabe, mehr als 20 Zeichen eingebe, führt 
das zu einem "undefinierten Verhalten"...

Bsp.: Wenn ich beim Username über 20 Zeichen eingebe, wird danach direkt 
zur Überprüfung(checkEqual()) gepsrungen, Passwort Abfrage wird 
übersprungen.

Wenn ich beim Username unter 20 Zeichen eingebe und danach beim Passwort 
mehr als 20 Zeichen(und ein falsches) eingebe, wird beim nächsten 
Durchlauf, die Abfragen komplett übersprungen.

Ich habe schon versucht über memset zu löschen, aber das setzt direkt 
bei Element im Prinzip den Nullterminator, und überspringt dann im 
zweiten Durchlauf die Abfragen...

Hat jemand eine Idee?

Die OMP Sachen sind nur Spielerei. Könnt ihr ignorieren ;)

Achja der Code:
1
#include <stdio.h>
2
#include <string.h>
3
#include <ctype.h>
4
#include <omp.h>
5
6
typedef enum { false, true } bool;
7
8
// Funktion um Eingabe zu prüfen, Nur Buchstaben und Zahlen erlaubt!
9
bool checkEingabe(const char *string)
10
{
11
    if(!string) return 0;
12
13
    size_t length = strlen(string), i;
14
    bool breakflag = 0;
15
16
    if(string[length] != '\0') return 0;
17
18
    #pragma omp parallel for shared(breakflag)
19
    for(i = 0; i<length-1; i++)
20
    {
21
        if(!breakflag)
22
        {
23
            if((isdigit(string[i]) || isalpha(string[i])) && !isspace(string[i])) continue;
24
            breakflag = 1;
25
        }
26
        else
27
        {
28
            continue;
29
        }
30
    }
31
32
    if(breakflag) return 0;
33
34
    return 1;
35
}
36
37
// ErweiterteFunktion um 2 Strings unterschiedlicher Größe zu vergleichen
38
bool checkEqual(const char *string1, const char *string2)
39
{
40
    if(!string1 || !string2) return 0;
41
42
    size_t length1 = strlen(string1), length2 = strlen(string2), i;
43
44
    bool breakflag = 0;
45
46
    if(length1 != length2) return 0;
47
48
    #pragma omp parallel for shared(breakflag)
49
    for(i = 0; i<length1; i++)
50
    {
51
        if(!breakflag)
52
        {
53
            if(string1[i] != string2[i]) breakflag = 1;
54
        }
55
        else
56
        {
57
            continue;
58
        }
59
    }
60
61
    if(breakflag) return 0;
62
63
    return 1;
64
}
65
66
int main()
67
{
68
    // Variablen Deklarationen
69
    char nutzername[20], passwort[20];
70
71
    // Konsolen Design
72
    system("color 7F");
73
74
    // Beginne Schleife
75
    do {
76
        // Bildschirm löschen
77
        system("cls");
78
79
        // Titel des Programms
80
        printf("\t\t\t\t-------------\n");
81
        printf("\t\t\t\t|Login-Seite|\n");
82
        printf("\t\t\t\t-------------\n\n\n");
83
84
        // Hinweis
85
        printf("Es d\x81rfen nur Buchstaben, Zahlen eingegeben werden!\n");
86
87
        // Sicheres Einlesen des Nutzernames
88
        printf("\nBitte geben Sie nun ihren Nutzernamen ein:\n");
89
        fgets(nutzername, sizeof(nutzername) / sizeof(char), stdin);
90
91
        // Newline entfernen und Nullterminator setzen
92
        nutzername[strlen(nutzername)-1] = 0;
93
94
        // Überprüfe die Eingabe des Nutzernamen auf Zahlen, Buchstaben und Leerzeichen
95
        if(checkEingabe(nutzername))
96
        {
97
            // Sicheres Einlesen des Passwortes
98
            printf("Bitte geben Sie nun Ihr Passwort ein:\n");
99
            fgets(passwort, sizeof(passwort) / sizeof(char), stdin);
100
101
            // Newline entfernen und Nullterminator setzen
102
            passwort[strlen(passwort)-1] = 0;
103
104
            // Überprüfe die Eingabe des Passwort auf Zahlen, Buchstaben und Leerzeichen
105
            if(checkEingabe(passwort))
106
            {
107
108
                printf("\t------------------------------------------------------------\n");
109
                printf("\t|Ihr Nutzername und das Passwort werden jetzt verglichen...|\n");
110
                printf("\t------------------------------------------------------------\n\n");
111
112
                // Warte 2 Sekunden
113
                sleep(2);
114
115
                // Ist das Nutzername korrekt?
116
                if(checkEqual(nutzername, "User"))
117
                {
118
                    // Ist das Passwort korrekt?
119
                    if(checkEqual(passwort, "Passwort"))
120
                    {
121
                        printf("\n\n");
122
                        printf("\t\t\t---------------------------\n");
123
                        printf("\t\t\t|Sie sind nun eingeloggt!!|\n");
124
                        printf("\t\t\t---------------------------\n");
125
                        printf("\n\n");
126
127
                        return 1;
128
                    }
129
                    else
130
                    {
131
                        // Lösche die Char Arrays
132
                        //memset(nutzername, 0, sizeof(nutzername));
133
                        //memset(passwort, 0, sizeof(passwort));
134
135
                        // Ausgabe des Fehlers
136
                        printf("Sie haben das falsche Passwort eingegeben!!!\n");
137
138
                        // Warte 5 Sekunden
139
                        sleep(5);
140
141
                        // Lösche den Bildschirm
142
                        system("cls");
143
144
                        // Wiederholungsanforderung
145
                        printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
146
                    }
147
                }
148
                else
149
                {
150
                    // Lösche die Char Arrays
151
                    //memset(nutzername, 0, sizeof(nutzername));
152
                    //memset(passwort, 0, sizeof(passwort));
153
154
                    // Ausgabe des Fehlers
155
                    printf("Der Nutzername stimmt nicht \x81\berein!!!\n");
156
157
                    // Warte 5 Sekunden
158
                    sleep(5);
159
160
                    // Lösche den Bildschirm
161
                    system("cls");
162
163
                    // Wiederholungsanforderung
164
                    printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
165
                }
166
167
            }
168
            else
169
            {
170
                // Lösche die Char Arrays
171
                //memset(nutzername, 0, sizeof(nutzername));
172
                //memset(passwort, 0, sizeof(passwort));
173
174
                // Ausgabe des Fehlers
175
                printf("Sie haben keine korrekte Eingabe gemacht!!!\n");
176
177
                // Warte 5 Sekunden
178
                sleep(5);
179
180
                // Lösche den Bildschirm
181
                system("cls");
182
183
                // Wiederholungsanforderung
184
                printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
185
            }
186
        }
187
        else
188
        {
189
            // Lösche die Char Arrays
190
            //memset(nutzername, 0, sizeof(nutzername));
191
            //memset(passwort, 0, sizeof(passwort));
192
193
            // Ausgabe des Fehlers
194
            printf("Sie haben keine korrekte Eingabe gemacht!!!\n");
195
196
            // Warte 5 Sekunden
197
            sleep(5);
198
199
            // Lösche den Bildschirm
200
            system("cls");
201
202
            // Wiederholungsanforderung
203
            printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
204
        }
205
    } while(getch() != 'k' || getch() != 'K');
206
}

Lieben Gruß

von Peter II (Gast)


Lesenswert?

ohne das Programm jetzt wirklich angeschaut zu haben.

Ein String (char Array) braucht immer 1 Zeichen mehr für die 
abschließende \0
1
fgets(nutzername, sizeof(nutzername) / sizeof(char), stdin);
2
[c]
3
4
sizeof(char) ist immer 1 - kannst du dir also sparen.
5
6
7
teste mal mit:
8
9
[c]
10
fgets(nutzername, sizeof(nutzername)-1, stdin);

von Peter (Gast)


Lesenswert?

Das hier sieht schon mal ungut aus:
1
// Newline entfernen und Nullterminator setzen
2
nutzername[strlen(nutzername)-1] = 0;
Stell Dir vor, strlen() gibt 0 zurück. Dann überschreibst Du Speicher 
vor dem Array.

Ansonsten hast Du doch sicher eine Entwicklungsumgebung mit einem 
Debugger, mit dem Du Dir anschauen kannst, was passiert?

von DirkB (Gast)


Lesenswert?

fgets liest maximal die Anzahl Zeichen -1 ein. Wenn da noch kein '\n' 
dabei war, ist die Eingabe noch nicht abgeschlossen.

Dies wird bei dir dann beim nächsten fgets eingelesen.

Das '\n' im String ist also nicht ganz überflüssig.

von Müller (Gast)


Lesenswert?

Herzlichen Dank für das Feedback!

Das newline hat mir diverse Probleme beim vergleichen eingebracht. Wenn 
ich das entfernen der Newline in der checkEqual Funktion einfüge, 
schmiert das Programm bei der ersten Abfrage(checkEqual) ab. Deswegen 
hab ich das in der Mainfunktion drinne.

@Peter 2
Danke, werd ich ändern.

@Peter 1
Du hast recht, ich mache eine kleine Abfrage davor.

Gruß

von Müller (Gast)


Lesenswert?

@dirkb p.s. Ich hatte dann statt const Char *string1, Char *string1 
drinne.

von Müller (Gast)


Lesenswert?

Hallo zusammen,

ich habe das Programm nun angepasst und eine Abfrage eingefügt, in der 
abgefragt wird, ob das Newline vorhanden ist(checkEingabe) und prüfe 
auch die Zeichenlänge. Jedoch bleibt weiterhin das Problem bestehen, 
dass wenn ich absichtlich mehr als 20 Zeichen eingebe, dass dies zu 
undefinierten Verhalten führt, weil die Variablen dann noch gespeichert 
bleiben...

Hier der angepasste Code:
1
#include <stdio.h>
2
#include <string.h>
3
#include <ctype.h>
4
#include <omp.h>
5
6
typedef enum { false, true } bool;
7
8
// Funktion um Eingabe zu prüfen, Nur Buchstaben und Zahlen erlaubt!
9
bool checkEingabe(const char *string)
10
{
11
    if(!string) return 0;
12
13
    size_t length = strlen(string), i;
14
    bool breakflag = 0;
15
16
    if(length <= 1) return 0;
17
18
    if(string[length] != '\0' || string[length-1] != '\n') return 0;
19
20
    #pragma omp parallel for shared(breakflag)
21
    for(i = 0; i<length-1; i++)
22
    {
23
        if(!breakflag)
24
        {
25
            if((isdigit(string[i]) || isalpha(string[i])) && !isspace(string[i])) continue;
26
            breakflag = 1;
27
        }
28
        else
29
        {
30
            continue;
31
        }
32
    }
33
34
    if(breakflag) return 0;
35
36
    return 1;
37
}
38
39
// ErweiterteFunktion um 2 Strings unterschiedlicher Größe zu vergleichen
40
bool checkEqual(char *string1, const char *string2)
41
{
42
    if(!string1 || !string2) return 0;
43
44
    if(sizeof(string1) <= 1 || sizeof(string2) <= 1) return 0;
45
46
    string1[strlen(string1)-1] = '\0';
47
48
    size_t length1 = strlen(string1), length2 = strlen(string2), i;
49
50
    bool breakflag = 0;
51
52
    if(length1 != length2) return 0;
53
54
    #pragma omp parallel for shared(breakflag)
55
    for(i = 0; i<length1; i++)
56
    {
57
        if(!breakflag)
58
        {
59
            if(string1[i] != string2[i]) breakflag = 1;
60
        }
61
        else
62
        {
63
            continue;
64
        }
65
    }
66
67
    if(breakflag) return 0;
68
69
    return 1;
70
}
71
72
int main()
73
{
74
    // Variablen Deklarationen
75
    char nutzername[20], passwort[20];
76
77
    // Konsolen Design
78
    system("color 7F");
79
80
    // Beginne Schleife
81
    do {
82
        // Bildschirm löschen
83
        system("cls");
84
85
        // Titel des Programms
86
        printf("\t\t\t\t-------------\n");
87
        printf("\t\t\t\t|Login-Seite|\n");
88
        printf("\t\t\t\t-------------\n\n\n");
89
90
        // Hinweis
91
        printf("Es d\x81rfen nur Buchstaben, Zahlen eingegeben werden!\n");
92
93
        // Sicheres Einlesen des Nutzernames
94
        printf("\nBitte geben Sie nun ihren Nutzernamen ein:\n");
95
        fgets(nutzername, sizeof(nutzername), stdin);
96
97
        // Überprüfe die Eingabe des Nutzernamen auf Zahlen, Buchstaben und Leerzeichen
98
        if(checkEingabe(nutzername))
99
        {
100
            // Sicheres Einlesen des Passwortes
101
            printf("Bitte geben Sie nun Ihr Passwort ein:\n");
102
            fgets(passwort, sizeof(passwort), stdin);
103
104
            // Überprüfe die Eingabe des Passwort auf Zahlen, Buchstaben und Leerzeichen
105
            if(checkEingabe(passwort))
106
            {
107
108
                printf("\t------------------------------------------------------------\n");
109
                printf("\t|Ihr Nutzername und das Passwort werden jetzt verglichen...|\n");
110
                printf("\t------------------------------------------------------------\n\n");
111
112
                // Warte 2 Sekunden
113
                sleep(2);
114
115
                // Ist das Nutzername korrekt?
116
                if(checkEqual(nutzername, "User"))
117
                {
118
                    // Ist das Passwort korrekt?
119
                    if(checkEqual(passwort, "Passwort"))
120
                    {
121
                        printf("\n\n");
122
                        printf("\t\t\t---------------------------\n");
123
                        printf("\t\t\t|Sie sind nun eingeloggt!!|\n");
124
                        printf("\t\t\t---------------------------\n");
125
                        printf("\n\n");
126
127
                        return 1;
128
                    }
129
                    else
130
                    {
131
                        // Lösche die Char Arrays
132
                        //memset(nutzername, 0, sizeof(nutzername));
133
                        //memset(passwort, 0, sizeof(passwort));
134
135
                        // Ausgabe des Fehlers
136
                        printf("Sie haben das falsche Passwort eingegeben!!!\n");
137
138
                        // Warte 5 Sekunden
139
                        sleep(5);
140
141
                        // Lösche den Bildschirm
142
                        system("cls");
143
144
                        // Wiederholungsanforderung
145
                        printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
146
                    }
147
                }
148
                else
149
                {
150
                    // Lösche die Char Arrays
151
                    //memset(nutzername, 0, sizeof(nutzername));
152
                    //memset(passwort, 0, sizeof(passwort));
153
154
                    // Ausgabe des Fehlers
155
                    printf("Der Nutzername stimmt nicht \x81\berein!!!\n");
156
157
                    // Warte 5 Sekunden
158
                    sleep(5);
159
160
                    // Lösche den Bildschirm
161
                    system("cls");
162
163
                    // Wiederholungsanforderung
164
                    printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
165
                }
166
167
            }
168
            else
169
            {
170
                // Lösche die Char Arrays
171
                //memset(nutzername, 0, sizeof(nutzername));
172
                //memset(passwort, 0, sizeof(passwort));
173
174
                // Ausgabe des Fehlers
175
                printf("Sie haben keine korrekte Eingabe gemacht!!!\n");
176
177
                // Warte 5 Sekunden
178
                sleep(5);
179
180
                // Lösche den Bildschirm
181
                system("cls");
182
183
                // Wiederholungsanforderung
184
                printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
185
            }
186
        }
187
        else
188
        {
189
            // Lösche die Char Arrays
190
            //memset(nutzername, 0, sizeof(nutzername));
191
            //memset(passwort, 0, sizeof(passwort));
192
193
            // Ausgabe des Fehlers
194
            printf("Sie haben keine korrekte Eingabe gemacht!!!\n");
195
196
            // Warte 5 Sekunden
197
            sleep(5);
198
199
            // Lösche den Bildschirm
200
            system("cls");
201
202
            // Wiederholungsanforderung
203
            printf("Bitte dr\x81cken Sie k, um die Eingabe zu wiederholen!\n\n");
204
        }
205
    } while(getch() != 'k' || getch() != 'K');
206
}
Lieben Gruß

von Peter II (Gast)


Lesenswert?

1
 size_t length = strlen(string), i;

warum schreibt man so etwas unlesbares? Selbst ich, der schon länger C 
schreibt, bin mir nicht sicher wie der "," Operator sich hier verhält.
1
#pragma omp parallel for shared(breakflag)

noch nicht mal richtig C können, und gleich versuchen zu optimieren? Bei 
einem Programm wo das absolut unnötig ist.

1
while(getch() != 'k' || getch() != 'K');

macht auch nicht was du willst, hier wird 2 mal eingelesen.

von Peter II (Gast)


Lesenswert?

es geht weiter:
1
if(sizeof(string1) <= 1 || sizeof(string2) <= 1) return 0;

ein Zeiger ist immer gleich groß, die abfrage macht überhaupt keinen 
sinn.



viel zu kompliziert und falsch (length-1 warum nicht das letzte zeichen 
prüfen!):
1
 for(i = 0; i<length-1; i++)
2
    {
3
        if(!breakflag)
4
        {
5
            if((isdigit(string[i]) || isalpha(string[i])) && !isspace(string[i])) continue;
6
            breakflag = 1;
7
        }
8
        else
9
        {
10
            continue;
11
        }
12
    }
13
14
    if(breakflag) return 0;

geht auch einfacher:
1
 for(i = 0; i<length-1; i++) {
2
    if((isdigit(string[i]) || isalpha(string[i])) && !isspace(string[i])) {
3
        return 0;
4
    }; 
5
}

von DirkB (Gast)


Lesenswert?

Müller schrieb:
> size_t length = strlen(string)
>
> if(string[length] != '\0'

Was soll denn da raus kommen.

So ist ein C-String definiert. Das geht da nicht anders.


Müller schrieb:
> Jedoch bleibt weiterhin das Problem bestehen,
> dass wenn ich absichtlich mehr als 20 Zeichen eingebe, dass dies zu
> undefinierten Verhalten führt

Weil du das Problem nicht behoben hast, das dann noch Zeichen im 
Eingabestrom stehen.
(Du musst dann solange Zeichen lesen, bis ein '\n' auftaucht)

von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Peter 2,

erstmal danke für die Hinweise. Habe das erstmal umgesetzt. Wie gesagt 
die Open-MP Sache, ist nur eine Spielerei. Die wird ja wohl nicht dafür 
verantwortlich sein, dass es nicht so recht funktioniert, denke ich.

Parallele Sektionen dürfen nicht unterbrochen(break/return) werden, 
deshalb sieht in erster Linie etwas kompliziert aus. Aber das weist du 
ja schon.

Ich habe die angepasste Datei angehängt, da dass etwas zu voll wird, 
denke ich.

Gruß

von Peter II (Gast)


Lesenswert?

Müller schrieb:
> erstmal danke für die Hinweise. Habe das erstmal umgesetzt. Wie gesagt
> die Open-MP Sache, ist nur eine Spielerei.

naja, ich halte das sogar für eine Resourcenverschwendung.

Wenn das 1.Zeichen schon ungültig ist, arbeiten dann die andere 
Cores/Threads noch den String bis zum ende ab. Obwohl schon lange 
feststeht, das er ungültig ist.

Nicht jedes Problem lässt sich optimieren.

Lerne doch erst mal mit einem Debugger umzugehen, dann kannst du das 
Programm schrittweise durchgehen und sehen was genau passiert und wo es 
zu einem Problem kommt.

von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Danke an alle für die Hilfe, ich denke ich habe das Problem jetzt 
behoben.

@DirkB
Dass du mir nicht direkt gesagt dass ich den Eingabestream leeren muss, 
dann hätte ich das auch eher kapiert :-(

Kurze Frage hierzu, ist es sinnvoll wenn man etwas Einliest, am Ende 
oder im Fehlerfall dem Eingabestream zu leeren? Oder ist das generell 
nötig? Auch wenn man sowas wie scanf oder so benutzt?

Gruß

von Mikro 7. (mikro77)


Lesenswert?

Hallo Müller,

schön, dass du dich für das Progammieren mit C interessierst. :-)

Falls Du Interesse hast, hier ein paar Kommentare:

(1) Schau dir mal die Beschreibung von fgets() genau an. Was passiert 
bei
- EOF (^D,^D^D oder ^Z)
- einer Zeichenkette mit EOF
- einer Zeichenkette mit Return
- einer Zeichenkette mit 20 oder mehr Zeichen und EOF
- einer Zeichenkette mit 19 oder mehr Zeichen und Return

(2) Die Wirkung von fflush(stdin) ist im Standard nicht definiert. Wenn 
es bei dir macht was du willst (alle "offenen" Zeichen in stdin 
löschen), ist das nicht portabel. Bei "mir" funktioniert das bspw. 
nicht.

(3) "Komplizierte" Ausdrücke vermeiden.
1
if((isdigit(string[i]) || isalpha(string[i])) && !isspace(string[i]))

Das ist nicht "falsch", aber was passiert da eigentlich genau?

(4) Du hast dir "bool" mit "true" und "false" definiert. Warum, wenn du 
true und false gar nicht benutzt?

Grüße, mikro

von Rolf M. (rmagnus)


Lesenswert?

S. J. schrieb:
> (4) Du hast dir "bool" mit "true" und "false" definiert. Warum, wenn du
> true und false gar nicht benutzt?

Und warum überhaupt selber definieren? Ein #include <stdbool.h> sollte 
reichen.

von Müller (Gast)


Lesenswert?

Hallo zusammen,
1) mach ich morgen mal in aller Ruhe
2) was wäre denn Standard? fseek(stdin,0,SEEK_END) ?
3) ich find das nicht kompliziert.
Wenn jemand AWL kennt, wäre das wie:
U(
O e ist Zahl?
O e ist Buchstabe?
)
UN e ist Kein Leerzeichen

Also es darf kein Leerzeichen sein und ( es darf eine Zahl sein oder ein 
Buchstabe ). Habe es nicht eingesehen gleich 3 Abfragen zu bauen.

4) naja damit man auf Anhieb sehen kann, dass es eine boolsche Funktion 
ist.



Lieben Gruß

von Mikro 7. (mikro77)


Lesenswert?

Hallo,

> 2) was wäre denn Standard? fseek(stdin,0,SEEK_END) ?

Nein.

Ich kenne leider keinen einfachen und zuverlässigen Weg einen 
Input-C-File-Stream nicht-blockierend und platformunabhängig 
(Win/DOS/Linux) zu "flushen".

Vielleicht kennt ja hier jemand solch eine Möglichkeit.

> 3) ich find das nicht kompliziert.

Das ist ok. Aber denk dran, dass vielleicht auch andere deinen Quelltext 
lesen.

Und was wäre denn wenn man den letzten Teil der Bedingung (!isspace(c)) 
wegläßt und statt dessen...
1
if (isdigit(c) || isalpha(c)) continue;

> 4) naja damit man auf Anhieb sehen kann, dass es eine boolsche Funktion
> ist.

Gut. Ich selbst würde dann auch false und true statt 0 und 1 benutzen.

Grüße, mikro

von Sheeva P. (sheevaplug)


Lesenswert?

Müller schrieb:
> ich habe das Programm nun angepasst und eine Abfrage eingefügt, in der
> abgefragt wird, ob das Newline vorhanden ist(checkEingabe) und prüfe
> auch die Zeichenlänge. Jedoch bleibt weiterhin das Problem bestehen,
> dass wenn ich absichtlich mehr als 20 Zeichen eingebe, dass dies zu
> undefinierten Verhalten führt, weil die Variablen dann noch gespeichert
> bleiben...
1
fflush(stdin);

ist Dein Freund. ;-)

von rüdiger (Gast)


Lesenswert?

Sheeva P. schrieb:
> fflush(stdin);
>
> ist Dein Freund. ;-)

Ist für input streams nur leider undefiniertes Verhalten.
Wenn man 'Glück' hat, klappt es auf bestimmten Systemen, aber drauf 
verlassen sollte man sich nicht...

von Sheeva P. (sheevaplug)


Lesenswert?

Müller schrieb:
> erstmal danke für die Hinweise. Habe das erstmal umgesetzt. Wie gesagt
> die Open-MP Sache, ist nur eine Spielerei. Die wird ja wohl nicht dafür
> verantwortlich sein, dass es nicht so recht funktioniert, denke ich.

"Premature optimization is the root of all evil." (Donald E. Knuth: 
"Computer Programming as an Art", 1974)

Erstens: das Programm verbringt mindestens 99,9% seiner Laufzeit damit, 
auf auf Benutzereingaben zu warten, die naturgemäss nicht parallelisiert 
werden können. Die übrigen 0,1% des Programms durch Parallelisierung 
beschleunigen zu wollen, ist, vorsichtig gesagt, Quatsch.

Zweitens erzeugt eine Parallelisierung zunächst einen Overhead, der 
durch das Verwalten der Prozesse, Threads oder Microthreads entsteht. 
Wenn Du so einfache und winzige Teile Deines Programmes parallelisierst, 
übersteigt dieser Overhead jeden Gewinn bei Weitem.

Es wäre nicht schlimm, wenn Deine Optimierung einfach nur unsinnig wäre. 
Daß sie kontraproduktiv und Dein Programm stattdessen komplizierter und 
ineffizienter machen: das ist schlimm.

Auch in einem anderen Kontext ist OMP hier kontraproduktiv, denn 
immerhin bist es ja Du, der hier nach Unterstützung sucht -- und der 
deswegen die Aufgabe hat, das Problem möglichst genau und unkompliziert 
darzustellen, damit andere sich das anschauen.

von Sheeva P. (sheevaplug)


Lesenswert?

S. J. schrieb:
> (2) Die Wirkung von fflush(stdin) ist im Standard nicht definiert. Wenn
> es bei dir macht was du willst (alle "offenen" Zeichen in stdin
> löschen), ist das nicht portabel. Bei "mir" funktioniert das bspw.
> nicht.

Entschuldige bitte, aber was für eine Plattform ist denn das dort bei 
Dir, wo das nicht funktioniert?

von Mikro 7. (mikro77)


Lesenswert?

Nennt sich Linux (konkret Debian 8).

von Keks (Gast)


Lesenswert?

Zu mal man von OpenMP  die Finger lassen sollte bevor man C richtig 
verstanden hat. Man muss schon sehr genau wissen was da passiert welche 
Variablen auf dem Stack / Heap liegen welche geshared werden müssen 
welche es auf keinen Fall dürfen. Ansonsten führt OpenMp nur zu falschen 
Ergebnissen oder ist sehr viel langsamer als sequentieller Code.

von Sheeva P. (sheevaplug)


Lesenswert?

S. J. schrieb:
> Nennt sich Linux (konkret Debian 8).

Laut [1] ist "fflush(stdin)" in POSIX.1-2008 definiert. Was nicht 
definiert ist, ist "fflush()" (ohne Parameter).

[1] http://man7.org/linux/man-pages/man3/fflush.3.html

von mikro77 (Gast)


Lesenswert?

Sheeva P. schrieb:
> Laut [1] ist "fflush(stdin)" in POSIX.1-2008 definiert. Was nicht
> definiert ist, ist "fflush()" (ohne Parameter).
>
> [1] http://man7.org/linux/man-pages/man3/fflush.3.html

For input streams associated with seekable files (e.g., disk files,
but not pipes or terminals), fflush() discards any buffered data that
has been fetched from the underlying file, but has not been consumed
by the application.

von Sheeva P. (sheevaplug)


Lesenswert?

mikro77 schrieb:
> Sheeva P. schrieb:
>> Laut [1] ist "fflush(stdin)" in POSIX.1-2008 definiert. Was nicht
>> definiert ist, ist "fflush()" (ohne Parameter).
>>
>> [1] http://man7.org/linux/man-pages/man3/fflush.3.html
>
> For input streams associated with seekable files (e.g., disk files,
> but not pipes or terminals), fflush() discards any buffered data that
> has been fetched from the underlying file, but has not been consumed
> by the application.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html

von Müller (Gast)


Lesenswert?

Hallo zusammen,
Wenn ich später mal Zeit habe, werde ich die OMP Sachen heraus nehmen.In 
dem Kontext macht es natürlich wenig Sinn, da habt ihr recht :) Ich 
fange im Prinzip damit an, damit ich im Laufe des nächsten Jahres, wenn 
wir quasi darin(bis zum Ende des Schuljahres) ein kleines Projekt machen 
müssen, vorbereitet bin. Ich wollte dann irgendwann einen Taschenrechner 
Parser machen.

Zum Thema Leerung:
fflush(stdin) ist unter Windows zulässig und sollte funktionieren, Msvrt 
unterstützt das wohl...
fseek(stdin,0,seek_end) funktioniert auch unter Windows.
Bei Linux soll wohl fpurge(stdin) klappen.
Da wir in der Schule eh nur Window 7 Rechner haben und ich persönlich 
Windows 8, nehme ich weiterhin fflUsh(stdin).

Es sei denn es gibt etwas was zu 100% funktioniert. Und Standard ist,
Dieses snippet:
1
Int c;
2
While(c = getchar() != '\n' && c != EOF);
Soll wohl auch leeren, aber das versteh ich nicht ganz, was passiert da 
genau?

Es wird so lange eingelesen bis newline und EOF kommt, aber was entleert 
den stdin dann?!

Da wird doch nix entleert?!

Vielen Dank

Gruß

von Rolf M. (rmagnus)


Lesenswert?

rüdiger schrieb:
> Sheeva P. schrieb:
>> fflush(stdin);
>>
>> ist Dein Freund. ;-)
>
> Ist für input streams nur leider undefiniertes Verhalten.

Ergibt ja eigentlich auch keinen Sinn. fflush() dient dazu, den Inhalt 
des internen Puffers an sein Ziel rauszuschreiben. Bei Input-Streams ist 
das Ziel aber dein Programm. Den Pufferinhalt zu verwerfen ist 
eigentlich eine andere Funktionalität.

> Es sei denn es gibt etwas was zu 100% funktioniert. Und Standard ist,
> Dieses snippet:
> Int c;
> While(c = getchar() != '\n' && c != EOF);
> Soll wohl auch leeren, aber das versteh ich nicht ganz, was passiert da
> genau?
>
> Es wird so lange eingelesen bis newline und EOF kommt, aber was entleert
> den stdin dann?!
>
> Da wird doch nix entleert?!

Warum nicht? Warum sollte ein Zeichen beim Lesen nicht aus dem Puffer 
entfernt werden?

von Mikro 7. (mikro77)


Angehängte Dateien:

Lesenswert?

Finde es schon ein bisschen komisch, dass man den input-buffer eines 
c-file-streams nicht portabel löschen kann, ohne zu blockieren. Da gibt 
es auch haufenweise matches im Web. Sehr irritierend. :-(

von Rolf M. (rmagnus)


Lesenswert?

S. J. schrieb:
> Finde es schon ein bisschen komisch, dass man den input-buffer eines
> c-file-streams nicht portabel löschen kann, ohne zu blockieren. Da gibt
> es auch haufenweise matches im Web. Sehr irritierend. :-(

Das liegt einfach daran, daß es auch Betriebssysteme gibt, bei denen 
diese Puffer vor dem Programm verborgen sind und wo das Programm keinen 
Einfluss darauf hat. Man hat dort einfach keine Möglichkeit, den 
Puffer-Inhalt zu löschen. Aber auch dafür soll es möglich sein, einen 
konformen C-Compiler zu schreiben.
Es gibt (und gab seit Erfindung von C) ja schließlich noch ein paar mehr 
Systeme neben Linux und Windows.

von Mikro 7. (mikro77)


Lesenswert?

Rolf M. schrieb:
> Das liegt einfach daran, daß es auch Betriebssysteme gibt, bei denen
> diese Puffer vor dem Programm verborgen sind

Naja, der C-File-Stream (FILE) ist Teil der C-Lib und nicht Teil des BS. 
Er könnte also immer von der Lib geflushed (gelöscht) werden.

Das jenseits des System Interfaces liegende Terminal, Pipe, Socket, File 
(whatever) wird im nächsten Schritt interessant. (Da kann man ggf. 
select/poll auf den FD des FILEs anwenden).

von Rolf M. (rmagnus)


Lesenswert?

S. J. schrieb:
> Rolf M. schrieb:
>> Das liegt einfach daran, daß es auch Betriebssysteme gibt, bei denen
>> diese Puffer vor dem Programm verborgen sind
>
> Naja, der C-File-Stream (FILE) ist Teil der C-Lib und nicht Teil des BS.

FILE (und stdin) ist nur ein Frontend für die Funktionalität des 
Betriebssystems.

> Das jenseits des System Interfaces liegende Terminal, Pipe, Socket, File
> (whatever) wird im nächsten Schritt interessant. (Da kann man ggf.
> select/poll auf den FD des FILEs anwenden).

Aber das ist dann eben nicht mehr portabel.

von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich habe mein Programm etwas "optimiert", ihr werdet es eh 
auseinanderreißen, aber egal =)

Kleine Sache noch:
Wenn ich über fflush oder fseek leere, kann ich auch STRG+D,Z,\n 
eingeben, da arbeitet er anstandslos weiter. Wenn ich nur über dieses 
Snippet leere, dann macht muss ich nach einer Eingabe von diesen Zeichen 
oder mehr als 20 Zeichen, nochmal eine Taste drücken, damit die 
Programmausfürhung weitergeht.

Lieben Gruß

von Mikro 7. (mikro77)


Lesenswert?

Rolf M. schrieb:

> FILE (und stdin) ist nur ein Frontend für die Funktionalität des
> Betriebssystems.

Meinst Du buffered I/O? Als "Frontend" kann man jede Funktion 
bezeichnen. Ist nicht falsch. Bringt aber auch nicht viel. ;-)

Aja, und die buffered I/O wird durch die C-Lib bereitgestellt, oder 
siehst du das anders?

>> Das jenseits des System Interfaces liegende Terminal, Pipe, Socket, File
>> (whatever) wird im nächsten Schritt interessant. (Da kann man ggf.
>> select/poll auf den FD des FILEs anwenden).
>
> Aber das ist dann eben nicht mehr portabel.

Man kommt zumindest mit POSIX C ohne curses aus. Das ist ja schon was. 
Mit IONBF klappt das dann auch.

von Forist (Gast)


Lesenswert?

Müller schrieb:
> Achja der Code:

Müller schrieb:
> Hier der angepasste Code:

Zur Schonung von Mouse-Rädern und sonstigen Navigationshilfen gibt es 
hier im Forum extra die Funktion "Dateianhang". Die ist wie geschaffen 
für längeren Code und verschont diejenigen mit nicht gewünschtem 
Lesestoff, die erstmal die Beiträge lesen möchten. ;-)

von DirkB (Gast)


Lesenswert?

Müller schrieb:
1
int c;
2
while(c = getchar() != '\n' && c != EOF);
> Soll wohl auch leeren, aber das versteh ich nicht ganz, was passiert da
> genau?
Es entfernt² die aktuelle Zeile aus dem Eingabestrom.

²wenn ein Zeichen gelesen wird, steht es nicht mehr im Eingabestrom. Es 
ist also daraus entfernt.

Das fflush(stdin) entfernt (wenn es funktioniert) alle bisher 
eingegebenen Zeichen. Auch mehrere Zeilen.

Man sollte dabei bedenken, dass auch eine Umleitung der Ein/Ausgabe 
mittels Pipe möglich ist.
Da macht ein fflush(stdin) wenig Sinn.

von Mikro 7. (mikro77)


Lesenswert?

Müller schrieb:

> ich habe mein Programm etwas "optimiert", ihr werdet es eh
> auseinanderreißen, aber egal =)

Weil das fflush(stdin) auf Win wohl funzt, würde ich die beiden 
Alternativen weg lassen. Es sei denn Du hast es durchgetestet und weißt 
wann fflush() fehlschlägt, wann fseek fehlschlägt() und dass dann die 
getc() Schleife das Problem löst. Hast du dafür ein Testszenario? 
Ansonsten, nur fflush().

Als Argument für die Funktion zur Fehlerausgabe würde ich statt eines 
Typs eher die Zeichenkette mit der Diagnose an die Funktion übergeben. 
Der Typ macht es nur komplizierter und bringt keine Vorteile (zumindest 
hier in diesem Fall).

Welchen speziellen Fall löst du mit... ?
1
if(length == 1 && !isalnum(string[0])) return false;

Zum Vergleich von Zeichenketten gibt es die strcmp() Funktion. (Das 
"Problem" mit dem '\n' läßt sich einfach lösen.)

Folgendes ist doppelt gemoppelt
1
if((fgets(passwort, sizeof(passwort), stdin) != NULL) && checkEingabe(passwort))
...weil du in checkEingabe() ebenfalls auf NULL prüfst.

Ich mag das verschachtelte Konstrukt nicht:
1
if (a)
2
  if (b)
3
     if (c)
4
       if (d)
5
         ...
6
       else
7
         ...
8
     else
9
       ...
10
   else 
11
     ...
12
else
13
  ...
Ich hatte mal einen Entwickler der hat das über mehrere hundert LOC 
getrieben. Schrecklich. [sic]

So, das fällt mir so auf Anhieb auf. Zum Aufhübschen. Du hast es so 
gewollt! ;-)

Grüße, mikro

von Müller (Gast)


Lesenswert?

Hallo,
Ich kann sagen das fflush auf meinem Rechner daheim funktioniert bei w8. 
Aber nicht bei anderen Versionen, deswegen einfach zur Sicherheit.

Zu dem Einlesen, du hast recht.

Zu der anderen Geschichte, wenn die eingelesene String nur 1 Element 
hat, soll er nicht extra in die for Schleife reinspringen.

Ob ich die Abfragen verschachtele oder alles in Funktionen auslagere, 
spielt doch keine Rolle?! Außerdem finde ich das so besser lesbarer, ich 
hab ja schon Probleme dein Program vernünftig nachvollziehen zu können 
:) da wäre eine Art Beiblatt mit ablaufdiagramm echt nützlich. Erst 
recht wenn ich mir hier so manche Sachen anschaue, wo Leute wirklich 
jede Mini Operation in eine Funktion ausgelagern ;) schrecklich :)

Herzlichen Dank, werde morgen mal weiter korrigieren.

Gruß

von Mikro 7. (mikro77)


Lesenswert?

Müller schrieb:

> Ob ich die Abfragen verschachtele oder alles in Funktionen auslagere,
> spielt doch keine Rolle?!

Mir geht es um Lesbarkeit.

Wenn da viele if sind und ich für jedes if das else suchen muss, wird 
der Code schlecht lesbar (und dadurch fehleranfällig). Häufig bieten 
sich statt dessen early returns an.

Grüße, mikro

: Bearbeitet durch User
von beric (Gast)


Lesenswert?

Müller schrieb:

> // ErweiterteFunktion um 2 Strings unterschiedlicher Größe zu vergleichen
> bool checkEqual(char *string1, const char *string2)

Wegschmeißen und strcmp() benutzen.

von Karl H. (kbuchegg)


Lesenswert?

beric schrieb:
> Müller schrieb:
>
>> // ErweiterteFunktion um 2 Strings unterschiedlicher Größe zu vergleichen
>> bool checkEqual(char *string1, const char *string2)
>
> Wegschmeißen und strcmp() benutzen.

... und statt dessen diese Funktionalität
1
         ...   (fgets(passwort, sizeof(passwort), stdin) != NULL) && checkEingabe(passwort)) ...

in eine Funktion zusammenfassen, die
* mittels fgets eine Eingabe holt
* ein eventuell vorhandenes Newline aus der Eingabe entfernt
* überprüft ob die Eingabe nicht leer oder sonst irgendwie ungültige
  Zeichen enthält.


PS: Du willst deinem Benutzer keinen Hinweis geben, ob jetzt der 
Benutzername oder doch das Password falsch ist. Entweder beide sind 
richtig, dann wird er akzeptiert oder eines von beiden ist falsch und er 
kriegt eine in beiden Fällen gleiche Fehlermeldung. Denn du willst einem 
blind probierendem keinen Hinweis geben, dass er jetzt zufällig einen 
gültigen Benutzernamen erraten hat.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Müller schrieb:

> Zu der anderen Geschichte, wenn die eingelesene String nur 1 Element
> hat, soll er nicht extra in die for Schleife reinspringen.

Ganz ehrlich.
Machs erst mal richtig und kümmere dich dann um Optimierungen.
So oft wie du strlen aufrufst, ... da ist erst mal jegliche sogenannte 
Optimierung vollkommen für die Würscht.

> Ob ich die Abfragen verschachtele oder alles in Funktionen auslagere,
> spielt doch keine Rolle?!

Doch tut es. Nicht für den Rechner, aber für den Programmierer, der das 
alles irgendwann nicht mehr nachvollziehen kann.

> Außerdem finde ich das so besser lesbarer


Was ist an
1
....
2
3
   AccountDataValid  = FALSE;
4
   do {
5
     // auf Benutzereingabe warten,
6
     // feststellen ob sie gültige Zeichen enthielt war und
7
     // ob Benutzername und Password gültig sind
8
     // Wenn ja, dann gilt der Benuzer als eingeloggt
9
10
     getInput( "Benutzername", Username, sizeof( Username ) );
11
     getInput( "Password", Password, sizeof( Password ) );
12
13
     AccountDataValid = checkAccountValid( Username, Password );
14
15
     if( !AccountDataValid )
16
       printf( "Benutzername oder Password ungültig\n" );
17
18
   }  while( !AccountDataValid  );
19
20
   printf( "Welcome %s to XYZ System\n", Username );
21
....

jetzt besonders unleserlich?
Denk dir ein paar Füllwörter wie zb Artikel und sonstigen Kleinkram 
dazu, dann steht da mehr oder weniger im Kartext, was an dieser Stelle 
im Code passiert.

: Bearbeitet durch User
von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe noch ein paar Aktualisierungen vorgenommen, u.a. den Main Loop 
etwas "übersichtlicher" gestaltet, denke ich. Außerdem habe ich die 
Passwort Eingabe mit Sternchen versehen.

Nochmal kurz zur Info:
Es soll darauf hingewiesen werden, dass Nutzer ODER Passwort falsch 
sind, ebenso soll darauf hingewiesen werden, falls die Eingabe ansich 
nicht korrekt ist(nur Zahlen und Buchstaben erlaubt).

checkEqual habe ich verworfen, und mache jetzt alles in getNutzername 
und getPasswort, ich denke dass ist jetzt doch übersichtlicher oder?!

checkEingabe besteht weiterhin, ich denke das ist ok, oder?

@Karl Heinz

Dein Vorschlag ist toll, aber dann kann ich ja direkt bei 0 anfangen ;(

@Alle
Ich habe nicht vor später als Softwerker zu arbeiten =) Wie gesagt ich 
mache meinen Techniker in Elektrotechnik/Automatisierungstechnik auf 
Abendschule. Es interessiert mich nur.

Vielen Dank für die Hilfe, weiterhin ist Kritik erwünscht ;)

Lieben Gruß

von Karl H. (kbuchegg)


Lesenswert?

Müller schrieb:

> @Karl Heinz
>
> Dein Vorschlag ist toll, aber dann kann ich ja direkt bei 0 anfangen ;(

Ja, das soll vorkommen, wenn man sich selbst in die Ecke programmiert 
hat.

Mach dir nichts draus. Ist uns am Anfang allen so gegangen. Sieh es mal 
so: Wer mit einer Tätigkeit anfängt, kann nicht erwarten von Anfang an 
gleich alles einigermassen richtig zu machen. Das macht nichts. 
Entscheidend ist, dass man daraus lernt. Niemand von uns ist zur Welt 
gekommen und hat bei einer Problemstellung von Anfang an intuitiv die 
Richtung eingeschlagen bei der er
a) sich selbst das Leben möglichst einfach macht
b) auf Anhieb sieht, welche Aufteilung in Funktionen sinnvoll ist
c) gleichzeitig eine leicht lesbare Codeform erhält
Gerade am Anfang hängt auch viel daran, dass man auch experimentiert, 
wie diverse Änderungen den Code an sich verändern; ob er einfacher wird; 
ob er komplexer wird; ob er leichter lesbar wird, etc. ...


Und ganz ehrlich: soooo umfangreich ist das, was du bisher hast nun auch 
wieder nicht. Das ist schnell geändert und so wie das aussieht, kannst 
du die Übung gut gebrauchen.

: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

Karl H. schrieb:
...

Also, für einen C-Einsteiger, der freiwillig für sich selbst eine 
zusätzliche Aufgabe gesucht hat, fand ich das nicht übel. Nachgefragt 
und umgesetzt. Dass das nicht optimal ist, damit kann ich leben. Habe in 
der Berufswelt weit schlimmere Sachen gesehen.

von Karl H. (kbuchegg)


Lesenswert?

S. J. schrieb:
> Karl H. schrieb:
> ...
>
> Also, für einen C-Einsteiger, der freiwillig für sich selbst eine
> zusätzliche Aufgabe gesucht hat, fand ich das nicht übel.


Full Ack: Absolut nicht. So war das auch nicht gemeint.

Jetzt muss er nur noch das typische Neuling-'Problem' ablegen: einmal 
geschriebener Code ist in Stein gemeisselt und wird auf keinen Fall 
geändert.

von Müller (Gast)


Lesenswert?

Hallo zusammen,

ich werde mal schauen, ob ich heute mich hinsetze und nochmal von vorne 
anfange. Ich muss noch etwas anderes für die Schule fertig machen. 
Momentan sind ja Ferien und ich hab Urlaub, deswegen bin ich da rel. 
Flexibel ;)

Ich habe da noch ein paar Fragen:
- Mirko du verwendest in Deiner Musterlösung mehrmals 
statischefunktionen, welchen Vorteil bringt das im Bezug auf das 
Program? ( soll das vor externen Zugriffen schützen? )
- Du benutzt überhaupt kein printf, warum?? Die kann doch nicht unsicher 
sein oder? Die gibt's doch auch bestimmt bei Linux
- dieses Poll gibt's bei mir gar nicht, zumindestens meckert mein GCc 
wenn ich versuche die einzubinden.
- was ist das überhaupt? Klingt für mich frei übersetzt wie Umfrage ;)

Ich benutze codeblocks als Editor und als linker und Compiler aber das 
GCc von devcpp, fragt nicht warum, devcpp kompiliert Mir Programme, die 
in nem segfault enden, auch wenns nur hallo World ist.

Gruß

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

S. J. schrieb:
> Ich kenne leider keinen einfachen und zuverlässigen Weg einen
> Input-C-File-Stream nicht-blockierend und platformunabhängig
> (Win/DOS/Linux) zu "flushen".

Ich weiß nicht, wie Du darauf kommst, dass fflush(stdin) unter Linux den 
Input blockt oder Dir bei Redirection den Input zerstört.
1
$ cat /etc/issue
2
Ubuntu 11.10 \n \l
3
$ cat fflush.c
4
#include <stdio.h>
5
6
int
7
main (void)
8
{
9
    int ch;
10
11
    fflush (stdin);
12
    puts ("Input:");
13
14
    while ((ch = getchar ()) != EOF)
15
    {
16
        putchar (ch);
17
    }
18
    return 0;
19
}
20
$ cc fflush.c -o fflush && ./fflush
21
Input:
22
abc
23
abc
24
$ cat hello.txt
25
Hello, World
26
$ ./fflush <hello.txt
27
Input:
28
Hello, World

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

S. J. schrieb:
> Rolf M. schrieb:
>> Das liegt einfach daran, daß es auch Betriebssysteme gibt, bei denen
>> diese Puffer vor dem Programm verborgen sind
>
> Naja, der C-File-Stream (FILE) ist Teil der C-Lib und nicht Teil des BS.
> Er könnte also immer von der Lib geflushed (gelöscht) werden.

Das Problem liegt darin, dass es in C kein Terminal und keine Tastatur 
gibt. In C wird alles als Stream angesehen, egal was da dahinter steckt.
Ausgabeseitig ist es noch recht einfach einen flush anzuleiern. Es 
werden einfach alle Output Buffer geleert und einer möglicherweise 
dahinter stehenden Hardware übermittelt. Egal was die dann damit macht.
Aber eingabeseitig ist das nicht so einfach. Denn auf eine UART hast du 
kaum einen Einfluss. Während die Softwarebuffer geleert werden trudelt 
schon das nächste Zeichen ein. Was soll mit dem geschehen? Wie verhalten 
sich Tastaturbuffer, die in Terminals verbaut sind? Selbiges mit 
Netzwerk.

Daher geht C den kleinsten gemeinsamen Nenner und definiert fflush nur 
für Ausgabestreams. Ob und wie ein fflush auf einen Eingabestream wirkt, 
das wird vom C Standard weder definiert noch gefordert.

von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:

> $ cc fflush.c -o fflush && ./fflush
> Input:
> abc
> abc
> $ cat hello.txt
> Hello, World
> $ ./fflush <hello.txt
> Input:
> Hello, World
> [/pre]

Wie genau unterscheidet sich dieses Verhalten jetzt davon, dass fflush 
nichts tut? Das ist mit diesem Test nämlich nicht erkennbar.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl H. schrieb:
> Wie genau unterscheidet sich dieses Verhalten jetzt davon, dass fflush
> nichts tut? Das ist mit diesem Test nämlich nicht erkennbar.

Es tut auch nichts. Wenn ich eingebe:
1
$ sleep 5; ./fflush
2
abc
3
Input:
4
abc
5
def
6
def

... dann wird "abc" ebenso ausgegeben, obwohl ich es vor dem Aufruf von 
fflush(stdin) eingegeben habe. Der Input wird also nicht geleert.

Es ging mir nur um die falsche Aussage, dass fflush(stdin) den Input 
blockieren (bei Terminal) bzw. den Inhalt leeren (bei Redicretion) soll. 
Das stimmt so nicht, denn es tut unter Linux.... einfach nichts! :-)

Unter Unix/Linux muss man da mit termios bzw. ioctl() dran. Dann geht 
das sicher.

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:

> Es ging mir nur um die falsche Aussage, dass fflush(stdin) den Input
> blockieren (bei Terminal) bzw. den Inhalt leeren (bei Redicretion) soll.
> Das stimmt so nicht, denn es tut unter Linux.... einfach nichts! :-)

OK.
Dann blasen wir ins selbe Horn.
Das Verhalten von fflush ist nicht garantiert. Auf deinem System tut es 
nichts, auf so manchem Windows System macht es das gewünschte. 
Dazwischen liegt noch eine weite Spanne, was sonst noch so alles 
passieren könnte.

von Mikro 7. (mikro77)


Lesenswert?

Frank M. schrieb:
> Ich weiß nicht, wie Du darauf kommst, dass fflush(stdin) unter Linux den
> Input blockt oder Dir bei Redirection den Input zerstört.

Habe ich nicht behauptet.

> S. J. schrieb:
>> Ich kenne leider keinen einfachen und zuverlässigen Weg einen
>> Input-C-File-Stream nicht-blockierend und platformunabhängig
>> (Win/DOS/Linux) zu "flushen".

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Nur der Vollständigkeit halber: So gehts unter Linux:
1
$ cat fflush.c
2
#include <stdio.h>
3
#include <termios.h>
4
#include <unistd.h>
5
6
int
7
main (void)
8
{
9
    int ch;
10
11
    (void) tcflush (fileno(stdin), TCIFLUSH);
12
13
    puts ("Input:");
14
15
    while ((ch = getchar ()) != EOF)
16
    {
17
        putchar (ch);
18
    }
19
20
    return 0;
21
}
22
$ cc fflush.c -o fflush && ./fflush
23
Input:
24
abc
25
abc
26
def
27
def
28
$ sleep 5; ./fflush
29
abc
30
Input:
31
def
32
def

Hier wird der Input ("abc"), der vor dem tcflush()-Call eingetippt 
wurde, vorher gelöscht und daher auch nicht wieder ausgegeben.

Trotzdem geht der Input bei Redirection nicht verloren:
1
$ ./fflush <hello.txt
2
Input:
3
Hello, World

Das ist natürlich alles Unix-/Linux-spezifisch. Mit select() bekäme man 
auch noch eine portable Lösung hin, die unter Unix/Linux und Windows 
laufen würde. Aber lohnt das den Aufwand?

: Bearbeitet durch Moderator
von Mikro 7. (mikro77)


Lesenswert?

Karl H. schrieb:

> Das Problem liegt darin...

Ist klar. Das von mir "geforderte" flush hat sich (erstmal) nur auf den 
buffer in der C-Lib bezogen.
-> Beitrag "Re: C - Char array löschen"

Karl H. schrieb:

> Daher geht C den kleinsten gemeinsamen Nenner und definiert fflush nur
> für Ausgabestreams. Ob und wie ein fflush auf einen Eingabestream wirkt,
> das wird vom C Standard weder definiert noch gefordert.

Nicht ganz.

Für seekable files ist es in POSIX.1-2008 definiert.
-> Beitrag "Re: C - Char array löschen" (hilft beim 
Terminal aber nicht)

von Mikro 7. (mikro77)


Lesenswert?

> ... Mit select() bekäme man auch noch eine portable Lösung hin,
> die unter Unix/Linux und Windows laufen würde...

Denke eher nicht.
-> Beitrag "Re: C - Char array löschen"

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

S. J. schrieb:
> Habe ich nicht behauptet.

Dann habe ich das missverstanden, sorry.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

S. J. schrieb:
>> ... Mit select() bekäme man auch noch eine portable Lösung hin,
>> die unter Unix/Linux und Windows laufen würde...
>
> Denke eher nicht.
> -> Beitrag "Re: C - Char array löschen"

Viel zu kompliziert, wo hast Du denn den Source aufgetrieben?

Hier Lösung mit select():
1
$ cat select.c
2
#include <stdio.h>
3
#ifdef unix // Unix & Linux
4
# include <unistd.h>
5
# include <time.h>
6
# include <netdb.h>
7
#else // Windows
8
# include <Windows.h>
9
#endif
10
11
void
12
flush_input (void)
13
{
14
    char            buf[1];
15
    struct timeval  tv;
16
    fd_set          fdset;
17
    int             max_fd;
18
    int             select_rtc;
19
20
    tv.tv_sec   = 0;
21
    tv.tv_usec  = 0;
22
    max_fd      = fileno(stdin);
23
24
    while (1)
25
    {
26
        FD_ZERO(&fdset);
27
        FD_SET (fileno(stdin), &fdset);
28
29
        select_rtc = select (max_fd + 1, &fdset, (fd_set *) 0, (fd_set *) 0, &tv);
30
31
        if (select_rtc <= 0)
32
        {
33
            break;
34
        }
35
        read (fileno(stdin), buf, 1);
36
    }
37
}
38
39
int
40
main (void)
41
{
42
    int ch;
43
44
    flush_input ();
45
    puts ("Input:");
46
47
    while ((ch = getchar ()) != EOF)
48
    {
49
        putchar (ch);
50
    }
51
52
    return 0;
53
}
54
55
$ cc select.c -o select && ./select
56
Input:
57
abc
58
abc
59
def
60
def
61
$ sleep 5; ./select
62
abc
63
Input:
64
def
65
def

EDIT:
Das Prüfen mittels FD_ISSET() habe ich mir mal gespart, da nur ein fd 
gecheckt wird.

: Bearbeitet durch Moderator
von Mikro 7. (mikro77)


Lesenswert?

Frank M. schrieb:

> Viel zu kompliziert, wo hast Du denn den Source aufgetrieben?

Im Sinne der Aufgabenstellung des TS.

> Hier Lösung mit select():

Das ist ohne Benutzung des C-file-streams (STDIN).

Auslesen/Select auf den FD klappt, das ist klar.

> You could also use direct
> non-blocking file descriptor access (poll/read) -- and don't use the
> stdin-file-stream at all.

Unter Windows habe ich in Erinnerung (lange ist's her) das Anonymous 
Pipes benutzt werden, die nicht asynchron gelesen werden können.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

S. J. schrieb:
> Das ist ohne Benutzung des C-file-streams (STDIN).

Du meinst den stdio filepointer stdin - um mal bei der Unix-Sprache zu 
bleiben.

> Auslesen/Select auf den FD klappt, das ist klar.

Den fd holt man sich portabel mit fileno(stdin), siehe oben. Ich habe 
KEIN fd=0 benutzt.

von Mikro 7. (mikro77)


Lesenswert?

Frank M. schrieb:

> Den fd holt man sich portabel mit fileno(stdin), siehe oben. Ich habe
> KEIN fd=0 benutzt.

...und hast zum Lesen nicht den "stdio filepointer stdin" benutzt.

Das Problem, dass im C-Lib buffer des "stdio filepointer stdin" noch 
character liegen können, hast du also gar nicht abgehandelt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

S. J. schrieb:
> Das Problem, dass im C-Lib buffer des "stdio filepointer stdin" noch
> character liegen können, hast du also gar nicht abgehandelt.

Da ist was dran. Vor dem ersten getchar() kann jedoch kein Zeichen im 
stdio buffer liegen. ;-)

Es bleibt jedenfalls dabei, was Karl Heinz, Du und ich auch schon 
sagten: man kann den stdio Input-Buffer nicht leeren.

Ich habs übrigens eben mal unter Windows mit fflush(stdin) ausprobiert: 
Es funktioniert (im Gegensatz zu dem oben behaupteten) auch NICHT unter 
Windows. Getestet mit Microsoft Visual C++ 2010 Express.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Im Helpfile meines Compilers (Watcom C) steht:

"If the file fp is open for output or update, the fflush function causes 
any unwritten data to be written to the file.  If the file fp is open 
for input or update, the fflush function undoes the effect of any 
preceding  ungetc operation on the stream.  If the value of fp is NULL, 
then all files that are open will be flushed."

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Joachim D. schrieb:
> If the file fp is open
> for input or update, the fflush function undoes the effect of any
> preceding  ungetc operation on the stream.

Okay, das ist aber nicht, was hier generell gewünscht wird. In der 
Watcom-Beschreibung geht es darum, ein per ungetc() zurückgestelltes 
Zeichen vom input-Buffer zu nehmen.

Nicht gerade das, was man in der Regel braucht ;-)

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Frank M. schrieb:
> Nicht gerade das, was man in der Regel braucht ;-)

Eben ;)

In der Praxis keine Auswirkung auf den Eingabepuffer ...

: Bearbeitet durch User
von Müller (Gast)


Lesenswert?

Hallo,
Könnte man nicht den Eingabebuffer nicht "umleiten"? Quasi ich definiere 
in Meinem Programm eine Char buffer, sage dann über setbuf(stdin, 
persönlicherbuffer), und wenn ich leeren will dann Buffer über memset 
löschen? Gehts das theoretisch? Bin grad nicht am Rechner, sonst würde 
ich es mal probieren.

Hruß

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Müller schrieb:
> Könnte man nicht den Eingabebuffer nicht "umleiten"?

Kann sein. Aber irgendwann kommst Du auch dahinter, dass die stdio-lib 
für Console-Input/Output gar nicht so geeignet ist und wirst nach 
Alternativen schauen.

von Müller (Gast)


Lesenswert?

Hallo,

habe es mal getestet, scheint zumindestens bei zu funktionieren...
1
#include <stdio.h>
2
3
int main()
4
{
5
    char buffer1[10];
6
    char buffer2[10];
7
8
    // Persönlicher Buffer setzen
9
    char buffer3[BUFSIZ];
10
    setbuf(stdin, buffer3);
11
12
    // Eingabe 1
13
    printf("Gib etwas ein:\n");
14
    fgets(buffer1, sizeof(buffer1), stdin);
15
16
    // Löschen und wieder setzen
17
    memset(buffer3, 0, sizeof(buffer3));
18
    setbuf(stdin, buffer3);
19
20
    // Eingabe 2
21
    printf("Gib noch etwas ein:\n");
22
    fgets(buffer2, sizeof(buffer2), stdin);
23
24
    // Ausgabe
25
    printf("Buffer 1: %s\n", buffer1);
26
    printf("Buffer 2: %s\n", buffer2);
27
}

Ausgabe ohne Löschen
1
Gib etwas ein:
2
0123456789123456
3
Gib noch etwas ein:
4
Buffer 1: 012345678
5
Buffer 2: 9123456
6
7
8
Process returned 19 (0x13)   execution time : 8.782 s
9
Press any key to continue.

Ausgabe mit Löschen:
1
Gib etwas ein:
2
0123456789123456
3
Gib noch etwas ein:
4
TEST
5
Buffer 1: 012345678
6
Buffer 2: TEST
7
8
9
Process returned 16 (0x10)   execution time : 31.111 s
10
Press any key to continue.

Gruß

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Müller schrieb:
> habe es mal getestet, scheint zumindestens bei zu funktionieren...

Zwei Sachen habe ich anzumerken:
1
> #include <stdio.h>
2
> 
3
> int main()
4
> {
5
>     ...
6
>     char buffer3[BUFSIZ];

Wichtig: Der Buffer muss über die Lebenszeit von main() hinaus 
existieren. Das hat damit zu tun, dass exit() je nach Betriebssystem 
noch mit dem Buffer arbeitet.

Daher:
1
     static char buffer3[BUFSIZ];

Sieht zwar in diesem Kontext komisch aus, ist aber notwendig.

Auszug aus der Dokumentation dazu:

"You must make sure that the space that buf points to still exists by 
the time stream is closed, which also happens at program termination."

1
>     // Ausgabe
2
>     printf("Buffer 1: %s\n", buffer1);
3
>     printf("Buffer 2: %s\n", buffer2);
4
> }

Hier fehlt ein "return 0;" am Ende.

Deshalb sieht man so willkürliche Zahlen bei Dir:

   Process returned 19 (0x13)   execution time : 8.782 s

bzw.

   Process returned 16 (0x10)   execution time : 31.111 s

: Bearbeitet durch Moderator
von apr (Gast)


Lesenswert?

Frank M. schrieb:
> Hier fehlt ein "return 0;" am Ende.
>
> Deshalb sieht man so willkürliche Zahlen bei Dir:
>
>    Process returned 19 (0x13)   execution time : 8.782 s
>
> bzw.
>
>    Process returned 16 (0x10)   execution time : 31.111 s

5.1.2.2.3 Program termination
[…] reaching the } that terminates the main function returns a value of 
0. […]

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

apr schrieb:
> Frank M. schrieb:
>>    Process returned 19 (0x13)   execution time : 8.782 s
>>    Process returned 16 (0x10)   execution time : 31.111 s
>
> 5.1.2.2.3 Program termination
> […] reaching the } that terminates the main function returns a value of
> 0. […]

Das mag für C11, C99 und C++ gelten, sonst aber nicht. Wie anders 
möchtest Du die willkürlichen Return-Werte 19 bzw. 16 erklären?

von Frank (Gast)


Lesenswert?

Frank M. schrieb:
> Das mag für C11, C99 und C++ gelten, sonst aber nicht. Wie anders
> möchtest Du die willkürlichen Return-Werte 19 bzw. 16 erklären?

Das liegt aber doch an dem Compiler, den du verwendest. Wenn der so 
alt ist, dass er nicht einmal C99 kennt, kannst du das in diesem Fall 
nicht dem Quellcode anlasten. Selbst der MS-Compiler macht hier keine 
Probleme, obwohl der bei C nicht gerade immer ... ganz up-to-date ist 
(Euphemismus).
Trotzdem ist "return 0" natürlich besser (besonders, wenn man ansonsten 
gar keine C99 oder C11-Features verwendet).

@Müller

Ich habe mir den Code nicht im Detail angesehen, aber mir sind ein paar 
Kleinigkeiten aufgefallen:

1. Du schreibst zu viele Kommentare dieser Art:

// Variablen
int zeichen;

// Länge 0?
if(length < 1)

// Länge 1?
if(length == 1

// Weiterzählen
counter++;

Das steht ja alles schon da.

2. Benutze anstelle von "8", "13" etc. besser Konstanten.

// Schleifendurchlauf Bei ENTER beenden
while((zeichen = getch()) != 13)
{
  // Wurde backspace gedrückt && vorher ein Zeichen eingegeben?
  if(zeichen == 8 && counter > 0)
  {

Auch auf den ersten Kommentar könnte man dann verzichten. Wenn statt 
"counter" ein spezifischerer Name verwendet werden würde, auch gleich 
auf den zweiten.

3. Kleiner "Trick" (ist aber nicht auf meinem Mist gewachsen)

nutzername[strcspn(nutzername, "\n")] = '\0';
bzw.
nutzername[strcspn(nutzername, "\r\n")] = '\0';

Müller schrieb:
> Erst recht wenn ich mir hier so manche Sachen anschaue, wo Leute wirklich
> jede Mini Operation in eine Funktion ausgelagern ;) schrecklich :)

Wir reden hier doch über prozedurale Programmierung ... ;-)
Ich hätte gar kein Problem damit, z.B.

void removeNewline(char* buffer)
{
  buffer[strcspn(buffer, "\n")] = '\0';
}

zu schreiben, wenn der Code dadurch lesbarer wäre und/oder die Funktion 
mehrmals benutzt werden würde.


BTW: Die Gesamtstruktur deines Programms sieht inzwischen deutlich 
besser aus als in der ersten Version, da ist auch mal ein Lob 
angebracht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank schrieb:
> Frank M. schrieb:
>> Das mag für C11, C99 und C++ gelten, sonst aber nicht. Wie anders
>> möchtest Du die willkürlichen Return-Werte 19 bzw. 16 erklären?
>
> Das liegt aber doch an dem Compiler, den du verwendest.

Nein, die Return-Werte hat der TO gepostet.
Und ich meine herausgelesen zu haben, dass TO unter Windows seine 
Sources kompiliert.

> Wenn der so
> alt ist, dass er nicht einmal C99 kennt, kannst du das in diesem Fall
> nicht dem Quellcode anlasten. Selbst der MS-Compiler macht hier keine
> Probleme, obwohl der bei C nicht gerade immer ... ganz up-to-date ist
> (Euphemismus).

Soviel ich weiß, kann der MS-Compiler kein C99. Und erst recht kein C11.

> Trotzdem ist "return 0" natürlich besser (besonders, wenn man ansonsten
> gar keine C99 oder C11-Features verwendet).

Eben, damit bricht man sich keinen Zacken aus der Krone.

von Frank (Gast)


Lesenswert?

Frank M. schrieb:
> Nein, die Return-Werte hat der TO gepostet.

Hatte ich übersehen (war bei dir auch nicht "gequotet").

> Soviel ich weiß, kann der MS-Compiler kein C99.

Doch, neuerdings schon - zumindest größtenteils:

"However, with the introduction of Visual C++ 2013 Microsoft implemented 
a limited subset of C99, then Visual C++ 2015 achieved a level of the 
C99 standard compliance that is similar to other C compilers."

https://en.wikipedia.org/wiki/C99

von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich hab mich jetzt nochmal hingesetzt, und neu angefangen. Wie findet 
ihr es?

Lieben Gruß

von Müller (Gast)


Angehängte Dateien:

Lesenswert?

Müller schrieb:
> Hallo zusammen,
>
> ich hab mich jetzt nochmal hingesetzt, und neu angefangen. Wie findet
> ihr es?
>
> Lieben Gruß

Sry kleiner Fehler drinne gehabt:
1
            else if(x > daten.buffer)
muss natürlich
1
            else if(x > daten.buffer-2)
heißen.

von Moral Apostell (Gast)


Lesenswert?

Ich merke gerade, dass ich noch 3 Return False in Return true ändern 
muss ;(

Sorry Leute!

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.