char* aus funktionsrueckgabewert
-
Entweder du machst deine eigene Stringklasse, die Besitz vom Speicher übernehmen kann, oder du gibst einen std::unique_ptr<char[]> zurück. Die Variante mit std::string ist zwar korrekt, erstellt aber leider eine Kopie. Ich hoffe, dass die da in Zukunft noch was machen, um sowas ohne Kopie zu ermöglichen.
Edit: Warum brauchst du eigentlich ein rohes char-Array? Warum nicht direkt einen std::string anlegen und den zurückgeben?
-
com_ptr<T> schrieb:
Wie kann ich einen char* aus 'ner funktion zurueckgeben?
Das ganze sieht wie folgt aus:const char* fun() { //calculate len const char *c = new char[len]; // fill c return c; }Hier gibts jetzt gleich 2 Probleme:
- Es wird ein pointer auf nicht existierenden speicher zurueckgegeben
- Es entsteht ein leak, da niemals delete [] c aufgerufen wird.
Das mit (1) musst Du mir nochmal erklären.
Und ob da ein Leak entsteht, hängt ja davon ab, wie das restliche Programm aussieht.com_ptr<T> schrieb:
Wie kann ich das ohne nutzung von std::string loesen?
Definiere das Problem bitte genauer.
-
Edit: Wozu das Geraffel mit new[] und delete[], in der std::string Version?
Naja, weil der String ne dynamische laenge hat?

Ansonsten find ich das nen sehr guten ansatz, mit dem char* uebergeben.Edit: Warum brauchst du eigentlich ein rohes char-Array? Warum nicht direkt einen std::string anlegen und den zurückgeben?
Ich lese eine binaere datei ein, und habe das gefuehl, dass bei der konvertierung etwas "abgeschnitten" wird/werden koennte.
-
Die komplette Funktion zum Laden der binaeren datei sieht nun so aus:
const std::string ShaderManager::loadCompiledShader(const std::string& name) { ifstream in(name.c_str(), ios::in || ios::binary); in.seekg(0, ios::end); std::streamoff length = in.tellg(); in.seekg(0, ios::beg); char *buffer = new char[static_cast<unsigned int>(length)]; in.read(buffer, length); in.close(); string strBuffer(buffer); delete [] buffer; return strBuffer; }Was sagt ihr dazu?
-
Dass man von std::string keinen Zeiger bekommen kann, über den man den String modifizieren darf, ist bedauerlich.
In vielen Fällen mann man den operator [] statt dessen verwenden, denn die einzelnen Zeichen in einem String zu verändern ist ja schliesslich nicht verboten.
In vielen Fällen kann man aber auch nicht ausweichen, und muss kopieren.@com_ptr<T>
Deine Funktion ist vollkommen korrekt. Sie verlangt bloss vom Aufrufer, dass dieser den Speicher mit delete [] wieder freigibt.
Und natürlich vorausgesetzt, dass zwischen dem new[] und dem return keine Exceptions fliegen können. Sonst hättest du nämlich wirklich ein Leak.Die Variante von cooky451 ist vielleicht besser, da der Aufrufer dann entscheiden kann wie er den Speicher verwaltet.
Nur solltest du die Funktion dann um einen Parameter erweitern, in dem der Aufrufer die Länge des Puffers übergibt. Um zu verhindern, dass die Funktion nen Pufferüberlauf macht.
Natürlich muss man dann wieder irgendwie den Fall behandeln, dass der Puffer zu klein ist, was die Sache wieder etwas lästig macht. Vor allem auf Seite des Aufrufers, der dann u.U. dynamisch den Puffer vergrössern muss.com_ptr<T> schrieb:
Waere folgende loesung mit std::string korrekt?
const strC fun() { //calculate len const char *c = new char[len]; // fill c string strC(c); delete [] c; return strC; }Wenn man den offensichtlichen Flüchtigkeitsfehler mal ignoriert ("const strC fun()" muss "std::string fun()" heissen), dann ja.
Bzw. gibt es hier wieder das selbe Problem mit Exceptions.
Gegenvorschlag:std::string fun() { // calculate len std::vector<char> buffer(len); const char* c = &buffer[0]; // fill c return std::string(buffer.begin(), buffer.end()); }Das ist das, was ich immer mache, wenn ich Strings von z.B. OS Funktionen zurückgegeben bekomme, die ich dann als std::string brauche.
Man kann das ganze auch noch etwas optimieren, wenn man eine Obergrenze für "len" Kennt, die in den meisten Fällen ausreichen wird:
std::string fun() { static size_t const stackBufferSize = 512; size_t len = ...; char const* c; char stackBuffer[stackBufferSize]; std::vector<char> buffer; if (len <= stackBufferSize) c = stackBuffer; else { buffer.resize(len); c = &buffer[0]; } // fill c return std::string(c, c + len); }Wenn man sowas häufiger braucht, kann man sich eine kleine Helper-Klasse basteln, wodurch man dann etwas in der Art schreiben kann:
std::string fun() { size_t len = ...; SmartBuffer<char, 512> buffer(len); char const* c = buffer.begin(); // fill c return std::string(buffer.begin(), buffer.end()); // oder gleich buffer.ToString() }EDIT: nachdem du aus einer Datei liest, vergiss die Optimierung. Der Dateizugriff ist um ein vielfaches langsamer, als die unnötige Speicheranforderung in std::vector<>. D.h. es zahlt sich nicht aus die wegzuoptimieren.
-
com_ptr<T> schrieb:
- Es wird ein pointer auf nicht existierenden speicher zurueckgegeben
Nein, der Speicher wurde ja mit "new" angefordert. Wenn das fehlschlägt, wirft C++ IMHO eine bad_alloc-Exception.
com_ptr<T> schrieb:
- Es entsteht ein leak, da niemals delete [] c aufgerufen wird.
Der Aufrufer kann "delete" auf den Rückgabewert der Funktionen anwenden und würde den Speicher damit freigeben.
Btw, funktionieren würde es so, allerdings bezweifle ich, dass dein Code dem entspricht, was gemeinhin als "guter C++-Stil" verstanden wird.
^^ Worum ich mich persönlich aber auch nie schere.
-
Was für Daten liest du denn aus der Datei und was passiert danach mit dem String?
-
Na, ich lese ne binaere datei ein. Fakt ist, dass ich wenn ich mit obiger funktion
die datei lade, seh ich, dass im debugger nen andrer string ist, als den, den die datei tatsaechlich beinhaltet. Also muss da was krumm sein.
Ich konvertiere den zurueckgegebenen std::string wieder in einen const char* mittels std::string::c_str(), was hier aber nicht weiter von bedeutung ist. Denn der "falsche" wert ist ja bereits im std::string vorhanden.
-
Naja, ich bezweifle mal, dass du die Daten intern binär brauchst. Du wirst sie doch sicherlich in andere Datentypen wie ints, floats, doubles o.ä. zerlegen, oder? In diesem Fall könntest du dir den Umweg über den String nämlich sparen.
-
Im konkreten handelt es sich um
diese: http://dl.dropbox.com/u/6559846/com_ptrT.bin
Datei, die ich lesen moechte.Debugging zeigt, dass die datei ~1500 bytes enthaelt.
Interessanterweise werden aber nur 21 gelesen (Laenge des std::strings).
Gelesen wird: "DXBCB÷”=}¤óUý<)íœæ’".
-
@314159265358979
Nein, natuerlich brauche ich sie in binaer, sonst wuerd ich sie ja nicht binaer lesen, und das noch in nen char*, oder? *hehe*
-
Enthält die Datei 0-Bytes? In diesem Fall würde der std::string nämlich nicht alles kopieren, wenn du den char const* ctor nimmst.
-
Enthält die Datei 0-Bytes? In diesem Fall würde der std::string nämlich nicht alles kopieren, wenn du den char const* ctor nimmst.
Yep, das war auch meine 1. Vermutung. Leider kann ich das nicht sagen, da es eine generierte datei ist. Wie loese ich das Problem?
-
Indem du den Range-Konstruktor verwendest:
std::string s(ptr, ptr + len);
-
Wenn das sowieso kein "Test-String" ist, dann wäre vermutlich
std::vector<char>der passendere Rückgabewerttyp.
Alternativ kannst du den Template-Konstruktor von std::string verwenden, der ein Iterator-Paar (begin, end) übergeben bekommt. Damit kann man in std::string auch wunderbar 0-Zeichen reintun.
-
Habe nun mal folgendes versucht:
const std::string ShaderManager::loadCompiledShader(const std::string& name) { ifstream in(name.c_str(), ios::in || ios::binary); in.seekg(0, ios::end); std::streamoff length = in.tellg(); in.seekg(0, ios::beg); vector<char> buffer(length); in.read(&buffer[0], length); in.close(); string strBuffer(buffer.cbegin(), buffer.cend()); return strBuffer; }Problem: Bereits das eingelesene ist "gecutted" (= nur 21 zeichen).
Hat es jemand mal mit der geposteten datei versucht?
-
ios::in || ios::binary- da ist ein | zuviel.
Flags verknüpft man mit|, nicht mit||.
-
Danke, sehr guter Punkt.
Geht nun!
Vielen Dank nochmal an @hustbaer und 314159265358979!
Hoffe es ist nicht <zu> tragisch, dass wir etwas vom Thema abgeschweift sind.
Aber man sieht immer wieder, was fuer ein "riesen Rattenschwanz" hinter vermeintlich "einfachen" fragen liegt.
Euch noch einen schoenen Abend!
-
Wie wärs mit dem:
const std::string ShaderManager::loadCompiledShader(const std::string& name) { std::ifstream in(name.c_str(), ios::in | ios::binary); return std::string((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>()); }
-
