Forum: PC-Programmierung fgets wartet nicht, setzen einer variablen in einem struct lässt programm abstürzen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Johannes (Gast)


Lesenswert?

Hallo,
ich versuche in C eine Liste zu erstellen, hier habe ich aber zwei 
Probleme

1
struct liste
2
{
3
    uint8_t number;
4
    char word[20];
5
    struct liste *next;
6
};
7
8
9
static void eingabe(void)
10
{
11
    uint8_t number_u8;
12
    char word[20];
13
    printf("number.....................:");
14
    scanf("%3d", &number_u8);
15
    printf("word.......................:");
16
    fgets(word, 20, stdin);
17
    anhaengen(number_u8, word);
18
}
19
20
static void anhaengen(uint8_t number, char* n)
21
{
22
    printf("\nadd input in list %u %c\n", number, n);
23
    struct liste *pointer;
24
25
    if(NULL == list)
26
    {
27
        printf("empty list, create first element\n");
28
        if(NULL == malloc(sizeof(struct liste)))
29
        {
30
            fprintf(stderr, "no free memory\n");
31
            return;
32
        }
33
34
        printf("add list number\n");
35
        list->number = number;
36
        printf("add list word\n");
37
        strcpy(list->word, n);
38
        printf("add list next\n");
39
        list->next = NULL;
40
        printf("next element created\n");
41
    }

Das erste Problem ist, dass bei fgets nicht gewartet wird, bis ich eine 
Eingabe gemacht habe.
und beim setzen von number in die liste stürzt das Programm ab.

Der Output sieht wie folgt aus:
1
H:\Projects\C\liste>main
2
number.....................:2
3
word.......................:
4
add input in list 2 Ù
5
empty list, create first element
6
add list number
7
8
H:\Projects\C\liste>



1) wieso wartet fgets nicht bis ich die Daten schicke?
2) wieso stürzt list->number = number; ab?

von Nop (Gast)


Lesenswert?

Johannes schrieb:

>     scanf("%3d", &number_u8);

Scanf erwartet dort einen Pointer auf einen Integer - es ist aber nur 
ein Pointer auf ein Byte.

>     fgets(word, 20, stdin);

word ist ein Buffer von 20 Zeichen, und Du liest 20 Zeichen ein - das 
ergibt u.U. einen String, der nicht nullterminiert ist. Mach den Buffer 
21 Zeichen groß und setz nach dem fgets noch:
word[20] = '\0';

>         if(NULL == malloc(sizeof(struct liste)))

Wo speicherst Du denn den Rückgabewert von malloc? Gar nicht.

von Nop (Gast)


Lesenswert?

Übrigens hättest Du einiges davon schon gefunden, wenn Du mit -Wall 
-Wextra kompiliert hättest. Deswegen macht man das, damit man solche 
Fehler nicht selber suchen muß, sondern das den Compiler machen läßt.

von Dirk B. (dirkb2)


Lesenswert?

Johannes schrieb:
> 1) wieso wartet fgets nicht bis ich die Daten schicke?

Weil im Eingabestrom noch das '\n' vom scanf ist.

> 2) wieso stürzt list->number = number; ab?

Weil list immer noch NULL ist.

fgets weiß um den Platz mit dem '\0' und berücksichtigt das.
fgets speichert das '\n' übrigens mit ab.

: Bearbeitet durch User
von 123 (Gast)


Lesenswert?

Johannes schrieb:
> Das erste Problem ist, dass bei fgets nicht gewartet wird, bis ich eine
> Eingabe gemacht habe.

weil dort bereits ein  '\n'-Zeichen (von der Zahleingabe) im Buffer ist.

Du musst stdin-Buffer leeren:
1
   printf("number.....................:");
2
    scanf("%3d", &number_u8);
3
    
4
    //Buffer leeren
5
    while (getchar() != '\n'){  
6
       //nothing to do
7
    }
8
9
    printf("word.......................:");
10
    fgets(word, 20, stdin);

Und die Sache mit malloc sollte auch verbessert werden

von 123 (Gast)


Lesenswert?

Nop schrieb:
> word ist ein Buffer von 20 Zeichen, und Du liest 20 Zeichen ein - das
> ergibt u.U. einen String, der nicht nullterminiert ist.

fgets schließt die Strings immer richtig (mit '\0') ab.

Die Frage ist nur, ob das Zeichen '\n' mitabgespeichert wird oder nicht. 
Bei einem Eingabestring >=20 Zeichen wird '\n' nicht mitgespeichert.

von Gerald K. (geku)


Lesenswert?

Ich würde scanf und fgets nicht mischen.

Entweder beide Parameter nur mit scanf oder nur mit fgets  einlesen.

von Nop (Gast)


Lesenswert?

Dirk B. schrieb:

> fgets weiß um den Platz mit dem '\0' und berücksichtigt das.
> fgets speichert das '\n' übrigens mit ab.

Stimmt. Das Problem, was ich meinte, hat strncpy, nicht fgets.

von Gerald K. (geku)


Angehängte Dateien:

Lesenswert?

Gerald K. schrieb:
> Ich würde scanf und fgets nicht mischen
Hilfreich wäre ein fehlerfrei kompilierbarer Code im Anhang gewesen.
Das im Text eingebettete Beispiel enthält viele Fehler. Ich habe es mit 
org.c nachgeholt.

In diesem Beispiel habe ich scanf durch fgets ersetzt.


Original (org.c):
1
static void eingabe(void)
2
{
3
 uint8_t number_u8;
4
 char word[20];
5
 printf("number.....................:");
6
 scanf("%3d", &number_u8);
7
 printf("word.......................:");
8
 fgets(word, 20, stdin);
9
 anhaengen(number_u8, word);
10
}

Nur mit fputs (test.c):
1
static void eingabe(void)
2
{
3
 uint8_t number_u8;
4
 char word[20];
5
 char number_str[4];
6
7
 printf("number.....................:");
8
 fgets(number_str,3,stdin);
9
 number_u8 = atoi(number_str) & 0xff;
10
 printf("word.......................:");
11
 fgets(word, 20, stdin);
12
 anhaengen(number_u8, word);
13
}

Das Programm sollte noch mit einem Eingabecheck versehen werden.

Scanf sollte man auf kleinen embedded Plattformen vermeiden, da sie viel 
Code produzieren.

Johannes schrieb:
> wieso stürzt list->number = number; ab?

Wo ist list deklariert?

liste wäre falsch, da es sich hier um den Datentyp handelt, richtig 
ist die Variable pointer mit dem Datentyp liste .

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Nop schrieb:
> Johannes schrieb:
>
>>     scanf("%3d", &number_u8);
>
> Scanf erwartet dort einen Pointer auf einen Integer - es ist aber nur
> ein Pointer auf ein Byte.

Ja. Zur Erklärung: Man ist es von printf gewöhnt, dass man für alles, 
was kleiner als int ist, auch %d verwenden kann, auf Grund der Integer 
Promotion. Das gilt für scanf aber nicht, da dort kein Integer-Wert, 
sondern ein Zeiger übergeben wird. Dort kann so etwas natürlich nicht 
stattfinden, daher muss man hier immer den exakten Typ angeben. Im 
obigen Code schreibt scanf in Bytes, die nicht zu number_u8 gehören und 
macht damit vermutlich andere Daten kaputt.

Dirk B. schrieb:
>> 2) wieso stürzt list->number = number; ab?
>
> Weil list immer noch NULL ist.

Dem gezeigten Code fehlt die Definition von list, daher ist unbekannt, 
ob das wirklich NULL ist.

Johannes schrieb:
> static void anhaengen(uint8_t number, char* n)
> {
>     printf("\nadd input in list %u %c\n", number, n);

%c ist hier falsch. Das erwartet einen int, in dem ein Zeichen steht. Du 
übergibst aber stattdessen einen Zeiger. Deshalb kommt diese Ausgabe:

> add input in list 2 Ù

Es müsste hier %s heißen.

Gerald K. schrieb:
> Johannes schrieb:
>> wieso stürzt list->number = number; ab?
>
> Wo ist list deklariert?
>
> liste wäre falsch, da es sich hier um den Datentyp handelt, richtig
> ist die Variable pointer mit dem Datentyp liste .

pointer ist vom Datentyp Zeiger auf liste und wird hier nicht verwendet. 
Die Vermutung liegt nahe, dass list eine globale Variable ist, die hier 
im Posting vergessen wurde.

von Gerald K. (geku)


Lesenswert?

Rolf M. schrieb:
> Die Vermutung liegt nahe, dass list eine globale Variable ist, die hier
> im Posting vergessen wurde.

Darum sollte man im Posting immer das komplette, lauffähige Beispiel im 
Anhang mitliefern.

Da man in der Liste nicht RÜCKWÄRTS suchen kann, ist eine Variable mit 
dem Zeiger auf das erste Listenelement notwendig. Globale oder static 
Variable.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Rolf M. schrieb:
> Dirk B. schrieb:
>>> 2) wieso stürzt list->number = number; ab?
>>
>> Weil list immer noch NULL ist.
>
> Dem gezeigten Code fehlt die Definition von list, daher ist unbekannt,
> ob das wirklich NULL ist.

In dem gezeigten Code-Schnippsel findet die Zuweisung an list->number 
nur statt, wenn NULL == list ist

von Dirk B. (dirkb2)


Lesenswert?

@ Johannes (Gast)

Was möchtest du mit dem fgets einlesen?

Ein Wort, wie es der Variablenname suggeriert - dann ist fgets dafür 
nicht geeignet, sonder scanf mit "%19s"

Mehrere Wörter, die durch Leerzeichen getrennt werden - auch dafür hat 
scanf einen Formatspecifier: " %19[^\n]" (beachte das Leerzeichen vor 
dem %)

: Bearbeitet durch User
von Gerald K. (geku)


Lesenswert?

Dirk B. schrieb:
> In dem gezeigten Code-Schnippsel findet die Zuweisung an list->number
> nur statt, wenn NULL == list ist

Vermutlich sollte mit malloc ein Wert zugewiesen werden.
Aber es ist richtig, dies funktioniert nur, wenn list vorher NULL war.
Mangels Code wissen wir es nicht.

von Dirk B. (dirkb2)


Lesenswert?

Gerald K. schrieb:
> Vermutlich sollte mit malloc ein Wert zugewiesen werden.

Das wäre doch sehr sinnvoll.
Wenn man es nicht macht, entsteht auch ein Speicherleck.

Hätte man den Code auf zwei Zeilen gepackt, wäre das nicht passiert.
(ich meine hier Zuweisung an list und dann Überprüfung mit if in einer 
neuen Zeile).
Und hier (da list == NULL) braucht man auch keinen Hilfspointer, falls 
malloc NULL liefert.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.