Forum: PC-Programmierung Fehler bei Rückgabe einer Liste


von Ratlos (Gast)


Lesenswert?

Hallo zusammen,

ich habe einen Fehler in einem Programm den ich einfach nicht finde. 
Vereinfacht gesagt ist die Aufgabe, aus einer Anzahl von gegebenen 
Werten eine Liste mit bestimmter Reihenfolge zu erstellen. Die Funktion 
besteht im Wesentlichen aus folgendem Code:
1
int16_t* generateList(int16_t *list_cnt){
2
3
    /* ****************************************************
4
    INITIALIZE VARIABLES
5
    **************************************************** */
6
    int16_t *list = NULL;
7
    *list_cnt = 0;
8
9
    int16_t *queue = NULL;
10
    int16_t queue_cnt = 0;
11
    int16_t queue_size = global_cnt.final + 1; /* Anzahl d. gegebenen Werte + 1 */
12
13
    int16_t previous[5]   = {0};
14
    int16_t previous_cnt  = 0;
15
16
    int16_t n = 0;
17
    int8_t considered = 0;
18
    int16_t currentEntry = 0;
19
20
    /* ****************************************************
21
    COPY THE FIRST ENTRIES QUEUE AND LIST
22
    **************************************************** */
23
    queue = calloc(queue_size, sizeof(int16_t));
24
    queue_cnt = global_cnt.final;
25
    for(n=0; n<global_cnt.final; n++){
26
        queue[n] = global.final[n]; /* Array mit gegebenen Werte */
27
    }
28
29
    *list_cnt = global_cnt.final;
30
    list = malloc((*list_cnt) * sizeof(int16_t));
31
    for(n=0; n < *list_cnt; n++){
32
        list[n] = global.final[n];
33
    }
34
35
    /* ****************************************************
36
    PROCESS THE DATA IN QUEUE
37
    **************************************************** */
38
    while(*list_cnt != global_cnt.states){
39
40
        considered = 0;
41
        currentEntry = queue[0];
42
        getPrevious(currentEntry, previous, &previous_cnt);
43
44
        if(previous_cnt != 0){
45
            /* check whether the entry was already mentioned */
46
            for(n=0; n<*list_cnt; n++){
47
                if(list[n] == previous[0]){
48
                    considered = 1;
49
                }
50
            } /* END: iterate over the list */
51
52
            /* Append new entry to list and queue */
53
            if(0 == considered){
54
                queue[queue_cnt] = previous[0];
55
                queue_cnt++;
56
57
                (*list_cnt)++;
58
                list = realloc(list, *list_cnt * sizeof(int16_t));
59
                list[*list_cnt - 1] = previous[0];
60
61
            } /* END: Entry is not considered yet */
62
63
        } /* END: There is a previous entry */
64
65
66
        /* ****************************************************
67
        REMOVE THE FIRST ENTRY IN QUEUE
68
        **************************************************** */
69
        if(queue_cnt > 1){
70
            for(n=1; n<queue_cnt; n++){
71
                queue[n-1] = queue[n];
72
            }
73
            queue[queue_cnt] = 0;
74
            queue_cnt--;
75
        }
76
77
puts("Ende While-Schleife");
78
free(queue);
79
/* return the generated list */
80
return list;
81
}

Aufgerufen wird das Ganze so:
1
    int16_t *list= NULL;
2
    int16_t list_cnt = 0;
3
4
    list= generateList(&list_cnt);
5
    puts("Back!");

Problem: die Liste wird richtig erstellt und die while Schleife 
verlassen (erkennbar an puts-Ausgabe). Danach stürzt das Programm ab. 
Die aufrufende Funktion wird nicht wieder erreicht.

Sieht jemand den Fehler?

Gruß

von Robert L. (lrlr)


Lesenswert?

(ich kann nicht C)
aber

 list[n] =

ist "bin ich mir sicher" falsch..??

von Ratlos (Gast)


Lesenswert?

Robert L. schrieb:
> (ich kann nicht C)
> aber
>
>  list[n] =
>
> ist "bin ich mir sicher" falsch..??

Wie bitte!?

von Peter II (Gast)


Lesenswert?

es wird wohl ein überschreiber vom Speicher sein. Da wir aber die 
Variablen (z.b. global_cnt) nicht kennen, wird hier keiner helfen 
können.

von NurEinGast (Gast)


Lesenswert?

um es besser nachvollziehen zu können,

was steht denn in global_cnt ?

von Ratlos (Gast)


Lesenswert?

global_cnt ist eine Struktur der Form:
1
typedef struct {
2
    int16_t ...;
3
    int16_t ...;
4
    int16_t final;
5
    int16_t ...;
6
    int16_t ...;
7
} global_cnt_t;

Also nichts weltbewegendes..

von Peter II (Gast)


Lesenswert?

Ratlos schrieb:
> Also nichts weltbewegendes..

und welche Werte stehen da drin

von Ratlos (Gast)


Lesenswert?

In global_cnt.final steht '1' und In global.final[..] steht '13'. Das 
bedeutet global_cnt.x enthält die Anzahl der Einträge in global.x.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Ratlos schrieb:
> Problem: die Liste wird richtig erstellt und die while Schleife
> verlassen (erkennbar an puts-Ausgabe). Danach stürzt das Programm ab.
> Die aufrufende Funktion wird nicht wieder erreicht.

Und, hast Du das ganze mal im Debugger laufenlassen?

Wozu gibt es Debugger?

von Peter II (Gast)


Lesenswert?

das sieht mir merkwürdig aus

queue[queue_cnt]

ich denke hier überschreibst du dir den stack.

von Karl H. (kbuchegg)


Lesenswert?

Puh.
Der Code sieht mir reichlich kompliziert aus.
Auch wenn ich nicht so ganz raus habe, was das ganze soll, bin ich mir 
recht sicher, dass man das auch einfacher schreiben kann.

Zb. ist mir überhaupt nicht klar, wozu du da die queue brauchst.
So wie das aussieht, willst du von einer global verfügbaren Liste
  global.final / global_cnt.final;
alle Einträge holen, wobei du Duplikate eliminierst.

Was allerdings
getPrevious
in diesem Zusammenhang soll, ist nicht wirklich ersichtlich.

Ich denke, mit deinen vielen Variablen und ebensovielen counts hast du 
dir selbst ein Eigentor geschossen und dir irgendwo den Speicher 
zerschossen.
Das hier
1
            if(0 == considered){
2
                queue[queue_cnt] = previous[0];
3
                queue_cnt++;
ist mir zb suspekt. Leider ist nicht einfach nachvollziehbar, welche 
Werte queue_cnt im Laufe der Zeit annimmt. Wenn du aber über die 
allokierte Länge drüber kommst, dann ....

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
    int16_t previous[5]   = {0};
2
    int16_t previous_cnt  = 0;
3
4
...
5
        getPrevious(currentEntry, previous, &previous_cnt);

ist zb auch gefährlich.

von Ratlos (Gast)


Lesenswert?

@Karl Heinz:
Die Anwendung IST leider sehr komplex! getPrevious sucht aus einer 
Baumstruktur den Vorgänger des aktuellen Eintrags. Die Funktion habe ich 
getestet und für gut befunden... :-)

@Peter II:
Der Absturz kommt aber erst nach der while-Schleife und dem richtigen 
Erstellen der Liste!

@Rufus:
Nein, habe ich noch nicht. Was mich eben wundert ist, dass der Absturz 
bei der Rückgabe zu passieren scheint. Dementsprechend habe ich gehofft, 
das würde die Fehlersuche eingrenzen. Und richtig: ich kann (noch) nicht 
wirklich mit dem Debugger umgehen.

von Peter II (Gast)


Lesenswert?

Ratlos schrieb:
> @Peter II:
> Der Absturz kommt aber erst nach der while-Schleife und dem richtigen
> Erstellen der Liste!

der Abstuzt erfolgt ja auch nicht beim überschreiben vom Speicher, 
sonder beim zugriff auf die fehlerhaften Daten.

Karl Heinz Buchegger  hat den gleichen code gefunden, er wird das 
Problem sein. Mache doch einfach mal die queue ein element größer und 
schau was dann passiert.

von Karl H. (kbuchegg)


Lesenswert?

Ratlos schrieb:
> @Karl Heinz:
> Die Anwendung IST leider sehr komplex!

Das ist kein Grund, dass man sich nicht Teilsysteme schreiben kann, die 
sich um einen bestimmten Aspekt kümmern, anstatt alles in eine Funktion 
zu pappen.

ZB. ist es kein Problem sich ein Queue Modul zu schreiben, welches
1
struct queue
2
{
3
  int  cnt;
4
  int* values;
5
}
6
7
void Init( struct queue* object );
8
void Enqueue( struct queue* object, int newValue );
9
int  Dequeue( struct queue* object );
10
}
sich ausschlieeslich um die Belange einer queue, samt 
Speicherallokierung kümmert.
Damit hat man dann diese dynamisch Speicherverwaltung aus der Funktion 
schon mal weg und hat gleichtzeitig dann auch noch eine Codestelle, an 
der man Überprüfungen in die Debug Version einbauen kann.

von Ratlos (Gast)


Lesenswert?

Ist diese Zeile richtig angewendet:
1
queue = calloc(queue_size, sizeof(int16_t));

Ich habe die Variable "queue_size" mal vergrößert. Es kommt zum selben 
Absturz. :-/

von Peter II (Gast)


Lesenswert?

Ratlos schrieb:
> Ist diese Zeile richtig angewendet:queue = calloc(queue_size,
> sizeof(int16_t));

die zeile ist erstmal richtig, es fehlt aber überll die 
Fehlerbehandlung. Was ist wenn du keinen speicher mehr bekommst?

> Ich habe die Variable "queue_size" mal vergrößert. Es kommt zum selben
> Absturz. :-/
dann musst du dich doch mal mit dem Debugger vertraut machen, das blickt 
hier sonst keiner. Und uns ein vollständiges Testprogramm geben.

von Karl H. (kbuchegg)


Lesenswert?

Ratlos schrieb:
> Ist diese Zeile richtig angewendet:
>
1
queue = calloc(queue_size, sizeof(int16_t));

Das ist grundsätzlich schon ok.
Die Frage ist, ob die allokierte Speichergröße reicht.
Das kann hier aber keiner sagen, weil die ja wiederrum von
1
    int16_t queue_size = global_cnt.final + 1; /* Anzahl d. gegebenen Werte + 1 */
abhängt.
Zudem ändert sich queue_cnt dann ja auch in Abhängigkeit der Daten
1
    queue_cnt = global_cnt.final;
und dann weiter wird queoe_cnt erniedrigt und erhöht, so dass ohne die 
Kentniss der Daten das auf dieser Seite des Bildschirms nicht 
nachvollziehbar ist, was passiert.

> Ich habe die Variable "queue_size" mal vergrößert.

Du betreibst Software-Entwicklung nach dem Prinzip der 'Hoffnung'.
Gerade bei dynamischen Datenstrukturen ist das keine gute Idee.

Wenn du schon deinen Code nicht aufräumst, dann spicke ihn wenigstens 
mit Ausgaben, so dass du am Papier nachvollziehen kannst, was passiert.

von Ratlos (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du schon deinen Code nicht aufräumst, dann spicke ihn wenigstens
> mit Ausgaben, so dass du am Papier nachvollziehen kannst, was passiert.

Schon geschehen, daher weiß ich ja, dass die Liste an sich richtig 
generiert wird. Ich die Ausgaben nur der Übersichtlichkeit halber hier 
rausgelöscht.

Okay, ich befürchte das Problem ist aus der Ferne nicht lösbar. Danke 
trotzdem.

von Karl H. (kbuchegg)


Lesenswert?

Das hier wäre mir zb suspekt
1
    queue = calloc(queue_size, sizeof(int16_t));
2
    queue_cnt = global_cnt.final;
3
4
5
...
6
7
8
            /* Append new entry to list and queue */
9
            if(0 == considered){
10
                queue[queue_cnt] = previous[0];
11
                queue_cnt++;
12
....
13
14
15
        if(queue_cnt > 1){
16
            for(n=1; n<queue_cnt; n++){
17
                queue[n-1] = queue[n];
18
            }
19
            queue[queue_cnt] = 0;
20
            queue_cnt--;
21
        }

Du greifst da recht munter auf den Speicher zu, ohne dich darum zu 
kümmern, dass queue_cnt nicht größer/gleich als queue_size werden darf.
Wozu das 0-setzen in
1
            queue[queue_cnt] = 0;
bei korrektem Programm brauchst du es nicht, denn queue_cnt sagt wir 
sowieso immer, wieviele Einträge gültig sind. Alles was du tust ist, du 
läufst Gefahr auf ein nicht existentes queue Element zuzugreifen, wenn 
die Queue erst mal verlängert wurde, ehe das erste Element rausgenommen 
wurde.


Mein Tip:
Kapsle die Queue Funktionalität in eigene Funktionen. Das IST machbar! 
Und dort vergrößerst/verkleinerst du den Speicher für die Queue nach 
Bedarf, anstatt auf einer Default-Initialisierung zu bestehen, die je 
nach Datenlage dich in enorme Probleme bringen kann.

von DirkZ (Gast)


Lesenswert?

Ratlos schrieb:
> Problem: die Liste wird richtig erstellt und die while Schleife
> verlassen (erkennbar an puts-Ausgabe). Danach stürzt das Programm ab.

Falsche Schlußfolgerung (die Liste wird richtig erstellt). Du weißt nur, 
dass Funktion bis zum Schluß durchläuft (puts-Ausgabe), dann das 
Programm abstürtzt.

Wenn
1
return list;
 auf einen ungültigen Speicherbereich verweist -> Absturz.

von Peter II (Gast)


Lesenswert?

DirkZ schrieb:
> Wennreturn list; auf einen ungültigen Speicherbereich verweist ->
> Absturz.

nein, denn wohin ist zeigt ist absolut egal.

Das Problem ist das return, dabei wird er stack aufgeräumt. Und wenn der 
stack dabei übeschrieben wurde, dann stützt das Programm halt ab.

von Ratlos (Gast)


Lesenswert?

Um das Thema noch mal kurz aufzuwärmen. Ich habe den Original-Code auf 
einem anderen System kompiliert und da geht es.

Fehlerhaft: Windows 7 64 Bit mit 32 Bit-MinGW
Fehlerfrei: Xubuntu 64 Bit   mit gcc

Kann man mit den Informationen den Fehler eingrenzen?

Gruß

von Peter II (Gast)


Lesenswert?

Ratlos schrieb:
> Kann man mit den Informationen den Fehler eingrenzen?

nein.

Ich würde weiter den fehler suchen. Es muss einer drin sein. kannst du 
nicht eine lauffähiges Quellcode bereitstellenm, wo wir es eventuell 
nachvollziehen können.

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.