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ürdescanf("%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!
-
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ürdescanf("%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);
-
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
-
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
-
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
werdenZumindest die Ausgabe war doch nicht verlangt und kommt wohl auch wieder raus :p