Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

28.03.2014, 14:00

[C++] std::queue::front() verursacht heap probleme

Guten Tag,

ich habe folgende sehr einfache Methode in einer DLL:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
std::string CConnection::GetBuffer()
{
    std::string tmp = "";
    if(!mInputBuffer.empty())
    {
        tmp = mInputBuffer.front(); //vom Typ std::queue<std::string>
        mInputBuffer.pop();
    }
    return tmp;
}

Beim Aufruf dieser Methode in meinem Hauptprogramm erhalte ich in der Release-Version die Meldung, dass Windows einen Haltepunkt ausgelöst hat und werde im Debugger auf die Datei free.c verwiesen, wo die Methode HeapFree fehlgeschlagen ist. In der Debug-Version tritt dieser Fehler nicht auf. Wenn ich die Zeile tmp = mInputBuffer.front() auskommentiere, tritt dieser Fehler auch nicht auf. Ich verstehe nicht, was hier falsch laufen kann, da meine Methode eine Wert by-value zurückgibt und somit die Referenz, die front() zurückgibt ruhig ungültig werden nach dem pop(). Von euch vielleicht ne Idee? Viele Grüße
newby

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

28.03.2014, 14:06

Der Fehler muss woanders liegen, hier ist alles OK. Rufst du die Methode womöglich über einen ungültigen Zeiger auf?
PS: Du musst den String nicht mit "" initialisieren, der ist standardmäßig leer.

3

28.03.2014, 14:16

Der Zeiger, auf dem ich die Methode aufrufe, ist korrekt, da ich ihn auch direkt vorher für andere Methodenaufrufe nutze, welche korrekt funktionieren.
Was ich noch vergessen habe: Der Aufruf direkt löst keinen Fehler aus, der Fehler entsteht erst, wenn die aufrufende Methode verlassen wird und irgendwelche Ressourcen wieder freigegeben werden. Dennoch bin ich mir sicher, dass es an dieser Methode liegt, da kein Fehler eintritt, wenn ich sie auskommentiere.
Hier die aufrufende Methode:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void Lobby::processConnection()
{
    //[...]
    while(mConnection->HasNext())
    {
        std::string buffer = mConnection->GetBuffer();
        std::string code = buffer.substr(0, 7);
        std::string content = buffer.substr(7, std::string::npos);
        if(code.compare("Player:") == 0)
        //[...]
        else if(code.compare("PlyRdy:") == 0)
        //[...]
    }
}

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

4

28.03.2014, 14:42

Das ist Code in einer DLL? Jede DLL hat ihren eigenen Heap, und std::string allokiert unter Umständen vom Heap. Es kann sein, dass Du den String im Hauptprogramm erstellt hast und ihn dann in die DLL gibst. Der Zeiger im String zeigt also auf einen Block des Hauptprogramm-Heaps, aber wenn der Code in der DLL den String dann in pop() zerstört, wird der Zeiger als Block aus dem DLL-Heap interpretiert. Das knallt, im glücklichsten Fall nur als Exception, im schlimmsten Fall als Absturz.

Du darfst grundsätzlich KEINE dynamisch allokierten Dinge (und damit auch quasi alle STL-Objekte) in eine DLL reingeben oder aus einer rausnehmen. Da ich vermute, dass das nur so ein überflüssiges "Ich will meine Engine in einer DLL haben"-Ding ist, lautet meine Empfehlung: lass den Mist und linke statisch. All die anderen Vorteile des statischen Linkens kriegst Du damit auch noch geschenkt.

Für den Fall, dass Du wirklich dynamische Bindung zur Laufzeit brauchst, will ich nichts gesagt haben.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

5

28.03.2014, 14:53

Heißt das, dass ich prinzipiell keine std::string Objekte zwischen Dll und Hauptprogramm tauschen sollte? Wie sieht die Alternative aus, wenn ich eine Methode in der DLL habe, die soetwas zurückgibt (wie hier)? Ich muss gestehen, dass ich nicht genau weiß, was statisch und dynamisch gelinkt heißt. Um diese dll zu nutzen inkludiere ich eine Headerdatei und linke eine .lib datei. In der Headerdatei steht dann alles was ich nutze in einem extern "C++" Block und alle genutzten Klassen sind mit __declspec(dllimport) gekennzeichnet. Ich vermute, dass mein Problem hiermit zusammenhängt, da auch Fehler in der Datei xstring beim allokieren von mehr Speicher auftreten, kann aber selber keine sinnvolle Lösung finden.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

6

28.03.2014, 15:12

@Schrompf: Das mit den separaten Heaps stimmt so nicht ganz.
Wenn das Programm und alle DLLs dieselbe dynamische Runtime benutzen, benutzen auch alle denselben Heap, und es ist kein Problem.
Quelle: http://msdn.microsoft.com/en-us/library/ms235460.aspx

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

7

28.03.2014, 15:19

Stimmt. QT z.B. nutzt das aus, um Allokationen übertragen zu können. Dafür muss die QT-DLL dann aber auch exakt mit dem Compiler und den Einstellungen gebaut werden, die man nachher auch im Hauptprogramm benutzt.

@newby: in Kurzform:

* Statisch Linken: Du hast eine Lib mit kompiliertem Code, die der Linker beim Zusammenbau Deiner Exe anzieht und mit in die Exe hineinbaut. Dabei kann er auch je nach Schlauheit über Lib-Grenzen hinweg optimieren und aller Code läuft in der selben Laufzeitumgebung.

* Dynamisch linken: Du hast eine Lib und eine DLL. In der Lib steht nur automatisch erzeugter Code, der erst beim Programmstart die DLL lädt und alle Funktionszeiger darin auflöst. Der eigentliche Code steht in der DLL. Jeder Funktionsaufruf geht also durch einen Funktionszeiger und ist damit ein bisschen teurer als ein Funktionsaufruf innerhalb des eigenen Programms. Und außerdem bringt die DLL ihre eigene Laufzeitumgebung mit, die kompatibel mit der des Hauptprogramms sein kann oder auch nicht.

Ich empfehle bei eigenem Code immer statisches Linken. Da kann man sich den ganzen DLL-Export sparen, gewinnt potentiell einiges an Performance und vermeidet außerdem Ärger wie den hier aus dem Thread.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

8

28.03.2014, 15:25

Vielen Dank euch beiden, das war interessant und hat mir zudem sehr geholfen. Wäre ich wohl selber niemals drauf gekommen. Ich konnte den Fehler beheben, indem ich die Laufzeitumgebung in den Projekteinstellungen auf /MD gestellt habe. Nur noch eine kurze Frage, wie implementiert man denn Methoden in einer DLL, die einen String zurückgeben sollen? Das soll ja durchaus vorkommen ;) Nochmal tausend Dank für die Erklärung, die war echt gut!

Werbeanzeige