【问题标题】:Converting a std string to char* using const cast使用 const cast 将 std 字符串转换为 char*
【发布时间】:2014-10-07 04:48:32
【问题描述】:

在尝试创建 shell 几天后,我请求了一些帮助。我已经开始使用不同的数据结构超过 4 次,并请求解决以下问题。我有一个字符串,我需要将它分解成单独的参数并有一个指向它的指针。我最终将 args 传递给 exec 函数,但由于我似乎无法正确填充 args,我得到了有趣的结果,这是正在发生的事情的简化版本

char* args[100];
int counter=0;
string temp = "some text and stuff here";
stringstream s (temp);
while(s>> temp)
{
    cout << "TOKEN " << counter << " =" << temp <<endl;
    args[counter]=const_cast<char *> (temp.c_str());
    counter++;
}
//print the debug info
      for( int ii=0; args[ii] != NULL; ii++ )
    {
      cout << "Argument OUT " << ii << ": " << args[ii] << endl;
    }

这段代码不起作用,我不明白为什么。 结果将“此处”存储在 args 的每个值中,但计数器会发生变化。

TOKEN 0 =some
TOKEN 1 =text
TOKEN 2 =and
TOKEN 3 =stuff
TOKEN 4 =here
Argument OUT 0: here
Argument OUT 1: here
Argument OUT 2: here
Argument OUT 3: here
Argument OUT 4: here

【问题讨论】:

  • c_str 返回一个 const 指针是有原因的!
  • @NeilKirk 常量问题与指向同一个地方的指针无关。请注意,他需要非常量指针,因为这是 exec*() 系列函数所需要的,即使它们不修改传递的字符串,所以 const_cast 在这里是安全的。导致问题的原因是重复使用相同的 std::string 对象。

标签: c++ string const-cast


【解决方案1】:

当你这样做时:

args[counter]=const_cast<char *> (temp.c_str());

您没有复制字符串,只是存储了指向其内容的指针。所以当然它们都指向同一个temp 字符串,这使得打印它们时的值都相同。

如果您只是将std::vector&lt;std::string&gt; 用于args,这会容易得多。

【讨论】:

    【解决方案2】:

    可能是因为temp 对象正在重用其内部分配。当您存储c_str() 结果时,您只存储了一个内存地址。 std::string 类不会在您每次从字符串流中读取到它时创建一个全新的分配,而是重用它已经拥有的分配(如果可能的话)。

    此外,在您对 std::string 对象执行 任何操作 后,使用 c_str() 返回的指针会调用未定义的行为。1

    如果可能,只需将args 更改为std::vector&lt;std::string&gt;。如果这是不可能的,那么您需要 strdup() c_str() 返回的指针,以便创建一个全新的分配来复制当时字符串的值。当然,您必须在完成后记住free() 分配。

    此外,丢弃const 限定符并写入指针会导致未定义的行为。2您至少需要将args 更改为const char * args[100];,但我会强烈建议改用字符串向量。


    1http://en.cppreference.com/w/cpp/string/basic_string/c_str

    c_str()获得的指针可能会被以下行为无效:

    • 将对字符串的非常量引用传递给任何标准库函数,或者
    • 在字符串上调用非常量成员函数,不包括operator[]at()front()back()begin()rbegin()end()rend()

    2http://en.cppreference.com/w/cpp/string/basic_string/c_str

    写入通过c_str() 访问的字符数组是未定义的行为。


    根据您的评论表明您需要使用exec(),听起来您确实需要一个指向char 的指针数组。但是,我们仍然可以使用向量来做到这一点。您需要一个向量来保存std::string 对象,该对象将拥有char* 分配。然后你可以使用另一个向量来保存实际的指针。像这样的:

    const char * binaryPath = "/bin/foo";
    
    std::vector<std::string> argStrings;
    std::vector<char *> argPointers;
    
    std::string temp = "some text and stuff here";
    istringstream s(temp);
    
    // argv[0] should always contain the binary's path.
    argPointers.push_back(binaryPath);
    
    while (s >> temp) {
        argStrings.push_back(temp);
    
        std::cout << "TOKEN " << argStrings.size()
                  << " =" << argStrings.back() << std::endl;
    
        // We cast away the const as required by the exec() family of functions.
        argPointers.push_back(const_cast<char *>(argStrings.back().c_str()));
    }
    
    // exec() functions expect a NULL pointer to terminate the arguments array.
    argPointers.push_back(nullptr);
    
    // Now we can do our exec safely.
    execv(binaryPath, &argPointers[0]);
    

    在这种情况下,argStrings 拥有实际的字符串分配,我们使用argPointers 仅保存我们将传递给execv() 的指针数组。 const_cast 是安全的,因为 execv() 不会修改字符串。 (参数是 char * const [] 是为了与旧的 C 代码兼容;函数的行为就像参数是 const char * const []。)

    【讨论】:

    • 我看到向量是现在要走的路,谢谢。如果我更改为向量,我仍然需要更新指向向量的 args 指针,以便我可以传递给 exec 系统调用。我是否希望 args 仍然是 const char * args[100] ,因为我必须进行转换。 (可能类似于 char **args)?
    • @ChaseCollisParker 嗯,这有点棘手,因为您需要匹配现有的 API。这将取决于您正在使用的特定 exec 调用(有很多,具有不同的参数类型)。如果您愿意,我们可以在 SO 聊天中进一步讨论。
    • 非常感谢您的深入解释。这段经历非常宝贵。我还没有完全弄清楚指针向量在传递给 exec 时会起作用。转换回指针(从 java)是一个巨大的痛苦,这次我会确保更好地记录。再次感谢先生。
    • 我为什么要说明二进制路径? exec 调用可以为我找到它,因为这将在另一个发行版上运行以进行测试我不确定我是否知道。
    • @ChaseCollisParker 是的,你会想使用execvp() 代替,它将采用“裸”的第一个参数,并在PATH 环境变量中搜索具有该名称的可执行文件(类似于外壳会做什么)。
    【解决方案3】:

    您需要单独存储每个字符串,存储指向临时对象的指针不是要走的路。例如

    #include <iostream>
    #include <vector>
    #include <sstream>
    
    using namespace std;
    
    void exec(char* args[])
    {
        for (int i = 0; args[i] != NULL; ++i)
          cout << args[i] << endl;
    }
    
    int main() 
    {
      string temp = "some text and stuff here";
      stringstream s (temp);    
    
      vector<string> tokens;
      while(s>> temp)
      {
        tokens.push_back(temp);
      }
    
      int counter = 0;
      char *args[100];
      for (auto it = tokens.begin(); it != tokens.end(); ++it)
        args[counter++] = const_cast<char*>(it->c_str());
      args[counter] = NULL;
    
      exec(args);
    
      return 0;
    }
    

    你可以运行它here

    【讨论】:

      猜你喜欢
      • 2014-05-16
      • 1970-01-01
      • 2021-07-18
      • 1970-01-01
      • 2020-04-23
      • 1970-01-01
      • 1970-01-01
      • 2011-05-26
      • 2020-07-08
      相关资源
      最近更新 更多