Forum: PC-Programmierung Speicherzugriffsfehler C++


von Max F. (mexxe)


Lesenswert?

Hallo Gemeinde,
Ich habe ein Problem beim Tausch von Werten eines Structs innerhalb 
einer Liste. Beim ersten Mal tausche ich die Werte ohne Probleme. Da ich 
sie aber nun zurück tauschen will, tritt ein Speicherzugriffsfehler auf 
und ich kann mir nicht erklären, wo dieser liegen soll.
Das Programm soll eine Liste von Dominosteinen so in einem Kreis 
anornden, dass dieser geschlossen ist. Beispielsweise soll, wenn die 
rechte Hälfte des Dominosteins eine 11 ist, der passende Dominostein 
gefunden werden und so gedreht werden, dass die 11 links, also dazu 
passend, ist. Hier der Quelltext auszugsweise:
1
struct node {
2
  int zahl_l;
3
  int zahl_r;
4
  node* next;
5
};
6
7
void sortieren (node* head, int anzahl) {
8
  node *new_head, *start, *pos, *aktuell, *temp, *tausch;
9
  int swap;
10
  int i = 0;
11
  int j = 0;
12
  int k = 0;
13
  int stelle = 0;
14
  node *feld[anzahl];
15
  bool getauscht = false;
16
17
  aktuell = head;
18
  while (aktuell != 0) {
19
    aktuell->markiert = false;
20
    aktuell = aktuell->next;
21
  }
22
23
  new_head = new node;
24
  new_head->zahl_l = head->zahl_l;
25
  new_head->zahl_r = head->zahl_r;
26
  new_head->next = 0;
27
  pos = head->next;
28
  aktuell = head->next;
29
30
31
start = head;
32
    while (start != NULL) {  
33
      aktuell = head;
34
      while (aktuell != 0) {
35
        aktuell->markiert = false;
36
        aktuell = aktuell->next;
37
      }
38
39
      new_head = new node;
40
      new_head->zahl_l = head->zahl_l;
41
      new_head->zahl_r = head->zahl_r;
42
      new_head->next = 0;
43
      pos = head->next;
44
      aktuell = head->next;
45
        
46
      tausch->zahl_l = new_head->zahl_l;
47
      tausch->zahl_r = new_head->zahl_r;
48
49
      new_head->zahl_l = start->zahl_l;
50
      new_head->zahl_r = start->zahl_r;
51
52
      start->zahl_l = tausch->zahl_l;
53
      start->zahl_r = tausch->zahl_r;
54
55
56
      while (aktuell->zahl_r != new_head->zahl_l) {
57
        pos = head->next;  
58
    //    if (pos == 0)
59
    //      return;
60
        if (i == 0) 
61
          aktuell = new_head;
62
        
63
  
64
        while (pos->zahl_r == aktuell->zahl_r && pos->zahl_l == aktuell->zahl_l) {
65
          pos = pos->next;
66
          j++;
67
68
          if (pos == 0)
69
            pos = head->next;
70
          if (pos->markiert == true)
71
            pos = pos->next;
72
          if (pos == 0)
73
            pos = head->next;
74
        
75
          while (pos->zahl_r != aktuell->zahl_r && pos->zahl_l != aktuell->zahl_r) {
76
            pos = pos->next;
77
            if (pos == 0)
78
              pos = head->next;
79
            if (pos->markiert == true)
80
              pos = pos->next;
81
            if (pos == 0)
82
              pos = head->next;
83
          }
84
  
85
          if (pos == 0)
86
            pos = head->next;
87
        }
88
89
        pos->markiert = true;
90
91
        if (j == 0) {
92
          while (pos->zahl_r != aktuell->zahl_r && pos->zahl_l != aktuell->zahl_r) {
93
            pos = pos->next;
94
            if (pos->markiert == true)
95
              pos = pos->next;
96
            if (pos == 0)
97
              pos = head->next;
98
          }
99
        }  
100
        j = 0;
101
        pos->markiert = true;
102
103
        if (pos->zahl_l != aktuell->zahl_r) {
104
          swap = pos->zahl_l;
105
          pos->zahl_l = pos->zahl_r;
106
          pos->zahl_r = swap;
107
          getauscht = true;
108
        }
109
        temp = new node;
110
        temp->zahl_l = pos->zahl_l;
111
        temp->zahl_r = pos->zahl_r;
112
        aktuell->next = temp;
113
        aktuell = temp;
114
        aktuell->next = 0;
115
      /*  if (getauscht == true) {     Hier kommt der Fehler
116
          swap = pos->zahl_l;
117
          pos->zahl_l = pos->zahl_r;
118
          pos->zahl_r = swap;
119
        }
120
  */      pos->getauscht = false;
121
        i++;
122
        k++;
123
        getauscht = false;
124
      }        // beendet while-Schleife
125
      i = 0;
126
      j = 0;
127
      k = 0;
128
      start->zahl_l = new_head->zahl_l;
129
      start->zahl_r = new_head->zahl_r;
130
      feld[stelle] = new_head;
131
      while (feld[stelle] != NULL) {
132
        printf("[%d;%d]", feld[stelle]->zahl_l, feld[stelle]->zahl_r);
133
        feld[stelle] = feld[stelle]->next;
134
      }
135
      printf("\n");
136
      stelle++;
137
      start = start->next;
138
    }

Ich übergebe der Funktion den head einer Liste, so viel zur Erläuterung.
Mein zweites Problem dann ist, dass ich ja grundsätzlich die Liste mit 
jedem Listenelement am Anfang ausgeben will, sodass die oben genannte 
Bedingung erfüllt ist. Nun ist es so, dass bei gewissen Datein das 
Programm sich in einer Endlosschleife aufhängt und bei manchen Datein 
macht er das bis zum letzten Element, gibt da allerdings nur den 
Listenkopf aus. Wie oben aufgezeigt, vertausche ich nicht die ganzen 
Elemente, sondern lediglich die gespeicherten Zahlenwerte. Woran liegt 
das Problem hier?

von Sven B. (scummos)


Lesenswert?

1
valgrind --track-origins=yes ./main

von Max F. (mexxe)


Lesenswert?

Sven B. schrieb:
>
1
valgrind --track-origins=yes ./main

Was bringt das, um mal dumm zu fragen?

von Sven B. (scummos)


Lesenswert?

valgrind (bzw. memcheck, das Default-Tool) ist ein "memory error 
detector", der dir genau sagen kannst, wann und von wo du zum Beispiel 
uninitialisierten Speicher liest, außerhalb von Array-Grenzen schreibst, 
auf gelöschte Objekte zugreifst etc. Die meisten komischen 
C++-Speicherfehler hat man damit sehr schnell gefunden.

von Max F. (mexxe)


Lesenswert?

Sven B. schrieb:
> valgrind (bzw. memcheck, das Default-Tool) ist ein "memory error
> detector", der dir genau sagen kannst, wann und von wo du zum Beispiel
> uninitialisierten Speicher liest, außerhalb von Array-Grenzen schreibst,
> auf gelöschte Objekte zugreifst etc. Die meisten komischen
> C++-Speicherfehler hat man damit sehr schnell gefunden.

Und wie genau implementiere ich ihn innerhalb meines Cods?

von user (Gast)


Lesenswert?

gar nicht, du musst nur valgrind mit deinem Programm als parameter 
starten, das Ergebniss bekommt du nach Beendigung deinen Programms

von Carl D. (jcw2)


Lesenswert?

Was daran ist eigentlich C++?
Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und 
kein Speicherproblem.

von Max F. (mexxe)


Lesenswert?

Carl D. schrieb:
> Was daran ist eigentlich C++?
> Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und
> kein Speicherproblem.

Speicherplatz anfordern mit new.
Darf ich nicht nutzen.

von The D. (thedaz)


Lesenswert?

Max F. schrieb:
> Carl D. schrieb:
>> Was daran ist eigentlich C++?
>> Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und
>> kein Speicherproblem.
>
> Speicherplatz anfordern mit new.
> Darf ich nicht nutzen.

Der STL kannst du deine eigene allocator Klasse in die Hand drücken, die 
dann statt der Standard-Heap Implementierung benutzt wird.

von Max F. (mexxe)


Lesenswert?

The D. schrieb:
> Max F. schrieb:
>> Carl D. schrieb:
>>> Was daran ist eigentlich C++?
>>> Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und
>>> kein Speicherproblem.
>>
>> Speicherplatz anfordern mit new.
>> Darf ich nicht nutzen.
>
> Der STL kannst du deine eigene allocator Klasse in die Hand drücken, die
> dann statt der Standard-Heap Implementierung benutzt wird.

Das Problem ist, ich darf die Aufgabe nur mit den im Programm genutzen 
Werkzeug lösen. Das macht die Sache erheblich schwerer.

von Carl D. (jcw2)


Lesenswert?

The D. schrieb:
> Max F. schrieb:
>> Carl D. schrieb:
>>> Was daran ist eigentlich C++?
>>> Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und
>>> kein Speicherproblem.
>>
>> Speicherplatz anfordern mit new.
>> Darf ich nicht nutzen.
>
> Der STL kannst du deine eigene allocator Klasse in die Hand drücken, die
> dann statt der Standard-Heap Implementierung benutzt wird.

Ich glaube er meinte, er darf die STL nicht benutzen, new aber schon, da 
es ja ein Sprachelement von C++ ist.
Klingt nach Schule oder untere Semester. Ich hoffe nach der Übung sagt 
der Lehrer/Prof. "so und jetzt das ganze mal mit STL, ganz knapp und 
stabil".
(man darf ja hoffen, oder? Auch wenn's sicher vergeblich ist ;-)

von Max F. (mexxe)


Lesenswert?

Carl D. schrieb:
> The D. schrieb:
>> Max F. schrieb:
>>> Carl D. schrieb:
>>>> Was daran ist eigentlich C++?
>>>> Wenn es C++ wäre, dann gäbe es da std::list<> (besser std::vector<>) und
>>>> kein Speicherproblem.
>>>
>>> Speicherplatz anfordern mit new.
>>> Darf ich nicht nutzen.
>>
>> Der STL kannst du deine eigene allocator Klasse in die Hand drücken, die
>> dann statt der Standard-Heap Implementierung benutzt wird.
>
> Ich glaube er meinte, er darf die STL nicht benutzen, new aber schon, da
> es ja ein Sprachelement von C++ ist.
> Klingt nach Schule oder untere Semester. Ich hoffe nach der Übung sagt
> der Lehrer/Prof. "so und jetzt das ganze mal mit STL, ganz knapp und
> stabil".
> (man darf ja hoffen, oder? Auch wenn's sicher vergeblich ist ;-)

2. Semester ;)
Den Speicherzugriffsfehler habe ich gelöst, danke an dieser Stelle für 
die Hilfe.
Allerdings besteht das 2. Problem immernoch, keine Ahnung, ob es hier 
passend ist.
Die eine Datei liest er zweifelsfrei, bei der anderen gerät er nach der 
Ausgabe des 2. neuen Heads in eine Endlosschleife. Ich muss erstmal 
gucken, in welcher.

von Sven B. (scummos)


Lesenswert?

Hier wäre ein Debugger empfehlenswert.

von Carl D. (jcw2)


Lesenswert?

Vielleicht solltest du das ganze in die Komponenten Swap, Sort und 
Compare zerlegen. Diese sind dann jeweils kleiner und überschaubarer.
Das was du hier machst, ist nämlich die Aufgabe des Compilers(/Linkers): 
aus der strukturierten Lösungsbeschreibung per Optimierung möglichst 
Branchfreien, auch Spagetti-Code genannten, Maschinen-Code zu 
produzieren. Jeder halbwegs vernünftige Compiler wird auf ein Resultat 
kommen, das bei dir schon im Sourcecode vorhanden ist und leicht 
unübersichtlich wirkt.
Da es a C++ sein soll, darf die struct auch diese Methoden besitzen: 
Sort(), Swap() und Compare() oder auch operator<(), oder?

von Max F. (mexxe)


Lesenswert?

Carl D. schrieb:
> Vielleicht solltest du das ganze in die Komponenten Swap, Sort und
> Compare zerlegen. Diese sind dann jeweils kleiner und überschaubarer.
> Das was du hier machst, ist nämlich die Aufgabe des Compilers(/Linkers):
> aus der strukturierten Lösungsbeschreibung per Optimierung möglichst
> Branchfreien, auch Spagetti-Code genannten, Maschinen-Code zu
> produzieren. Jeder halbwegs vernünftige Compiler wird auf ein Resultat
> kommen, das bei dir schon im Sourcecode vorhanden ist und leicht
> unübersichtlich wirkt.
> Da es a C++ sein soll, darf die struct auch diese Methoden besitzen:
> Sort(), Swap() und Compare() oder auch operator<(), oder?

Das wäre dann aber ein abstrakter Datentyp (=Class) und kein Struct mehr 
;)
Das mit den in Unterfunktionen zu untergliedern, ist sicher richtig,das 
ist auch zugegebenermaßen meine Schwäche :D
Ich habe es aber jetz einmal bis hier hin, das würde ich ungern über den 
Haufen werfen.
Die fehlerhafte Schleife habe ich zwar gefunden, den Fehler da drin aber 
noch nicht.
Das ist der aktuelle Stand der Dinge:
1
void sortieren (node* head, int anzahl) {
2
  node *new_head, *start, *pos, *aktuell, *temp, *tausch;
3
  int swap;
4
  int i = 0;
5
  int j = 0;
6
  int k = 0;
7
  int stelle = 0;
8
  int zahl_links, zahl_rechts;
9
  node *feld[anzahl];
10
11
  aktuell = head;
12
  while (aktuell != 0) {
13
    aktuell->markiert = false;
14
    aktuell->getauscht = false;
15
    aktuell = aktuell->next;
16
  }
17
18
  new_head = new node;
19
  new_head->zahl_l = head->zahl_l;
20
  new_head->zahl_r = head->zahl_r;
21
  new_head->next = 0;
22
  pos = head->next;
23
  aktuell = head->next;
24
25
start = head;
26
    while (start != NULL) {  
27
      aktuell = head;
28
      while (aktuell != 0) {
29
        aktuell->markiert = false;
30
        aktuell = aktuell->next;
31
      }
32
      tausch = new node;
33
      new_head = new node;
34
      new_head->zahl_l = head->zahl_l;
35
      new_head->zahl_r = head->zahl_r;
36
      new_head->next = 0;
37
      pos = head->next;
38
      aktuell = head->next;
39
        
40
      tausch->zahl_l = new_head->zahl_l;
41
      tausch->zahl_r = new_head->zahl_r;
42
      if (start->getauscht != true) {            // löst den Speicherzugriffsfehler übrigens
43
        new_head->zahl_l = start->zahl_l;
44
        new_head->zahl_r = start->zahl_r;
45
      }
46
      else {
47
        new_head->zahl_l = start->zahl_r;
48
        new_head->zahl_r = start->zahl_l;
49
      }
50
      start->getauscht = false;
51
      start->zahl_l = tausch->zahl_l;
52
      start->zahl_r = tausch->zahl_r;
53
54
      while (aktuell->zahl_r != new_head->zahl_l && i < 7) {
55
  //      printf("[%d;%d]\n", aktuell->zahl_l, aktuell->zahl_r);
56
        pos = head->next;  
57
        head->markiert = true;
58
        if (pos == 0)
59
          return;
60
        if (i == 0) 
61
          aktuell = new_head;
62
        
63
  
64
        while (pos->zahl_r == aktuell->zahl_r && pos->zahl_l == aktuell->zahl_l) {
65
          pos = pos->next;
66
          j++;
67
68
          if (pos == 0)
69
            pos = head->next;
70
          if (pos->markiert == true)
71
            pos = pos->next;
72
          if (pos == 0)
73
            pos = head->next;
74
        
75
          while (pos->zahl_r != aktuell->zahl_r && pos->zahl_l != aktuell->zahl_r) {
76
            pos = pos->next;
77
            if (pos == 0)
78
              pos = head->next;
79
            if (pos->markiert == true)
80
              pos = pos->next;
81
            if (pos == 0)
82
              pos = head->next;
83
          }
84
  
85
          if (pos == 0)
86
            pos = head->next;
87
        }
88
89
        pos->markiert = true;
90
91
        if (j == 0) {
92
          while (pos->zahl_r != aktuell->zahl_r && pos->zahl_l != aktuell->zahl_r) {
93
            pos = pos->next;
94
            if (pos->markiert == true)
95
              pos = pos->next;
96
            if (pos == 0)
97
              pos = head->next;
98
          }
99
        }  
100
        j = 0;
101
        pos->markiert = true;
102
103
        zahl_links = pos->zahl_l;
104
        zahl_rechts = pos->zahl_r;
105
        if (pos->zahl_l != aktuell->zahl_r) {
106
          swap = pos->zahl_l;
107
          pos->zahl_l = pos->zahl_r;
108
          pos->zahl_r = swap;
109
          pos->getauscht = true;
110
        }
111
        temp = new node;
112
        temp->zahl_l = pos->zahl_l;
113
        temp->zahl_r = pos->zahl_r;
114
  
115
    /*    if (getauscht == true) {
116
          tausch->zahl_l = temp->zahl_l;
117
          tausch->zahl_r = temp->zahl_r;
118
          pos->zahl_l = tausch->zahl_r;
119
          pos->zahl_r = tausch->zahl_l;
120
          printf("[%d;%d]", pos->zahl_l, pos->zahl_r);
121
          printf("drin\n");
122
        }
123
        else {
124
          printf("draußen\n");
125
        } */
126
        aktuell->next = temp;
127
        aktuell = temp;
128
        aktuell->next = 0;
129
        i++;
130
        k++;
131
      }        // beendet while-Schleife
132
      i = 0;
133
      j = 0;
134
      k = 0;
135
      start->zahl_l = new_head->zahl_l;
136
      start->zahl_r = new_head->zahl_r;
137
      feld[stelle] = new_head;
138
      while (feld[stelle] != NULL) {
139
        printf("[%d;%d]", feld[stelle]->zahl_l, feld[stelle]->zahl_r);
140
        feld[stelle] = feld[stelle]->next;
141
      }
142
      printf("\n");
143
      stelle++;
144
      start = start->next;
145
    }
Der Fehler muss in der Schleife sein:
1
while (aktuell->zahl_r != new_head->zahl_l) {  
2
...
3
}
Die Liste
1 3 (= Dominostein mit links 1 und rechts 3)
3 5
2 6
6 23
23 42
2 42
1 5
liefert nach Abbruch folgendes, fehlerhaftes Ergebnis:
[1;3][3;5][5;1] (richtig)
[3;5] (falsch, keine Ahnung was er hier macht)
[2;6][6;23][23;42][42;23][23;42][42;23][23;42][42;23] (falsch)
[6;23][23;42][42;2][2;6] (richtig)
[42;23][23;6][6;2][2;42] (falsch, da er eigentlich mit [23;42] beginnen 
muss)
[42;2][2;6][6;23][23;6][6;23][23;6][6;23][23;6] (falsch)
[1;5][5;3][3;1] (richtig)

Wenn ich die Sache nicht nach 7 Durchläufen abbrechen würde, würde er 
sich irgendwann in der Dauerschleife verlieren und nur noch [23;42] und 
[42;23] jeweils im Wechsel ausgeben. Woran könnte das liegen?

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

In C++ unterscheidet sich eine struct von der class nur durch den 
Defaultwert für die Komponentensichbarkeit. Bei class ist diese private, 
bei einer struct ist sie public.

von Max F. (mexxe)


Lesenswert?

Carl D. schrieb:
> In C++ unterscheidet sich eine struct von der class nur durch den
> Defaultwert für die Komponentensichbarkeit. Bei class ist diese private,
> bei einer struct ist sie public.

Stimmt, im Struct sind alle Methoden grundsätzlich Public.

Nichtsdestotrotz hilft mir das im Moment nicht weiter und mir ist es 
leider auch nicht gelungen die Fehlerquelle weiter einzugrenzen.

von Sven B. (scummos)


Lesenswert?

Max F. schrieb:
> Nichtsdestotrotz hilft mir das im Moment nicht weiter und mir ist es
> leider auch nicht gelungen die Fehlerquelle weiter einzugrenzen.
Hast du denn mal einen Debugger benutzt? Sorry, ich habe keine Lust mich 
in diesen Code einzulesen, das musst du im Detail schon selber tun ...

von Carl D. (jcw2)


Lesenswert?

Max F. schrieb:
> Carl D. schrieb:
>> In C++ unterscheidet sich eine struct von der class nur durch den
>> Defaultwert für die Komponentensichbarkeit. Bei class ist diese private,
>> bei einer struct ist sie public.
>
> Stimmt, im Struct sind alle Methoden grundsätzlich Public.
>
> Nichtsdestotrotz hilft mir das im Moment nicht weiter und mir ist es
> leider auch nicht gelungen die Fehlerquelle weiter einzugrenzen.

Meine Idee (am Anfang) war ja auch eher dich zu Gedanken über dein 
Nudelprogramm anzuregen. Eine seiner Nachteil erlebst du ja gerade.

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.