const variablen + sichere Zeichenketten Eingabe



  • 1. const Variablen sollten nach meinem Verständnis schreibgeschützt sein, warum kann ich sie also in meinem Programm trotzdem ändern? Was ist dann der Sinn von const Variableb bzw. wo ist mein Denkfehler 😕

    2. Ich will die Eingabe einer Zeichenkette mithilfe einer symbolischen Konstante auf eine bestimmte Zeichenlänge begrenzen. Mein Programm meldet mir zwar zurück, wenn die Eingabe zu lang war, dann wurde aber ja schon in nicht reservierten Speicher geschrieben.
    Zwar würde

    scanf("%6s", zeichenkette);
    

    funktionieren, es soll aber von der symbolischen Konstante abhängig sein. Wie bekomme ich das hin?

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LAENGE 6
    
    int leseZeichenkette(const char *zeichenkette){ //Das const einfach wegdenken
    	printf("\nEingabe: ");
    	scanf("%s", zeichenkette);
    	if (strlen(zeichenkette) > MAX_LAENGE - 1){
    		while (getchar() != '\n'){} 
    		printf("\n--Eingabe zu lang--\n");
    		return -1;
    	}
    	return 0;
    }
    
    int main(){
    	const char c[MAX_LAENGE]; //Das const ebenfalls einfach wegdenken
    
    	while (leseZeichenkette(c) == -1){}
    	printf("\n%s", c);
    
    	return 0;
    }
    

    Vielen Dank schonmal im Voraus!


  • Mod

    KJoke schrieb:

    1. const Variablen sollten nach meinem Verständnis schreibgeschützt sein, warum kann ich sie also in meinem Programm trotzdem ändern? Was ist dann der Sinn von const Variableb bzw. wo ist mein Denkfehler 😕

    Const ist kein physikalischer Schreibschutz, sondern ein Mechanismus, mit dem der Compiler Logikfehler in deinem Programm erkennen kann. Mit hinreichend Casts oder Ähnlichem kannst du diese Sicherheitsmechanismen natürlich außer Kraft setzen. Ob das dann tatsächlich funktioniert, ist aber undefiniert.

    2. Ich will die Eingabe einer Zeichenkette mithilfe einer symbolischen Konstante auf eine bestimmte Zeichenlänge begrenzen. Mein Programm meldet mir zwar zurück, wenn die Eingabe zu lang war, dann wurde aber ja schon in nicht reservierten Speicher geschrieben.
    Zwar würde

    scanf("%6s", zeichenkette);
    

    funktionieren, es soll aber von der symbolischen Konstante abhängig sein. Wie bekomme ich das hin?

    Für scanf gibt es (im Gegensatz zu printf) keinen solchen Mechanismus. Du wirst den Formatstring vorher selbst zusammen basteln müssen. Falls die Größe dynamisch zur Laufzeit ist, mittels sprintf. Falls die Größe zur Compilezeit feststeht durch passende Präprozessormakros.



  • In diesem Fall kannst du fgets verwenden:

    fgets(zeichenkette, MAX_LAENGE, stdin);
    

  • Mod

    icarus2 schrieb:

    In diesem Fall kannst du fgets verwenden:

    Macht aber was anderes als das scanf-Format %s. fgets trennt an newlines (und schreibt das newline mit in das Ergebnis), %s trennt an Whitespace (der nicht mit gelesen wird). Kann natürlich trotzdem sein, dass es für den Threadersteller die passende Lösung ist; ich habe schon oft gesehen, dass Leute %s benutzen, wenn sie einen gets-Effekt möchten. Ab C11 gibt es übrigens ein char *gets_s( char *str, rsize_t n ); in Anlehnung an das (abgeschaffte) gets .



  • KJoke schrieb:

    1. const Variablen sollten nach meinem Verständnis schreibgeschützt sein, warum kann ich sie also in meinem Programm trotzdem ändern? Was ist dann der Sinn von const Variableb bzw. wo ist mein Denkfehler 😕

    Du kannst const-Variablen nicht direkt ändern. Du kannst allerdings durch einen Cast das const wegbekommen. Dadurch kannst du dir selbst in den Fuß schießen, das sollte klar sein. Es kommt hierbei zu undefinertem Verhalten, falls das Objekt ursprünglich const ist.

    In deinem Fall ist kein Cast weit und breit zu sehen, allerdings ist scanf eine Funktion mit variabler Parameterliste. Nur der erste Parameter ist als const char* festgelegt, die anderen Parameter sind beliebig. Du gibst also deinen const-Pointer rein und auf der anderen Seite holt scanf einen nicht-const-Pointer raus, ohne dass es dazwischen einen Check gibt.



  • SeppJ schrieb:

    Ab C11 gibt es übrigens ein char *gets_s( char *str, rsize_t n ); in Anlehnung an das (abgeschaffte) gets .

    Ist ein optionaler Teil und der damaligen Reaktion anderer Libc-Entwickler zufolge wird das wohl auch nirgends anders implementiert werden. Zumindest nicht als Teil der jeweiligen libc proper. Denkbär wäre allerdings eine gesonderte Library zur Implementierung von Annex K.

    Grad nochmal gegoogelt und zumindest für die GNU libc wurden vor einem halben Jahr auf libc-alpha doch nochmal Patches diskutiert. Im Sourcetree findet sich aber immer noch keinerlei Erwähnung dieser Funktionen. Selbst wenn sich das ändert, bleiben immer noch andere C-Librarys, die Annex K wohl nie implementieren werden.

    Ist auch ein bisschen komisch, wie das ganze Zeug in C11 (wenn auch optional) gelandet ist...



  • Danke für die Antworten!

    Ok, ich hatte nur gehofft es wäre etwas schwerer mir selbst ins Bein zu schießen...zumindest brauche ich const nicht allzu oft :p

    fgets wäre die Lösung, darf/soll ich aber nicht verwenden, weil's in meinen Vorlesungen nicht drankam (warum auch immer, nachdem scanf alles andere als sicher ist..) deswegen habe ichs jetzt so gelöst:

    #define MAX_LAENGE 5
    
    int leseZeichenkette(char *zeichenkette){
    	int i;
    	printf("\nEingabe: ");
    	for (i = 0; i < MAX_LAENGE; i++)
    	{
    		zeichenkette[i] = getchar();
    		if ((i == (MAX_LAENGE - 1)) && (zeichenkette[i] != '\n')){
    			printf("\n--Eingabe zu lang--\n");
    			while (getchar() != '\n'){}
    			return -1;
    		}
    		if (zeichenkette[i] == '\n'){
    			zeichenkette[i] = '\0';
    			break;
    		}
    	}
    	return 0;
    }
    

    Tut was es soll und ist nicht übermäßig viel Code 🙂


  • Mod

    Es gibt auch so etwas wie Compilerwarnungen. Ein guter Compiler wird dich warnen, wenn du versuchst eine Konstante über scanf zu modifizieren, wenn du ihn nur lässt.

    Deine Funktion finde ich verbesserungsfähig. Vorschläge:
    -Irgendetwas auszugeben sollte nicht Aufgabe eine Lesefunktion sein
    -Die maximale Länge sollte ein Parameter der Funktion sein, kein globales Makro.
    -Der Fall, dass die maximale Länge erreicht wird, gefällt mir nicht (nicht nur wegen der Ausgabe). Warum nicht einfach aufhören, wenn die maximale Länge erreicht wurde? Was passiert, wenn niemals ein '\n' kommt (Dateiende)? Außerdem würde ich das nicht als Lesefehler ansehen.
    -Apropos Fehler: Ich würde Rückgabewerte so nutzen wie in der Standardbibliothek. Die ist schon recht gut durchdacht und viele Programmierer sind damit vertraut.

    P.S.: Es gibt auch die Möglichkeit mit scanf bis zum nächsten newline zu lesen, inklusive Längenangabe: scanf("%1234[^\n]", string);



  • Habe ich ausprobiert, bei gcc kam mit -Wall auch eine Warnung, bei VisualStudio2013 kam aber leider keine (Ich weiss allerdings auch nicht, ob bzw. wo ich Compilerwarnungen anschalten kann)

    Ausgabe/Rückgabewert/Symbolische Konstante waren alles Vorgaben in der Aufgabenstellung, deswegen hab ich das hier so aber danke für die Vorschläge 🙂

    Durch die Schleife

    for (i = 0; i < MAX_LAENGE; i++)
    

    werden ja nur MAX_LAENGE Elemente überprüft, sollte zeichen[MAX_LAENGE-1] kein '\n' sein, wird einfach der Puffer geleert und die Funktion beendet. Aufgerufen wird sie ja in dem Fall mit

    while (leseZeichenkette(c) == -1){}
    

    getchar() muss ich allerdings noch auf EOF überprüfen


  • Mod

    KJoke schrieb:

    Ausgabe/Rückgabewert/Symbolische Konstante waren alles Vorgaben in der Aufgabenstellung, deswegen hab ich das hier so aber danke für die Vorschläge 🙂

    Soll das wirklich alles in die Funktion oder soll nicht eher das Programm diese Ausgaben machen? Ich könnte mir so etwas vorstellen:

    puts("Eingabe: ");
    if(deine_lesefunktion(string, MAX_LENGTH) == 0)
      printf("Eingabe war: %s", string);
    else
      puts("Fehler bei der Eingabe.");
    

    Das sieht dann auch halbwegs wie normales C aus, daher sollte eine gut formulierte Aufgabenstellung in diese Richtung gehen.



  • int leseZeichenkette(char * zeichenkette): Soll eine Zeichenkette mit maximal
    einer festen Anzahl an Zeichen (definiert über eine symbolischen Konstante MAX_LAENGE)
    einlesen und an zeichenkette schreiben. Ist die eingelesene Zeichenkette zu lang, dann
    soll der Puffer geleert und -1 zurückgegeben werden. Anderenfalls soll 0 zurückgegeben
    werden

    Zumindest die Ausgabe war doch nicht verlangt und kommt wohl auch wieder raus :p


Anmelden zum Antworten