Variable wird falsch initialisiert



  • Hallo,
    ich arbeite mit dem MS VC++ Express 2005.
    Mein Problem ist folgendes:
    Ich definiere eine Variable, die ich gleich mit einer anderen initialisiere.
    Nun wird meine neue Variable aber nicht initialisiert, sie bleibt nach wie vor auf dem Wert 8 (bei jedem Debugginglauf) obwohl sie mit 0 initialisiert wird.
    Wenn ich die Variable mit volatile definiere tritt das Problem nicht auf, dieses Schlüsselwort ist hier meiner Meinung nach aber nicht angebracht.

    Hier noch der Code:

    int CRechenablauf::End_of_Element(string Ausdruck, const int Elem)
    {
    	/*volatile*/ int End = Elem;	//<- die Problemstelle
    
    	//...
    	//einiger Code if else, End wird mehrfach bedingt inkrementiert
    	//...
    
    	return End;
    }
    

    Ich denke es handelt sich hier um ein Compilerproblem, deshalb hier in diesem Forum, falls ich damit falsch liege bitte ich um Entschuldigung - ich weiß das Problem nicht recht zu klassifizieren.



  • Poste doch mal bitte ein vollständiges Beispiel.
    Ist es Release oder Debug?



  • Debug

    OK ich denke zwar kaum, dass der weitere Code von belang ist, aber bitte:

    int CRechenablauf::End_of_Element(string Ausdruck, const int Elem)
    {
    	int End = Elem;
    	//es werden immer 5 Elemente Existieren, da die letzten 5 Zeichen
    	//in Ausdruck von Korrigiere_Syntax(...) eingefügt wurden
    	//("$eoA$") hiermit wird das Ende angezeigt
    	if(var(Ausdruck.substr(Elem, 5)))
    	{
    		End++;
    	}
    	else if(zahl(Ausdruck[Elem]))
    	{
    		while(End < signed(Ausdruck.length()) && zahl(Ausdruck[End]))
    		{
    			End++;
    		}
    	}
    	else if(rechenzeichen(Ausdruck.substr(Elem, 5)))
    	{
    		string c = Ausdruck.substr(Elem, 5);
    
    		//Todo: folgendes aus der Funktion entfernen u.U. DLLMain()
    		map<string, int> tab;
    		tab["+"] = PLUS;
    		tab["-"] = MINUS;
    		/*...*/
    		tab["atan"] = ATAN;
    		tab["$eoA$"] = EOAUSDRUCK;
    
    		while(tab[c] == EOAUSDRUCK && c.length() > 0)
    		{
    			if(tab[c] != EOAUSDRUCK)
    			{
    				break;
    			}
    			else
    			{
    				c.erase(c.end());
    			}
    			End++;
    		}
    		End++;
    	}
    	return End;
    }
    

    Das ganze ist Inhalt einer Neubearbeitung (m)einer Formelparser-DLL.
    Zum verständnis der Funktion, ihre Aufgabe ist es das Ende eines grammatischen Elementes zu finden, nicht jede Zahl ist schließlich nur ein Zeichen lang und Funktionen á la sin auch nicht. Es wird der Indexwert des Endes des Elements zurückgegeben.



  • du sollst ein vollständigen, minmalen quelltext zeigen!



  • Bitte was versteht ihr unter vollständig, soll ich das gesamte Projekt posten? Das sind einige Zeilen - das würd ich mir nicht mal selber antun.
    Die Klasse dürfte auch kaum von belang sein, aber von mir aus, hier die Klasse:

    class CRechenablauf
    {	
    	template<class T> friend class TTermElement;
    
    	public:
    		CRechenablauf ();
    		CRechenablauf (string Ausdruck);
    		CRechenablauf (const CRechenablauf & rhs);
    		~CRechenablauf ();
    		CRechenablauf & operator= (const CRechenablauf & rhs);
    		vector < TTermElement < double > > GetRechenablauf ();
    		double GetVar (string var);
    
    	private:
    		void Ini();
    		void Korrigiere_Syntax (string &Ausdruck);
    		void Parse (string Ausdruck);
    		int End_of_Element(string Ausdruck, const int Elem);
    		double StrToDouble(string str) const;
    
    		map < string, double > m_vars;
    
    		vector < TTermElement < double > > m_Rechenablauf;
    };
    
    bool zahl (char c);
    int rechenzeichen (string c);
    bool var (string c);
    

    Sagt doch bitte was ihr alles sehen wollt, ich denke es ist genauso sinnlos den Code Stückchenweise zu posten wie es auch sinnlos ist (grob geschätzt) 800 Zeilen am Stück zu posten.



  • Mach ne Sicherheitskopie von deinem Projekt und nimm dann Stück für Stück alles raus bis der Quellcode minimal ist, aber trotzdem noch der Fehler sichbar ist. Und dabei wirste wahrscheinlich schon selbst den Fehler finden.



  • Es muss an der Codeoptimierung des Compilers liegen, wenn ich die abschalte arbeitet der Code ordentlich. Habe Codeoptimierung auf maximale Geschwindigkeit gestellt.
    Auch andere Variablen werden nicht initialisiert bzw. der Debugger zeigt mir deren Existenz auch nicht an (was bei meinem eigentlichen Problem aber der Fall war).
    Ich musste gerade noch feststellen, dass bei volatile der Wert dann zwar 0 ist, aber sich nicht ändern lässt (vom Programm) mit static (welches ich aus reiner Verzweiflung mal davorgeschrieben habe) lief es dann OK - aber static hat da nix verloren.



  • Ger001 schrieb:

    Es muss an der Codeoptimierung des Compilers liegen, wenn ich die abschalte arbeitet der Code ordentlich.

    Die Erfahrung sagt mir, dass das äußerst unwahrscheinlich ist; der Fehler liegt viel eher darin, dass du dich z.B. darauf verlässt, dass lokale Variablen oder Arrays standardmäßig 0 enthalten, was z.B. in der optimierten Version nicht mehr der Fall ist.

    Falls das wirklich ein Compilerfehler ist, wäre der außerdem deiner Beschreibung nach so gravierend, dass den schon sehr viele Leute vor dir hätten bemerken müssen.

    Wie der ungenannte Poster vom 26.04.2006, 16:45 geschrieben hat: Reduzier deinen Code solange, bis du den Fehler auf ein kleines Codestück lokalisieren kannst.



  • Ger001 schrieb:

    Wenn ich die Variable mit volatile definiere tritt das Problem nicht auf, dieses Schlüsselwort ist hier meiner Meinung nach aber nicht angebracht.

    kommt mir bekannt vor. guckst du: http://www.c-plusplus.net/forum/viewtopic-var-t-is-141287-and-start-is-0.html

    Christoph schrieb:

    Falls das wirklich ein Compilerfehler ist, wäre der außerdem deiner Beschreibung nach so gravierend, dass den schon sehr viele Leute vor dir hätten bemerken müssen.

    das muss nicht sein. solche fehler treten super selten auf wenn compiler bzw. linker beim optimieren was falsch machen. oft ist der fehler wieder weg, wenn man an einer anderen stelle im code etwas ändert, das mit der fehlerhaften stelle in keinem logischen zusammenhang steht.



  • Vielleicht hat es auch was mit der DLL zu tun!



  • So, ich habe mir jetzt mal das Disassembly angetan, die Variable wird schlicht und einfach nicht auf dem RAM bearbeitet sondern bleibt von Anfang bis Ende im Register (einmal in esi und sonst edi). Der Debugger guckt natürlich nach den Variablen im RAM, dort sind sie aber nicht wirklich, deshalb passiert scheinbar nichts.
    Das was ich als Resultat dieses Fehlers abgetan hatte liegt näher an der Ursache.

    Der Code ist hier nicht gepostet, std::string::substr(off, count) macht Probleme, es bekommt die richtigen Parameter, dann kann ich weil z.T. die Quelltextdateien Fehlen den Fehler nur schwer lokalisieren ich vermute es werden falsche Adressen an memcopy übergeben. Der Assemblercode liest sich für mich aber sehr zäh, wo da was falsch läuft kann ich echt nicht sagen. Die Parameter für substr(...) sind auf jeden Fall korrekt und werden auch korrekt empfangen, aber der Rückgabestring ist für die Tonne. Der Fehler schein also weniger bei mir als eher in der Implementierung/Compilierung der Standardbibliotheken zu liegen.

    Der Fehler tritt auf mit /O2 (Optimierung: maximale Geschwindigkeit) und mit /Ox (komplette Optimierung).

    Soll ich mal in nem Microsoft Forum posten?



  • Benutzen EXE und DLL die DLL Version der CRT?



  • Ger001 schrieb:

    Der Fehler tritt auf mit /O2 (Optimierung: maximale Geschwindigkeit) und mit /Ox (komplette Optimierung).

    Vorher hattest Du noch was von "Debug" geredet...
    Wenn Du eine Release-Version debuggest, dann werden oft lokale Variablen nicht korrekt angezeigt, der der Optimierer diese entfernt oder "inline" gemacht hat.

    Hast Du nicht ein kleines vollständiges Code-Beispiel?



  • Um den Fehler einzugrenzen hatte ich einfach mal einen try/catch Block um den substr Aufruf geschrieben. Es wird nichts gefangen, aber der Fehler tritt auch nicht mehr auf, später im Code tritt das selbe Phänomen nochmals auf.

    void CRechenablauf::Parse(string Ausdruck)
    {
    	while(Ausdruck.length() > 0)
    	{
    		if(zahl(Ausdruck[0]))
    		{
    			int eoElem = End_of_Element(Ausdruck, 0);
    			string strZahl;
    			try
    			{
    				strZahl = Ausdruck.substr(0, eoElem);
    			}
    			catch(exception x)
    			{
    				string f;
    				f = x.what();
    			}
    			double dZahl = StrToDouble(strZahl);
    		}
    		else
    		{
    			//belanglos da Fehler im Debugger schon vorher sichtbar
    		}
    	}
    }
    

    Um nochmal die Symptome zu beschreiben, ohne try/catch bekommt strZahl zufällige Werte aus dem Speicher (z.B. "ìp³´È6paÊ").
    Aus dem Diassembly Debugging kann ich sagen, dass die Kopieranweisungen in memcpy_s nicht ausgeführt werden, im letzten Moment (ich glaub 2 Zeilen vorher) springt er in eine Funktion names CopyUnwindUp. Mit beenden dieser Funktion geht es über die ganzen Funktionsebenen schnell zurück in meinen C++ Code oben, allerdings wird außer einigen Register hin und herschieben (vmtl. Rückgabewerte usw.) nichts weiter gemacht, jedenfalls nicht mit meinem String.
    Hier ein Auszug aus dem Disassembly:

    041DCC57  test        edi,3 
    041DCC5D  jne         CopyLeadUp (41DCC74h) 
    041DCC5F  shr         ecx,2 
    041DCC62  and         edx,3 
    041DCC65  cmp         ecx,8 
    041DCC68  jb          CopyUnwindUp (41DCC94h) 
    041DCC6A  rep movs    dword ptr es:[edi],dword ptr [esi] 
    041DCC6C  jmp         dword ptr TrailUpVec (41DCD84h)[edx*4]
    

    Das ganze wird aus der Funktion string::assign(...) aufgerufen, diese ruft
    mit

    _Traits_helper::copy_s<_Traits>(_Myptr(), _Myres, _Right._Myptr() + _Roff, _Num);
    

    memcpy_s auf, woraus das Disassembly oben ein Ausschnitt ist.

    Gut zu sehen ist, dass esi auf den Source-String zeigt und edi auf den Destination-String.

    Das ganze ist Debug Compilert, nicht Release. Debuginformationsformat: /Zi

    @................ (ich versuch mal deine "Frage" zu interpretieren)
    also das ganze ist eine DLL, komplett ohne CRT sondern reines unmanaged C++.
    Diese DLL wird von einer WindowsForms EXE aufgerufen, da der Fehler mit dem try/catch Block nicht mehr auftritt kann ich sagen, dass zumindest der Datenaustausch zwischen DLL und EXE keine Probleme bereitet.
    Wo kann ich die CRT Version rausbekommen? (falls es doch von Belang ist).



  • Ger001 schrieb:

    also das ganze ist eine DLL, komplett ohne CRT sondern reines unmanaged C++.

    Das kann also schon mal nicht stimmen... was soll den sonst "string" sein!? Das sieht sehr nach STL aus, welche die CRT voraussetzt.

    [quote="Ger001"]Diese DLL wird von einer WindowsForms EXE aufgerufen, da der Fehler mit dem try/catch Block nicht mehr auftritt kann ich sagen, dass zumindest der Datenaustausch zwischen DLL und EXE keine Probleme bereitet.
    Wo kann ich die CRT Version rausbekommen? (falls es doch von Belang ist)./quote]Wie rufst Du denn die Methode aus der EXE auf? Ist dies EXE auch C++? Falls, ja, dann musst Du auch die DLL-Version der CRT verwenden (Projekteinstellungen|C/C++|Code generation|Runtime library)



  • Ja string ist hier std::string.
    Ähmm im ernst STL brauch CRT? Find ich ja fast, dass das ne Schweinerei ist...

    EXE ist ebenfalls C++.

    So Hab auf Multithreaded-Debug-DLL umgestellt (/MDd). Jetzt klappts ohne try/catch...

    Aber wieso brauch ich hier unbedingt die DLL Version???
    (Ich sollte mich wohl doch mal mehr mit .NET auseinandersetzen als nur um ein Fenster auf den Schirm zu bringen)

    Falls der Aufruf noch von Interesse ist, der erfolgt über diese Wrapperfunktion:

    using namespace System;
    using namespace System::Runtime::InteropServices;
    
    	[DllImport("formelteiler.dll", EntryPoint = "?_Term@@YANPAD@Z")]
    		double __cdecl _Term([MarshalAs(UnmanagedType::LPStr)] String ^ Term);
    

    in der unmanaged DLL sieht das so aus:

    #define DLLEXPORT __declspec(dllexport)
    
    DLLEXPORT double _Term(char * Term);
    

    Diese Wrapperfunktion erstellt ein Klassenobjekt, welches wieder andere Klassenobjekte erstellt usw., bis wir irgendwann bei meinem (ehemaligen!?) Problem ankommen.

    std::string Ausdruck
    

    entspricht aber letztenendes

    char * Term
    

    aus der Wrapperfunktion.

    [EDIT]
    Zu früh gefreut, wenn ich als die CRT Multithreaded-Debug-Dll einbinde hat ich Probleme mit iteratoren, die auf einmal nicht mehr dereferenzierbar sind und exceptions werfen lassen.
    Das trat vorher nicht auf.
    [/EDIT]


Anmelden zum Antworten