Verbesserung der Umwandlung eines ANSI- in einen Wide-String gesucht



  • Ich habe zwei Funktionen geschrieben, um normale Strings in Unicode-Strings und umgekehrt unzuwandeln. Die eine Funktion sieht so aus:

    LPCWSTR ConvertANSIToWideString(LPCSTR str)
    {
    	const UINT LENGTH = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, NULL, 0);
    	LPWSTR wstr = new WCHAR[LENGTH];
    
    	MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, wstr, LENGTH);
    
    	return wstr;
    }
    

    Mein Problem ist jetzt, daß ich den zurückzugebenden String mit new initialisiere (denn wenn wstr NULL wäre, würde es nicht funktionieren) und in diesem Fall muß er ja auch wieder gelöscht werden, was natürlich unprakisch ist, da das eine Aufgabe für den Code außerhalb der Funktion wäre.
    Wie muß ich die Funktion umschreiben, damit ich auf das new verzichten kann?



  • Eine große Verbesserung wäre, wenn Du std::wstring verwenden würdest, dann müsste man sich um das Freigeben den Speichers nicht kümmern...

    Auch solltest Du mögliche Fehler-Codes beachten!

    ALSO: Lies bitte die Doku zu "MultiByteToWideChar"!



  • Jochen Kalmbach schrieb:

    Eine große Verbesserung wäre, wenn Du std::wstring verwenden würdest, dann müsste man sich um das Freigeben den Speichers nicht kümmern...

    Ich weiß, das hab ich bisher auch immer gemacht. Nur geht es mir im Moment wirklich um die Programmierung mit der WinAPI. Und wenn ich dort irgendwelche Strings, die ich aus windows.h-Funktionen oder -Strukturen habe, umwandeln will, dann sollte es hierfür schon eine Funktion geben, die auch genau die Windows-Typen entgegennimmt. Klar, man kann auch dem std::string eine LPCSTR-Variable zuweisen. Und den std::wstring-Rückgabewert kann man mit der Funktion c_str() in LPCWSTR zurückverwandeln.

    Aber:

    Erstens würde das Programm abstürzen, wenn ich der Funktion einen LPCSTR-Wert übergebe, der auf NULL zeigt. Folgendes Beispiel verdeutlicht das:

    #include <string>
    #include <windows.h>
    
    using namespace std;
    
    void MeineMessageBox(string text);
    
    int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
    	LPCSTR text1 = "Hallo";
    	LPCSTR text2 = NULL;
    
    	MeineMessageBox(text1); // Alles klar.
    
    	try
    	{
    		MeineMessageBox(text2);
    	}
    	catch (...)
    	{
    		MessageBoxA(NULL, "Tja, war wohl nichts!", "Fehler!", MB_OK);
    
    		// Und es gibt keine meines Wissens nach keine
    		// Möglichkeit für die Funktion MeineMessageBox,
    		// zu prüfen, ob ihr Parameter NULL ist.
    	}
    
    	return 0;
    }
    
    void MeineMessageBox(string text)
    {
    	MessageBoxA(NULL, text.c_str(), "Message Box", MB_OK);
    }
    

    Zweitens wäre das nichtmal ein zu vermeidender Fall, denn wenn ich zum Beispiel eine WNDCLASSEXA-Varable in eine WNDCLASSEXW-Variable umwandeln will, dann kann es sein, daß lpszMenuName NULL ist. Und dann soll bei der Umwandlung auch ganz regulär NULL zurückgegeben werden, ohne daß es sich hier um irgendeine Art Fehler handeln würde. Und das bekommt man mit den String-Klassen so nicht hin. Deshalb müssen die Funktionen im Zusammenhang der WinAPI-Programmierung durchaus die Möglichkeit bieten, C-Style-Strings zu übernehmen und zurückzugeben.

    Jochen Kalmbach schrieb:

    Auch solltest Du mögliche Fehler-Codes beachten!

    ALSO: Lies bitte die Doku zu "MultiByteToWideChar"!

    Na gut, um Fehlercodes kann ich mich immer noch kümmern, wenn das Ding so funktioniert, wie es soll. Und die Dokumentation hab ich jetzt auch gelesen, aber ich weiß immer noch nicht, wie es möglich ist, den erhaltenen String zurückzugeben, ohne daß da noch ein new rumschwirrt, das eigentlich ein delete bräuchte.



  • aber ich weiß immer noch nicht, wie es möglich ist, den erhaltenen String zurückzugeben, ohne daß da noch ein new rumschwirrt, das eigentlich ein delete bräuchte.

    Direkt ist das garnicht möglich.

    Das "NULL in" ist im übrigen kein Problem, das kannst du abfangen indem du einfach vor der Umwandlung die Parameter checkst.

    Das "NULL out" ist etwas tricky. Normalerweise kann man es ignorieren und "NULL in" einfach in einen leeren std::(w)string verwandeln. Wenn du unbedingt "NULL in, NULL out" brauchst ist das aber auch einfach nur etwas Arbeit. Eine kleine Helper-Klasse welche z.B. einen std::wstring enthält, sowie die Information ob ein NULL Pointer zurückgegeben werden soll, oder ein Zeiger auf den Inhalt des Strings. Dann definierst du noch passende Konstruktoren, und ggf. einen operator wchar_t const*, fertig.



  • hustbaer schrieb:

    aber ich weiß immer noch nicht, wie es möglich ist, den erhaltenen String zurückzugeben, ohne daß da noch ein new rumschwirrt, das eigentlich ein delete bräuchte.

    Direkt ist das garnicht möglich.

    Das "NULL in" ist im übrigen kein Problem, das kannst du abfangen indem du einfach vor der Umwandlung die Parameter checkst.

    Wie soll ich denn einen Parameter vom Typ std::string, dem ich einen LPCSTR übergebe, auf NULL prüfen?

    hustbaer schrieb:

    Das "NULL out" ist etwas tricky. Normalerweise kann man es ignorieren und "NULL in" einfach in einen leeren std::(w)string verwandeln. Wenn du unbedingt "NULL in, NULL out" brauchst ist das aber auch einfach nur etwas Arbeit. Eine kleine Helper-Klasse welche z.B. einen std::wstring enthält, sowie die Information ob ein NULL Pointer zurückgegeben werden soll, oder ein Zeiger auf den Inhalt des Strings. Dann definierst du noch passende Konstruktoren, und ggf. einen operator wchar_t const*, fertig.

    Wieso muß ich hier überhaupt mit den C++-Strings arbeiten? Meine Funktion ist, wie gesagt, direkt für die Umwandlung von Strings, die in der WinAPI vorkommen. Ich finde es da nicht besonders günstig (oder passend), die Umwandlungsfunktion mit std::string zu schreiben, zumal die Umwandlung selbst durch MultiByteToWideChar wieder mit LPCSTR getätigt wird. Ich hätte also einen LPCSTR, den ich als Parameter an einen std::string übergebe. Aus dem mache ich in der Funktion einen LPCSTR, um den LPCWSTR zu bekommen. Der wird dann wieder an einen std::wstring übergeben, den ich zurückliefere und der außerhalb der Funktion in einen LPCWSTR umgewandelt wird. Da finde ich, sollte ich gleich von vornherein nur mit LPCSTR/LPCWSTR arbeiten (und dann vielleicht als Zusatz die Funktion mit std::string um die LPCSTR-Funktion wrappen).

    Wie wird das eigentlich in den originalen WinAPI-Funktionen gemacht? Soweit ich weiß, ruft ja zum Beispiel die MessageBoxA-Funktion einfach nur die MessageBoxW-Funktion auf. Dort müssen die das doch auch irgendwie umwandeln. Und da die WinAPI in C geschrieben ist, gibt es dort keinen std::string. Wie würden diese A-Funktionen also aussehen? Ist davon auszugehen, daß dort überall erst dynamischer Speicher für die Strings reserviert wird? So wie hier

    int MessageBoxA(HWND hWnd, LPCSTR message, LPCSTR caption, UINT buttons)
    {
        LPCWSTR wMessage = malloc(irgendwas);
        MultiByteToWideChar(blablabla, wMessage, blablabla);
    
        LPCWSTR wCaption = malloc(irgendwas);
        MultiByteToWideChar(blablabla, wCaption, blablabla);
    
        int returnValue = MessageBoxW(hWnd, wMessage, wCaption, buttons);
    
        free(wMessage);
        free(wCaption);
    
        return returnValue;    
    }
    


  • Ja, die einzelnen Ansi-Strings werden vorher in Unicode-Strings umgewandelt und nach dem Aufruf der Unicode-Version wird der Speicher wieder freigegeben.

    Hast Du Dir schon einmal die Konvertierungsklassen der ATL (CW2A, CA2W) angeschaut?



  • sri schrieb:

    Ja, die einzelnen Ansi-Strings werden vorher in Unicode-Strings umgewandelt und nach dem Aufruf der Unicode-Version wird der Speicher wieder freigegeben.

    Gibt's irgendwo mal die Implementierung davon zu sehen? Bei Wine sollte das doch möglich sein, oder?

    sri schrieb:

    Hast Du Dir schon einmal die Konvertierungsklassen der ATL (CW2A, CA2W) angeschaut?

    Ja, hab ich gestern gesehen. Aber ich hab sie wieder verworfen, weil es sie nur für Visual C++ gibt und MinGW sie beispielsweise sie nicht kennt.


  • Mod

    Du hast den Source Code der ATL. Es dauert 5 Minuten aus den ATL Klassen und Templates eine eigene Konvertierung zu schreiben.
    Zumal die Implementierung sehr nett ist, effektiv, simpel und absolut verständlich für Jedermann.



  • NES-Spieler schrieb:

    Gibt's irgendwo mal die Implementierung davon zu sehen? Bei Wine sollte das doch möglich sein, oder?

    Ja. MessageBoxA ist in dlls\user32\msgbox.c implementiert.



  • Ich hab mir die ATL-Sachen mal angeguckt, aber soweit ich gesehen habe, brauchen die auch alle einen bereits initialisierten String. Ein Zeiger, der noch auf NULL zeigt, funktioniert da nicht.

    Dann nochmal zum Vorschlag, ich solle mit den C++-Strings arbeiten. Das funktioniert auch nicht, was folgende Funktion verdeutlicht:

    #include <iostream>
    #include <windows.h>
    
    using namespace std;
    
    string Funktion()
    {
    	string str = "Hallo";
    
    	return str;
    }
    
    int main()
    {
    	LPCSTR winAPIString = Funktion().c_str();
    
    	cout << "Dieser Text lautet auf keine Fall 'Hallo':\n"
    	     << winAPIString << '\n';
    }
    

    Denn die c_str()-Funktion liefert ja nur die Adresse des intern gespeicherten Character-Arrays, welche aber schon nicht mehr gültig ist, wenn die Funktion verlassen wird. Das heißt, wenn ich einen ANSI-String habe und will diesen auf WNDCLASSEXW::lpszClassName kopieren, dann könnte ich gar nicht mit einer Konvertierungsfunktion arbeiten, die std::string und std::wstring erwartet.

    Ich weiß also noch immer nicht, wie ich weiter vorgehen soll. Der Vollständigkeit halber werde ich mal zeigen, worum es eigentlich geht: Ich habe ein paar Funktionen, die mir das Erstellen von Fenstern vereinfachen sollen. Jede Funktion, die Strings entgegennimmt, existiert in zwei Ausführungen: Einmal für ANSI-, einmal für Unicode-Strings. Die ANSI-Funktion wandelt den String um und ruft die Unicode-Funktion auf, welche dann die eigentlichen Aufgaben erledigt. Soweit wäre das kein großes Problem, da ich in diesem Fall ja in der ANSI-Funktion einen String auf dem Heap erstellen könnte, den ich wieder lösche, bevor die Funktion verlassen wird. Das Problem liegt vielmehr innerhalb der eigentlichen Unicode-Funktion: Denn für den Fall, daß auf dem Betriebssystem Unicode nicht unterstützt wird, muß der String dort nochmal in einen ANSI-String zurückverwandelt werden:

    HWND Create(/* ... */ LPCWSTR className /* ... */)
    {
    	WNDCLASSEXW wndClassW;
    	WNDCLASSEXA wndClassA;
    
    	// ...
    	wndClassW.lpszClassName = className;
    	// ...
    
    	if (UnicodeIsSupported()) // Eigene Funktion
    		RegisterClassExW(&wndClassW);
    	else
    	{
    		// Wenn Unicode nicht unterstützt wird (Windows 95/98/Me),
    		// dann werden alle wndClassW-Werte nochmal auf wndClassA
    		// kopiert und die Klasse wird entsprechend anders
    		// initialisiert.
    
    		// ...
    		wndClassA.lpszClassName =
    			ConvertToANSIString(wndClassW.lpszClassName); //Eigene Funktion
    		// ...
    
    		RegisterClassExA(&wndClassA);
    	}
    
    	if (UnicodeIsSupported())
    		return CreateWindowExW(/* ... */ wndClassW.lpszClassName /* ... */);
    	else
    		return CreateWindowExA(/* ... */ wndClassA.lpszClassName /* ... */);
    }
    

    Wie kann ich hier die Strings aus der WNDCLASSEXW-Struktur umwandeln? Kann ich sie mit new initialisieren, dann meine ursprüngliche Umwandlungsfunktion (aus meinem ersten Post) nehmen und sie nach dem CreateWindowEx-Aufruf wieder löschen oder werden die Strings innerhalb des WinAPI-Frameworks weiter gebraucht? Wie muß man solche Probleme wie hier geschildert handhaben?


  • Mod

    NES-Spieler schrieb:

    Ich hab mir die ATL-Sachen mal angeguckt, aber soweit ich gesehen habe, brauchen die auch alle einen bereits initialisierten String. Ein Zeiger, der noch auf NULL zeigt, funktioniert da nicht.

    Was hast Du Dir bitte angesehen?
    Die CA2W Funktion und andere Funktionieren perfekt auch mit NULL Zeigern!
    Ebenso A2W!

    Dann nochmal zum Vorschlag, ich solle mit den C++-Strings arbeiten. Das funktioniert auch nicht, was folgende Funktion verdeutlicht:

    #include <iostream>
    #include <windows.h>
    
    using namespace std;
    
    string Funktion()
    {
    	string str = "Hallo";
    
    	return str;
    }
    
    int main()
    {
    	LPCSTR winAPIString = Funktion().c_str();
    
    	cout << "Dieser Text lautet auf keine Fall 'Hallo':\n"
    	     << winAPIString << '\n';
    }
    

    Denn die c_str()-Funktion liefert ja nur die Adresse des intern gespeicherten Character-Arrays, welche aber schon nicht mehr gültig ist, wenn die Funktion verlassen wird.

    Logisch! Das temporäere Objekt ist nur gültig solange eben das Statement gilt.
    Man kann so nicht mit Zeigern arbeiten. Die könntest Du einfach nicht automatisch entsorgen.
    Du musst also das Objekt (std::string) zwischenspeichern. Und schon geht es.

    Das heißt, wenn ich einen ANSI-String habe und will diesen auf WNDCLASSEXW::lpszClassName kopieren, dann könnte ich gar nicht mit einer Konvertierungsfunktion arbeiten, die std::string und std::wstring erwartet.

    Korrekt! Ohne das Du das Objekt auch zwischenspeicherst geht hier gar nichts!
    Dafür gibt es aber auck eine andree Lösung. Denn wie willst Du das Objekt auch dterminsitisch wieder entsorgen.

    Du könntest eine Kontextklasse einrichten wie es mit den alten ATL Klassen und USES-CONVERSION geschieht und diese Klasse auf dem Stack einrichten. In dessen Kontext werden die temnporären Strings gespeichert und entsorgt wenn der Kontext aus dem Skope geht.

    Schau Dr mal die alten ATL Konvertierungen an, die verwenden alloca!
    Die erzeugen eine Persitenz auf dem Stack!

    Ich weiß also noch immer nicht, wie ich weiter vorgehen soll. Der Vollständigkeit halber werde ich mal zeigen, worum es eigentlich geht: Ich habe ein paar Funktionen, die mir das Erstellen von Fenstern vereinfachen sollen. Jede Funktion, die Strings entgegennimmt, existiert in zwei Ausführungen: Einmal für ANSI-, einmal für Unicode-Strings. Die ANSI-Funktion wandelt den String um und ruft die Unicode-Funktion auf, welche dann die eigentlichen Aufgaben erledigt. Soweit wäre das kein großes Problem, da ich in diesem Fall ja in der ANSI-Funktion einen String auf dem Heap erstellen könnte, den ich wieder lösche, bevor die Funktion verlassen wird. Das Problem liegt vielmehr innerhalb der eigentlichen Unicode-Funktion: Denn für den Fall, daß auf dem Betriebssystem Unicode nicht unterstützt wird, muß der String dort nochmal in einen ANSI-String zurückverwandelt werden:

    HWND Create(/* ... */ LPCWSTR className /* ... */)
    {
    	WNDCLASSEXW wndClassW;
    	WNDCLASSEXA wndClassA;
    
    	// ...
    	wndClassW.lpszClassName = className;
    	// ...
    
    	if (UnicodeIsSupported()) // Eigene Funktion
    		RegisterClassExW(&wndClassW);
    	else
    	{
    		// Wenn Unicode nicht unterstützt wird (Windows 95/98/Me),
    		// dann werden alle wndClassW-Werte nochmal auf wndClassA
    		// kopiert und die Klasse wird entsprechend anders
    		// initialisiert.
    
    		// ...
    		wndClassA.lpszClassName =
    			ConvertToANSIString(wndClassW.lpszClassName); //Eigene Funktion
    		// ...
    
    		RegisterClassExA(&wndClassA);
    	}
    
    	if (UnicodeIsSupported())
    		return CreateWindowExW(/* ... */ wndClassW.lpszClassName /* ... */);
    	else
    		return CreateWindowExA(/* ... */ wndClassA.lpszClassName /* ... */);
    }
    

    Wie kann ich hier die Strings aus der WNDCLASSEXW-Struktur umwandeln? Kann ich sie mit new initialisieren, dann meine ursprüngliche Umwandlungsfunktion (aus meinem ersten Post) nehmen und sie nach dem CreateWindowEx-Aufruf wieder löschen oder werden die Strings innerhalb des WinAPI-Frameworks weiter gebraucht? Wie muß man solche Probleme wie hier geschildert handhaben?

    Die alten ATL Makros machen genau das!

    #include <iostream>
    #include <windows.h>
    #include <atlbase.h>
    
    using namespace std;
    
    string Funktion()
    {
    	string str = "Hallo";
    
    	return str;
    }
    
    int main()
    {
             USES_CONVERSION;
    	LPCWSTR winAPIString = A2W(Funktion().c_str());
    
    	cout << "Dieser Text lautet auf keine Fall 'Hallo':\n"
    	     << winAPIString << '\n';
    }
    

    Das geht! 🕶



  • Martin Richter schrieb:

    Was hast Du Dir bitte angesehen?
    Die CA2W Funktion und andere Funktionieren perfekt auch mit NULL Zeigern!
    Ebenso A2W!

    Dieses ATLASSERT(lpw != NULL) in AtlW2AHelper hat mich zu dem Gedanken gebracht. Außerdem hab ich USES_CONVERSION nicht beachtet. Aber egal. Es funktioniert jetzt erstmal.

    Ich hab nur noch eine Frage: Da das ATL-Zeug nur bei Visual C++ existiert und ich die Makros und Funktionen somit direkt in meinen Quellcode kopieren muß, müßte ich noch wissen: Ist sichergestellt, daß alloca bei jedem Compiler dabei ist? Denn soweit ich das sehe, ist "malloc.h" kein Standardheader.


  • Mod

    NES-Spieler schrieb:

    Martin Richter schrieb:

    Was hast Du Dir bitte angesehen?
    Die CA2W Funktion und andere Funktionieren perfekt auch mit NULL Zeigern!
    Ebenso A2W!

    Dieses ATLASSERT(lpw != NULL) in AtlW2AHelper hat mich zu dem Gedanken gebracht. Außerdem hab ich USES_CONVERSION nicht beachtet. Aber egal. Es funktioniert jetzt erstmal.

    Wenn Du es Dir so wichtig, ist einegute Funktion zu finden würde ich Dir raten, die Code-Empfehlungen besser anzusehen. Die ältere ATL-Makros haben drekt einen Test, der den Umgang mit dem NULL Pointer regelt.
    Das hätte sofort auffallen müssen:

    #define A2W(lpa) (\
    	((_lpa = lpa) == NULL) ? NULL : (\
    		_convert = (lstrlenA(_lpa)+1),\
    		(INT_MAX/2<_convert)? NULL :  \
    		ATLA2WHELPER((LPWSTR) alloca(_convert*sizeof(WCHAR)), _lpa, _convert, _acp)))
    

    Ich hab nur noch eine Frage: Da das ATL-Zeug nur bei Visual C++ existiert und ich die Makros und Funktionen somit direkt in meinen Quellcode kopieren muß, müßte ich noch wissen: Ist sichergestellt, daß alloca bei jedem Compiler dabei ist? Denn soweit ich das sehe, ist "malloc.h" kein Standardheader.

    Kann ich Dir so nicht sagen. Frage bitte nach einem spezifischen Compiler.
    Allerdings hat jeder C Compiler mit der entsprchenden CRT, die ich kenne und auch schon verwendet habe eine entsprechende Funktion wie alloca (GNU, Intel)



  • Da wär ich mal wieder. 😃

    Ich habe die Makros jetzt mal in eine Funktion gepackt. (Immerhin sollen sie später in mein Programm kommen und da will ich auch mit Funktionen und nicht mit ätzenden Makros arbeiten. Und so kann man schonmal gucken, ob es überhaupt geht.) Das Problem ist jetzt aber: Solange sich der String in der Konvertierungsfunktion befindet, funktioniert er. Aber außerhalb ist er dann wieder ungültig. Und das, obwohl ich die LPCSTR-Variable per Referenz übergeben habe:

    #include <windows.h>
    #include <atlbase.h>
    
    void Convert(LPCSTR &a, // Übergabe des umzuwandelnden
                            // Strings per Referenz!
                 LPCWSTR w)
    {
    	USES_CONVERSION;
    
    	a = W2A(w);
    	// Hier ist noch alles in Ordnung:
    	// Adresse von a: 0x0065fcbc
    	// Wert    von a: "Ich bin ein String"
    
    	MessageBoxA(NULL, a, "Convert", MB_OK);
    	// Korrekte Ausgabe
    }
    
    int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
    	LPCSTR str = NULL;
    
    	Convert(str, L"Ich bin ein String.");
    	// Korrekte Adresse, aber der String ist leer:
    	// Adresse von str: 0x0065fcbc
    	// Wert    von str: ""
    
    	MessageBoxA(NULL, str, "Main", MB_OK);
    	//Leere Message-Box
    }
    

    Sollte der mit alloca generierte Speicherplatz nicht solange gültig sein wie auch die Variable gültig ist? Warum also ist zwar die Adresse von str aus WinMain noch immer die gleiche wie die von a aus Convert, während aber alle Zeichen auf 0 gesetzt sind?


  • Mod

    <kopfschüttel>
    Das ist doch kein Auto-Pointer System!
    Die konvertierten Strings werden auf dem Stack abgelegt! Deine Funktion kann so nicht funktionieren. Sie funktionieren so lange wie der aktuelle Stack-Frame nicht verlassen wird.

    Das was Du hier willst bedeutet den konsequenten Einsatz von std::string oder CString und bei Bedarf eben die Konvertierungen an dieser Stelle. Die konvertierten Strings dürfen nur in dem Funktionsblock verwendet werden in dem sie definiert wurden. RÜckgabeist so unmöglich, genauso wie das persistente Speichern in anderen Strukturen.



  • Martin Richter schrieb:

    <kopfschüttel>
    Das ist doch kein Auto-Pointer System!
    Die konvertierten Strings werden auf dem Stack abgelegt! Deine Funktion kann so nicht funktionieren. Sie funktionieren so lange wie der aktuelle Stack-Frame nicht verlassen wird.

    Aber ich habe die Variable doch per Referenz übergeben. Das heißt, der Stack-Rahmen dafür endet erst am Ende der Main-Funktion.

    Martin Richter schrieb:

    Das was Du hier willst bedeutet den konsequenten Einsatz von std::string oder CString und bei Bedarf eben die Konvertierungen an dieser Stelle. Die konvertierten Strings dürfen nur in dem Funktionsblock verwendet werden in dem sie definiert wurden. RÜckgabeist so unmöglich, genauso wie das persistente Speichern in anderen Strukturen.

    Deshalb hab ich den String ja auch nicht zurückgegeben, sondern per Referenz übergeben.


  • Mod

    NES-Spieler schrieb:

    Martin Richter schrieb:

    <kopfschüttel>
    Das ist doch kein Auto-Pointer System!
    Die konvertierten Strings werden auf dem Stack abgelegt! Deine Funktion kann so nicht funktionieren. Sie funktionieren so lange wie der aktuelle Stack-Frame nicht verlassen wird.

    Aber ich habe die Variable doch per Referenz übergeben. Das heißt, der Stack-Rahmen dafür endet erst am Ende der Main-Funktion.

    Aber der Strin Array selbst liegt doch im Stackrahmen der Funktion!
    Es gibt keine eferenz-Zählung für einfache Pointer! Punkt!

    Genauso falsch ist das hier und kommt Deinem Code sehr nahe:

    char *MyString()
    {
    char szTest[20];
    strcpy(szTest,"TEST");
    return szTest;
    }
    

    Martin Richter schrieb:

    Das was Du hier willst bedeutet den konsequenten Einsatz von std::string oder CString und bei Bedarf eben die Konvertierungen an dieser Stelle. Die konvertierten Strings dürfen nur in dem Funktionsblock verwendet werden in dem sie definiert wurden. RÜckgabeist so unmöglich, genauso wie das persistente Speichern in anderen Strukturen.

    Deshalb hab ich den String ja auch nicht zurückgegeben, sondern per Referenz übergeben.

    Kennst Du den Unterschied zwischen einer Referenz und einem Zeiger und vor allem einer Referenz auf einen Zeiger?
    Wenn nicht beschäftige dich mal damit und versuche vor allem mal raus zu bekommen welchen Einfluss eine Referenz auf den Storage hat auf den sie verweist... 🕶

    Antwort (hier vorab): Keinen!



  • Martin Richter schrieb:

    NES-Spieler schrieb:

    Aber ich habe die Variable doch per Referenz übergeben. Das heißt, der Stack-Rahmen dafür endet erst am Ende der Main-Funktion.

    Aber der Strin Array selbst liegt doch im Stackrahmen der Funktion!
    Es gibt keine eferenz-Zählung für einfache Pointer! Punkt!

    Ich weiß, daß es keine Referenzzählung gibt. (Hätte ich das geglaubt, dann hätte ich die Funktion ja einfach so geschrieben, daß der umgewandelte String per return zurückgegeben wird und nicht, daß ich den umzuwandelnden String per Referenz übergebe.) Ich hatte gedacht, daß der Speicherplatz auf dem Stack dann der ursprünglich deklarierten Variable, die per referenz übergeben wurde, zugewiesen wird, aber ich sehe jetzt, was Du meinst.

    Gibt es nun überhaupt eine Möglichkeit, einen String in einen Unicode-String umzuwandeln, ohne
    a) ohne den String dynamisch auf dem Heap zu erstellen (was ja eine manuelle Löschung mit delete erfordern würde)
    b) ohne die Benutzung von Makros
    c) ohne den Konvertierungscode an der entsprechenden Stelle immer wieder neu zu schreiben
    d) ohne die Verwendung von C++-Strings (denn wenn ich einen String aus einer WNDCLASSEXA-Struktur in einen String aus einer WNDCLASSEXW-Struktur umwandeln will, nützt mir das nichts)?



  • Ich glaube NES-Spieler kennt den Unterschied zwischen einem Zeiger und einem String bzw. Array nicht.



  • Ironischerweise gibt es da in C nichtmal wirklich einen durch die Quellcodesyntax gesicherten Unterschied:

    int main()
    {
    	char c = 'A';
    
    	char *p = &c; // p ist ein char-Pointer.
    	p = "Hallo";  // Nein, Moment! p ist ein String.
    	p[3] = 'X';   // Oder doch ein char-Array?
    }
    

    😉


Anmelden zum Antworten