[geloest] C++ casting(?) Problem

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
MichaDi
Beiträge: 26
Registriert: 08.02.2022 12:54:07

[geloest] C++ casting(?) Problem

Beitrag von MichaDi » 26.06.2022 17:22:25

Der folgende Code funktioniert wie erwartet

Code: Alles auswählen

std::vector<char*> commandParts;
std::string test = settings.mainPath + "Programmname";
commandParts.push_back((char*)test.c_str());
char **command = commandParts.data();
std::cout << "command[0]=" << command[0] << std::endl;
Bei der folgenden Version bleibt "command[0]" aber leer!?

Code: Alles auswählen

std::vector<char*> commandParts;
commandParts.push_back((char*)(settings.mainPath + "Programmname").c_str());   //<-"test" ist hier integriert und existiert nicht mehr separat!
char **command = commandParts.data();
std::cout << "command[0]=" << command[0] << std::endl;
Ich sollte vielleicht noch erwähnen, dass "settings.mainPath" vom Typ "STD::STRING" ist!
Zuletzt geändert von MichaDi am 26.06.2022 19:17:01, insgesamt 1-mal geändert.

JTH
Moderator
Beiträge: 3077
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: C++ casting(?) Problem

Beitrag von JTH » 26.06.2022 18:14:41

Du verwaltest in deinem commandParts in der zweiten Variante dangling Pointer – Pointer auf Objekte, die gar nicht mehr existieren.

Das Problem ist diese Zeile:

Code: Alles auswählen

commandParts.push_back((char*)(settings.mainPath + "Programmname").c_str());
Du nimmst dort per c_str() einen char * von einem String, das nach der Zeile nicht mehr existiert.

Der Operator + eines std::string gibt dir einen neuen String zurück. Da aber nicht dafür gesorgt wird, dass der weiter lebt (in dem er z.B. in einem zweiten Vector abgelegt wird), wird er nach der Verwendung wieder freigegeben. Dies hier erzeugt den neuen String:

Code: Alles auswählen

settings.mainPath + "Programmname"

Warum benutzt du überhaupt einen std::vector<char *>? Warum nicht direkt std::vector<std::string>? Damit ginge genau das, was du hier (soweit erkennbar) vorhast:

Code: Alles auswählen

std::vector<std::string> commandParts;
commandParts.push_back(settings.mainPath + "Programmname");
std::cout << "command[0]=" << command[0] << std::endl;
Kurz und bündig – und C++-typisch, ohne fehlerträchtige Pointerjongliererei, wo sie eh nicht sinnvoll oder notwendig ist.

Noch zur Verdeutlichung, in C übersetzt macht deine zweite Variante Folgendes:

Code: Alles auswählen

char *commandParts[1];
commandParts[0] = strcat(mainPath, "Programmname");
free(commandParts[0]);         // Der zusammenghängte String wird freigegeben, der Pointer aber nicht verworfen …
char **command = commandParts; // … und sogar nach dem Freigeben weiterbenutzt.
printf("%s\n", command[0]);

Und noch als Ergänzung:
std::vector::c_str() gibt einen const char * zurück. Du castest hier das const weg. Falls du sogar auf dem Wege dort reinschreiben willst, ist das strenggenommen undefined Behavior.
Manchmal bekannt als Just (another) Terminal Hacker.

MichaDi
Beiträge: 26
Registriert: 08.02.2022 12:54:07

Re: C++ casting(?) Problem

Beitrag von MichaDi » 26.06.2022 18:22:40

Vielen Dank für die Antwort.
Ich benötige das "char *", da ich den Vektor (der diverse Parameter enthält) an "execv" übergeben will:
execv(command[0], &command[0]);

JTH
Moderator
Beiträge: 3077
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: [geloest] C++ casting(?) Problem

Beitrag von JTH » 26.06.2022 19:34:55

Die beste Lösung hängt dann ein wenig vom Drumherum der bisherigen Zeilen ab. Entweder fängst du doch an, die Strings von Hand mit new/delete zu verwalten, das dann wie gehabt in einem std::vector<const char *>. Oder du nimmst C++-iger und wenn sich aus deinem restlichen Code so anbietet, weiter einen std::vector<std::string> und konvertierst den für execv() zu einem Vektor von const char *.

Janz top modern ginge das mit C++20 etwa so:

Code: Alles auswählen

#include <ranges>
#include <string>
#include <vector>

auto to_cstr_vec(const std::vector<std::string>& str_vec)
{
    auto tv = std::views::transform(str_vec, &std::string::c_str);
    return std::vector(tv.begin(), tv.end());
}
Um den resultierenden Vektor benutzen zu können, muss aber der zugrundeliegende Vektor aus std::string weiter existieren. Sonst hast du das gleiche Problem wie eingangs.
Manchmal bekannt als Just (another) Terminal Hacker.

MichaDi
Beiträge: 26
Registriert: 08.02.2022 12:54:07

Re: [geloest] C++ casting(?) Problem

Beitrag von MichaDi » 26.06.2022 20:22:49

Danke für den Tipp, arbeite aber noch mit C++17!

JTH
Moderator
Beiträge: 3077
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: [geloest] C++ casting(?) Problem

Beitrag von JTH » 26.06.2022 22:39:42

Da ginge es etwas gesprächiger wohl so oder ähnlich

Code: Alles auswählen

#include <algorithm>
#include <functional>
#include <string>
#include <vector>

auto to_cstr_vec(const std::vector<std::string>& str_vec)
{
    std::vector<const char *> cstr_vec(std::size(str_vec));
    std::transform(std::begin(str_vec), std::end(str_vec), std::begin(cstr_vec), std::mem_fn(&std::string::c_str));
    return cstr_vec;
}
Manchmal bekannt als Just (another) Terminal Hacker.

Antworten