C Generelle Frage: Sichere Eingabe

psi24

Cadet 4th Year
Registriert
Dez. 2012
Beiträge
75
]Hallo zusammen,

ich habe vor ein paar Tagen angefangen C zu lernen (Programmierkenntnisse vorhanden: C++ basics, C# schon ein bisschen mehr) und muss ganz ehrlich gestehen, dass ich C super finde. Obwohl alle tutorials scanf() als Standart für die Eingabe durch die Konsole wählen, was ja alles andere als sicher ist. Hab mich hier und hier versucht darüber zu informieren, aber die 'sichere' Eingabe mit sscanf() hat auch seine Macken.

Deshalb wollte ich ein für alle Male klären, wie denn eine sichere Eingabe aussieht.

Die Methode aus dem ersten link lässt sich nähmlich genauso mit der Eingabe eines char veräppeln:

Code:
#include <stdio.h>
#include "functions.h"

int main()
{
	//
	int a, b;
	char buf[256];
	unsigned int frame, field, tmp;
	printf("Seitenlänge des Rahmens(wird quadriert): \n");
	fgets(buf, 255, stdin);
	sscanf(buf, "%d", &a);
	printf("Feldgrößse (wird quadriert): \n");
	fgets(buf, 255, stdin);
	sscanf(buf, "%d", &b);
	tmp = a;
	field = b;
	if (tmp > field)
	{
		frame = tmp;
	}
	else if (field > tmp)
	{
		frame = field;
		field = tmp;
	}
	else
	{
		field = 0;
	}
	printf("Rahmen: %d\nFeld: %d\n", frame, field);

	if (frame % 2 == 0 && field % 2 == 0)
	{
		// both even
		printf("Beide Zahlen gerade!\n");
		printFrameAndField(frame, field);
	}
	else if (frame % 2 != 0 && field % 2 != 0)
	{
		//	both odd
		printf("Beide Zahlen ungerade!\n");
		printFrameAndField(frame, field);
	}
	else
	{
		printf("Beide Zahlen muessen entweder gerade oder ungerade sein!\n");
	}
	return 0;
}

eigentlich hatte ich gehofft, dass die Typ Konvertierung in unsigned int, das ausschließt.
 
Zuletzt bearbeitet:
Eine sichere Eingabe sieht z.B. so aus:

Code:
char *get_line(FILE *fp) {
    size_t len = 0, size = 0;
    char *buf  = NULL;

    do {
        size += MAXBUF;
        buf = realloc(buf, size);
        if(fgets(buf + len, MAXBUF, fp) != NULL)
            len = strlen(buf);
    } while (!feof(fp) && buf[len - 1]!='\n');

    if(len == 0) {
        free(buf);
        return NULL;
    }

    buf[len - 1] = '\0';

    return buf;
}

Schnell hingeklatscht, nicht getestet, könnte zum Weltuntergang führen. Keinerlei Garantien!
 
Im Ernst? C bietet keinen Standard für eine halbwegs sichere Eingabe? :freak:

Vielen Dank trotzdem für dein snippet, asdfman :daumen:

Allerdings muss ich gestehen, dass ich die Funktion von 'len' in deiner Lösung, die sich auf eine Datei bezieht btw (?), nicht ganz durchschaue :(

Code:
 if(fgets(buf + len, MAXBUF, fp) != NULL)
len = strlen(buf);

und wieso bei jeder Iteration size um den Wert von 'MAXBUF' erhöhen? :confused_alt:

Ich hatte mir schon gedacht, dass es darauf hinauslaufen wird, eine Wrapperfunktion zu schreiben, aber selbst das folgende Konzept(!!!) erscheint mit auch nicht sicher genug:

(für numerischen input)
Eingabe als string ein lesen (mit gets?)
Parsen?

Eventuell eine wrapperfunktion, der man als parameter übergeben kann, welcher Zieldatentyp erwartet werden soll?

Werde morgen mal eine Konzeption nachliefern


psi24
 
Die Funktion liest nicht aus einer Datei, sondern einem Stream (der auf eine Datei zeigen kann). Als Argument kann man also auch einfach stdin benutzen.

len enthält die Länge des Strings, der schon gelesen wurde. Es ist dafür da, fgets() zu sagen, wo es mit dem nächsten Block anfangen soll.
size ist die Größe des Puffers, in den fgets() schreibt. Er wird bei Bedarf um den Wert der Konstanten MAXBUF vergrößert. Keine schöne Wahl für den Namen, zugegeben. Aber vielleicht fällt dir ja ein besserer ein ;(

Mit gets() Strings einzulesen ist aus dem selben Grund schlecht, wie scanf() zu benutzen. Wenn du einen String in eine Zahl umwandeln willst, gibt es atoi().
 
Oh, tut mir leid, meinte fgets().
Okay, vielen Dank für die schnelle Erläuterung. Ich denke ich werde die Funktion von dir mal testen und ein wenig mit parameterausgaben spicken, wie genau sich die Parameter verändern. Zum ändern muss nur stdin übergeben und die Rückgabetypen geändert werden?
 
Du gibst als Parameter den Stream an, aus dem du lesen willst und bekommst einen String zurück. Mit dem machst du dann, was du willst und gibst ihn hinterher wieder frei (mit free()).
 
Ich hab gerade versucht, sie umzuschreiben auf stdin, aber bei dieser Zeile stellt sich mir eine Frage:
Code:
} while (!feof(fp) && buf[len - 1]!='\n');

Theoretisch (meine ich zumindest), bräuchte ich die Schleife ja nicht zwingend, wenn das ganze nur von stdin ließt. Doch dann untergrabe ich wahrscheinlich den Ansatz deiner Lösung, dass der Puffer stets ausreichend ist, korrekt?

Tut mir leid, will nicht, dass ihr wahnsinnig werdet :D
 
Ich wird nicht ganz schlau, was du da weglassen willst, wenn du das while wegmachst, dann hast maximal MAXBUF viel gelesen. Nicht so der Burner. Die Schleife macht zweierlei, zuerst testet sie, ob es einfach nicht weitergeht (feof) oder ob man das genwünschte Zeichen (\n = Return) gefunden hat, wenn eins von beidem zutrifft, dann muss man nicht mehr weiterlesen.

Auch stdin kann EOF sein, (Unter Windows wie Linux via Pipe (| oder <) oder mittels STRG+D).
 
Verstehe auch nicht, warum er überhaupt etwas ändern will. Eine allgemein verwendbare Funktion in einen Spezialfall umzubauen mag ja je nach individuellen Umständen angebracht sein, hier gewinnt man dadurch aber wirklich nichts, weil der Mechanismus der Funktion vollständig erhalten bleiben muss. Warum dann dem Anwender die Flexibilität wegnehmen?

€:
OP: Verstehe ich dich richtig, dass du aus meiner Funktion, die aus jedem beliebigen Stream lesen kann eine machen willst, die nur noch aus stdin lesen kann? Wenn ja, was versprichst du dir davon?
 
Zuletzt bearbeitet:
Das stdin auch in Kombination mit EOF verwendet werden kann, wusste ich nicht, danke für den Hinweis.

Der Sinn des Zusammenfassens war, die Funktion zu vereinfachen. Bin davon ausgegangen, dass man so den Vektor von schlechtem input verkleinern könnte.


Edit://
Hab es ans laufen bekommen, werde mal etwas mit schlechtem input attakieren :p
Nochmals vielen dank für da snippet asdfman
 
Zuletzt bearbeitet: (Fehler gefunden)
strlen() ist in string.h deklariert. Ich finde deinen Mangel an Initiative beklagenswert.
Die Deklaration von get_line() in deinem Header entspricht nicht der Definition.
Deine main() ist völlig ok, aber du solltest dir angewöhnen, wie ich bereits sagte, free() zu verwenden. Ich gehe davon aus, dass du das nicht absichtlich weggelassen hast, weil du weißt, dass es hier ausnahmsweise geht, sondern dass du mich einfach ignoriert hast.

Zuletzt empfehle ich, wenn du so Codeschnipsel hin geworfen bekommst, wie von mir, ihn zu lesen und zu verstehen und wenn das Verständnis schwer fällt, nachzufragen. Ich mag vielleicht häufig überheblich klingen, aber das ist nur so mein Duktus und du solltest das nicht persönlich nehmen. Wenn du dich ernsthaft mit Code beschäftigt hast und du ihn trotzdem nicht verstehst, ist mir tausendmal lieber, du fragst nochmal, als dass du ihn einfach so benutzt. Denn das wäre der Pfad des PHP-Entwicklers.

Ich bin kein allwissender Programmiergott und man kann die Funktion mit Sicherheit viel besser machen, aber sie für stdin spezialisieren ist nicht zielführend und ich finde es schade, dass du einfach drauflos "verbessern" wolltest, ohne zu wissen was du tust, oder was ich mit der Funktion überhaupt gemeint habe. Denn wenn du dir erstmal diese wichtige(!) Arbeit gemacht hättest, hättest du dir unnötige Arbeit bei deiner Verbesserungsidee erspart und am Ende viel mehr davon gehabt.
 
Zuletzt bearbeitet:
Zurück
Oben