std::Vektor zum char*-Array

Lesezeit: 9 Minuten

Benutzeravatar von Christopher DuBois
Christopher DuBois

Ich habe ein std::vector<std::string> die ich für a verwenden muss C Argument der Funktion, das lautet char* foo. Ich habe gesehen, wie man a umwandelt std::string zu char*. Als Neuling bei C++ich versuche herauszufinden, wie diese Konvertierung für jedes Element des Vektors durchgeführt und erzeugt wird char* Reihe.

Ich habe mehrere eng verwandte SO-Fragen gesehen, aber die meisten scheinen Wege zu veranschaulichen, wie man in die andere Richtung gehen und etwas erschaffen kann std::vector<std::string>.

  • Was ist die genaue C-Schnittstelle. Wir können verschiedene Dinge tun, je nachdem, wo sich die const befinden und wie die Funktion den Speicher während der Verwendung behandelt (C-Funktionen können unangenehme Dinge tun, wie z. B. den Aufruf von realloc).

    – Martin York

    13. August 2011 um 6:03 Uhr

  • Model* ModelInitialize (char *fnames, int nterms)

    – Christopher DuBois

    13. August 2011 um 6:14 Uhr

  • Diese Funktion akzeptiert char*nicht char** wie in deiner frage. Welches ist es?

    – Luc Danton

    13. August 2011 um 6:39 Uhr

  • verkohlen*. Sorry für die Verwirrung. Ich habe versehentlich eine Funktion betrachtet, die char** fnames als Argument akzeptiert und später ModelInitialize aufruft.

    – Christopher DuBois

    13. August 2011 um 6:46 Uhr


  • Die Funktionssignatur enthält eindeutig nicht genügend Informationen, um die richtige Vorgehensweise zu bestimmen. Ist Eigentum an fnames übertragen in ModelInitialize? (wenn ja: wie muss er zugeordnet worden sein?) Ist der aufrufende Code dazu gedacht delete, free oder anderweitig freigeben Model zurückgekehrt von ModelInitialize? (wenn ja: wie muss es freigegeben werden?) Muss fnames ein nullterminierter String sein? Auf welche Weise evtl fnames modifiziert sein?

    – Mankarse

    13. August 2011 um 7:43 Uhr


Benutzeravatar von Nawaz
Nawaz

Sie können verwenden std::transform wie:

std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);  

Was Sie implementieren müssen convert() wie:

char *convert(const std::string & s)
{
   char *pc = new char[s.size()+1];
   std::strcpy(pc, s.c_str());
   return pc; 
}

Testcode:

int main() {
       std::vector<std::string>  vs;
       vs.push_back("std::string");
       vs.push_back("std::vector<std::string>");
       vs.push_back("char*");
       vs.push_back("std::vector<char*>");
       std::vector<char*>  vc;

       std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);   

       for ( size_t i = 0 ; i < vc.size() ; i++ )
            std::cout << vc[i] << std::endl;

       for ( size_t i = 0 ; i < vc.size() ; i++ )
            delete [] vc[i];
}

Ausgabe:

std::string
std::vector<std::string>
char*
std::vector<char*>

Online-Demo: http://ideone.com/U6QZ5

Sie können verwenden &vc[0] wo immer Sie brauchen char**.

Beachten Sie, dass da wir verwenden new Speicher für jeden zuzuweisen std::string (in convert Funktion), müssen wir am Ende den Speicher freigeben. Dies gibt Ihnen die Flexibilität, den Vektor zu ändern vs; du kannst push_back Wenn Sie weitere Zeichenfolgen hinzufügen möchten, löschen Sie die vorhandene aus vsund vc (dh vector<char*> bleibt gültig!

Aber wenn Sie diese Flexibilität nicht wollen, dann können Sie diese nutzen convert Funktion:

const char *convert(const std::string & s)
{
   return s.c_str();
}

Und du musst dich ändern std::vector<char*> zu std::vector<const char*>.

Jetzt nach der Transformation, wenn Sie sich ändern vs durch Einfügen neuer Zeichenfolgen oder durch Löschen der alten daraus, dann alle char* in vc könnte ungültig werden. Das ist ein wichtiger Punkt. Ein weiterer wichtiger Punkt ist, dass Sie es nicht verwenden müssen delete vc[i] in Ihrem Code mehr.

  • Kurze Frage: Warum ist das Löschen [] vc-Teil erforderlich, wenn wir einen std::vector verwenden?

    – Christopher DuBois

    13. August 2011 um 6:51 Uhr

  • @Christopher: Weil wir verwenden new Speicher zuzuweisen für char*deshalb machen wir delete vc[i]. Aber wir weisen keinen Speicher für zu char**daher tun wir es nicht delete vc.

    – Nawaz

    13. August 2011 um 7:04 Uhr

  • Dieser Code leckt, wenn das neue in convert wirft. Es wäre viel besser, a zu verwenden std::vector<char>.

    – Mankarse

    13. August 2011 um 7:32 Uhr

  • @Christopher: Siehe meine Antwort. Jetzt hat es mehr Erklärung.

    – Nawaz

    13. August 2011 um 7:36 Uhr

  • @Mankarse: std::vector<char> wo? In Konvertierungsfunktion? Lokale Variable?

    – Nawaz

    13. August 2011 um 7:40 Uhr

Das Beste, was Sie tun können, ist ein zuzuweisen std::vector von const char* die gleiche Größe wie Ihr Vektor. Gehen Sie dann durch jedes Element des Vektors und rufen Sie c_str() um das String-Array zu erhalten und das entsprechende Element des Arrays zu speichern. Dann können Sie den Zeiger auf das erste Element dieses Vektors an die betreffende Funktion übergeben.

Der Code würde so aussehen:

std::vector<const char *> cStrArray;
cStrArray.reserve(origVector.size());
for(int index = 0; index < origVector.size(); ++index)
{
  cStrArray.push_back(origVector[index].c_str());
}

//NO RESIZING OF origVector!!!!

SomeCFunction(&cStrArray[0], cStrArray.size());

Beachten Sie, dass Sie kann nicht Ermöglichen Sie es, dass der ursprüngliche Vektor der Zeichenfolgen zwischen dem Abrufen der Größe geändert wird const char*s aus der std::stringsund die Zeit, zu der Sie die C-Funktion aufrufen.

  • Gibt c_str() kein const char zurück? Wird das ein Problem sein, wenn ich nur ein Zeichen* brauche? (Ich habe die genaue Schnittstelle in die Kommentare aufgenommen.)

    – Christopher DuBois

    13. August 2011 um 6:16 Uhr

  • Sie könnten auch std::vectorcStrArray( origVector.size()+1, NULL); und dann im Iterator cStrArray verwenden[i]=ursprünglicher Vektor[i].c_str(); Dies kann bei Funktionen wie execv() helfen. Aber wie der obige Hinweis sagt, könnten wir mehr Informationen über ModelInitialize verwenden.

    – Don hell

    14. Juli 2012 um 21:44 Uhr

Benutzeravatar von GWW
GWW

Das sollte funktionieren:

char ** arr = new char*[vec.size()];
for(size_t i = 0; i < vec.size(); i++){
    arr[i] = new char[vec[i].size() + 1];
    strcpy(arr[i], vec[i].c_str());
}

BEARBEITEN:

So würden Sie diese Datenstrukturen freigeben, vorausgesetzt, vec hat immer noch die richtige Anzahl von Elementen. Wenn Ihre C-Funktion dieses Array irgendwie ändert, müssen Sie die Größe möglicherweise auf andere Weise ermitteln.

for(size_t i = 0; i < vec.size(); i++){
    delete [] arr[i];
}
delete [] arr;

Nochmals BEARBEITEN:

Es ist möglicherweise nicht erforderlich, die Zeichenfolgen zu kopieren, wenn Ihre C-Funktion die Zeichenfolgen nicht ändert. Wenn Sie erläutern können, wie Ihre Benutzeroberfläche aussieht, können wir Ihnen sicher besser helfen.

  • Sie müssen zeigen, wie man dieses Array löscht, zumal es so kompliziert ist. Verwendung nicht vergessen delete[].

    – Nicol Bolas

    13. August 2011 um 6:03 Uhr

  • Dies leckt, wenn die new in dem for wirft. Es wäre viel besser, a zu verwenden std::vector.

    – Mankarse

    13. August 2011 um 7:21 Uhr

Benutzeravatar von Luc Danton
Luca Danton

Eine C++0x-Lösung, bei der Elemente von std::string werden garantiert zusammenhängend gespeichert:

std::vector<std::string> strings = /* from somewhere */;
int nterms = /* from somewhere */;

// using std::transform is a possibility depending on what you want
// to do with the result of the call
std::for_each(strings.begin(), string.end(), [nterms](std::string& s)
{ ModelInitialize(&s[0], nterms); }

Wenn die Funktion null ihr Argument beendet, dann nach dem Aufruf (s.begin(), s.end()) vielleicht nicht aussagekräftig. Sie können dies nachbearbeiten, um das zu beheben:

s = std::string(s.begin(), std::find(s.begin(), s.end(), '\0'));

Eine aufwändigere Version, die jede Zeichenfolge separat in a kopiert char[]:

typedef std::unique_ptr<char[]> pointer;
std::vector<pointer> args;
std::transform(strings.begin(), strings.end()
               , std::back_inserter(args)
               , [](std::string const& s) -> pointer
{
    pointer p(new char[s.size()]);
    std::copy(s.begin(), s.end(), &p[0]);
    return p;
});

std::for_each(args.begin(), args.end(), [nterms](pointer& p)
{ ModelInitialize(p.get(), nterms); });

const char* ist auch dasselbe wie char*, nur unterschiedlich in der const_ness, Ihre Schnittstellenmethode akzeptiert sowohl konstante als auch nicht konstante Zeichenfolgen.

Gibt c_str() kein const char zurück? Wird das ein Problem sein, wenn ich nur ein Zeichen* brauche?

Ja, es gibt eine konstante Zeichenfolge zurück und nein, es sollte kein Problem geben

const char*a="something";
////whatever it is here
const char* retfunc(const char*a)
{
   char*temp=a;
   //process then return temp
}

Die Rückgabe eines lokalen Objekts wird von vielen Leuten nicht akzeptiert, und dieses kleine Beispiel wird so wie es ist bereitgestellt.

  • Dieser Code wird nicht kompiliert. Es ist weder legal noch sicher, einen Zeiger auf eine Konstante einem Zeiger auf eine Nicht-Konstante zuzuweisen.

    – Mankarse

    13. August 2011 um 7:28 Uhr

  • “Die Rückgabe eines lokalen Objekts wird von vielen Leuten nicht akzeptiert” // Nein, das ist Müll. Rückkehr a Hinweis oder Zeiger zu einem lokalen Objekt wird von der Sprache oder von Compilern nicht akzeptiert. Aber das ist nicht dasselbe.

    – Leichtigkeitsrennen im Orbit

    13. August 2011 um 15:52 Uhr

  • Und dieser Code ist nicht nur ungültig, sondern selbst wenn Sie es wären eigentlich warf die weg constness, es wäre eine schrecklich dumme Sache zu tun.

    – Leichtigkeitsrennen im Orbit

    13. August 2011 um 15:52 Uhr

  • Danke Tomalak für deine Korrekturen und Kommentare, bitte sei nicht zu hart, es ist eine andere Idee, wie man es sicherer macht, hängt immer noch von der tatsächlichen Codierungserfahrung des OP ab …

    – Markus

    13. August 2011 um 15:59 Uhr

  • “const char* ist auch das gleiche wie char*, nur anders in der const_ness”, das ist so hilfreich wie die Aussage, dass “Äpfel das gleiche wie Orangen sind, nur anders”

    – 463035818_ist_keine_Nummer

    14. November 2017 um 13:51 Uhr

Benutzeravatar von user1438233
Benutzer1438233

Die Elemente eines Vektors sind zusammenhängend gespeichertalso ist der beste und einfachste Weg:

std::vector<char> v;
char* c = &v[0];

  • Dieser Code wird nicht kompiliert. Es ist weder legal noch sicher, einen Zeiger auf eine Konstante einem Zeiger auf eine Nicht-Konstante zuzuweisen.

    – Mankarse

    13. August 2011 um 7:28 Uhr

  • “Die Rückgabe eines lokalen Objekts wird von vielen Leuten nicht akzeptiert” // Nein, das ist Müll. Rückkehr a Hinweis oder Zeiger zu einem lokalen Objekt wird von der Sprache oder von Compilern nicht akzeptiert. Aber das ist nicht dasselbe.

    – Leichtigkeitsrennen im Orbit

    13. August 2011 um 15:52 Uhr

  • Und dieser Code ist nicht nur ungültig, sondern selbst wenn Sie es wären eigentlich warf die weg constness, es wäre eine schrecklich dumme Sache zu tun.

    – Leichtigkeitsrennen im Orbit

    13. August 2011 um 15:52 Uhr

  • Danke Tomalak für deine Korrekturen und Kommentare, bitte sei nicht zu hart, es ist eine andere Idee, wie man es sicherer macht, hängt immer noch von der tatsächlichen Codierungserfahrung des OP ab …

    – Markus

    13. August 2011 um 15:59 Uhr

  • “const char* ist auch das gleiche wie char*, nur anders in der const_ness”, das ist so hilfreich wie die Aussage, dass “Äpfel das gleiche wie Orangen sind, nur anders”

    – 463035818_ist_keine_Nummer

    14. November 2017 um 13:51 Uhr

1387090cookie-checkstd::Vektor zum char*-Array

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy