【问题标题】:std::vector<std::string> to char* arraystd::vector<std::string> 到 char* 数组
【发布时间】:2011-10-26 07:22:17
【问题描述】:

我有一个std::vector&lt;std::string&gt;,我需要将它用于C 函数的参数,该参数为char* foo。我有seen howstd::string 转换为char*。作为C++ 的新手,我正在尝试拼凑如何对向量的每个元素执行此转换并生成char* 数组。

我见过几个密切相关的 SO 问题,但大多数似乎都说明了走向另一个方向并创建 std::vector&lt;std::string&gt; 的方法。

【问题讨论】:

  • 什么是确切的 C 接口。根据 const 的位置以及函数在使用期间如何处理内存,我们可以做几件不同的事情(C 函数可以做一些讨厌的事情,比如调用 realloc)。
  • 模型* ModelInitialize (char *fnames, int nterms)
  • 该函数接受char*,而不是您的问题中的char**。是哪个?
  • 字符*。对困惑感到抱歉。我无意中看到了一个将 char** fnames 作为参数并随后调用 ModelInitialize 的函数。
  • 在函数签名中显然没有足够的信息来确定正确的行动方案。 fnames 的所有权是否转移到 ModelInitialize? (如果是这样:它必须如何分配?)调用代码是否意味着deletefree 或以其他方式释放从ModelInitialize 返回的Model? (如果是这样:它必须如何被释放?)fnames 必须是一个以 null 结尾的字符串吗?可以通过哪些方式修改fnames

标签: c++ c stl stdvector


【解决方案1】:

您可以将std::transform 用作:

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

这需要您将convert() 实现为:

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

测试代码:

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];
}

输出:

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

在线演示:http://ideone.com/U6QZ5

您可以在需要char** 的任何地方使用&amp;vc[0]

请注意,由于我们使用new 为每个std::string 分配内存(在convert 函数中),我们必须在最后释放内存。这使您可以灵活地更改矢量vs;你可以给它push_back更多的字符串,从vs中删除现有的字符串,然后vc(即vector&lt;char*&gt;仍然有效!

但是如果你不想要这种灵活性,那么你可以使用这个convert函数:

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

您必须将std::vector&lt;char*&gt; 更改为std::vector&lt;const char*&gt;

现在转换后,如果您通过插入新字符串或从中删除旧字符串来更改vs,那么vc 中的所有char* 可能会失效。这是重要的一点。另一个重要的一点是,您不再需要在代码中使用delete vc[i]

【讨论】:

  • 快速提问:如果我们使用 std::vector,为什么需要 delete [] vc 部分?
  • @Christopher:因为我们使用newchar* 分配内存,所以我们使用delete vc[i]。但是我们没有为char** 分配内存,因此我们没有为delete vc 分配内存。
  • 如果convert 中的新代码抛出,此代码将泄漏。最好使用std::vector&lt;char&gt;
  • @Christopher:看我的回答。现在有了更多的解释。
  • @Mankarse:std::vector&lt;char&gt; 在哪里?在转换功能?局部变量?
【解决方案2】:

你能做的最好的就是分配一个std::vectorconst char* 与你的向量大小相同。然后,遍历向量的每个元素,调用c_str() 获取字符串数组并将其存储为数组的对应元素。然后,您可以将指向该向量的第一个元素的指针传递给相关函数。

代码如下所示:

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());

请注意,您不能允许在从 std::strings 获取 const char*s 到调用 C 函数之间调整原始字符串向量的大小。

【讨论】:

  • c_str() 不返回 const char 吗?如果我只需要一个 char*,那会是个问题吗? (我已经在 cmets 中包含了确切的接口。)
  • 你也可以做 std::vectorcStrArray( origVector.size()+1, NULL);然后在迭代器中使用 cStrArray[i]=origVector[i].c_str();这可以帮助像 execv() 这样的函数。但正如上面的注释所说,我们可以使用有关 ModelInitialize 的更多信息。
【解决方案3】:

这应该可行:

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());
}

编辑:

如果你的 C 函数以某种方式修改了这个数组,你可能需要以另一种方式获取大小,假设 vec 仍然具有正确数量的元素,那么你将如何释放这些数据结构。

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

再次编辑:

如果您的 C 函数不修改字符串,则可能不需要复制字符串。如果您能详细说明您的界面是什么样的,我相信我们可以为您提供更好的帮助。

【讨论】:

  • 您需要展示如何删除该数组,特别是因为它非常复杂。不要忘记使用delete[]
  • 如果for 中的new 抛出,则会泄漏。最好使用std::vector
【解决方案4】:

一个 C++0x 解决方案,其中std::string 的元素保证连续存储:

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); }

如果函数 null 终止了它的参数,那么在调用 (s.begin(), s.end()) 之后可能没有意义。您可以后处理来解决这个问题:

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

更精细的版本,将每个字符串分别复制到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); });

【讨论】:

    【解决方案5】:

    const char* 也和 char* 一样,只是 const_ness 不同,你的接口方法接受 const 和 non-const 字符串。

    c_str() 不返回 const char 吗?如果我只是 需要一个 char* 吗?

    是的,它返回一个常量字符串,不应该没有问题

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

    很多人不接受返回本地对象,这个小例子是按原样提供的。

    【讨论】:

    • 这段代码不会编译。将指向 const 的指针分配给指向非 const 的指针既不合法也不安全。
    • "返回本地对象不被很多人接受" // 不,这是垃圾。语言或编译器不接受将 referencepointer 返回到本地对象。但这不一样。
    • 不仅这段代码无效,而且即使你实际上抛弃了constness,这也是一件非常愚蠢的事情。
    • 感谢Tomalak的指正和cmet,请不要太苛刻,这是另一个想法,如何使其更安全仍然取决于OP的实际编码经验......
    • “const char* 也与 char* 相同,只是 const_ness 不同”,这与说明“苹果与橙子相同,只是不同”一样有用
    【解决方案6】:

    向量的元素是stored contiguously,所以最好最简单的方法是:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-07
      • 2014-10-24
      • 1970-01-01
      • 1970-01-01
      • 2012-05-13
      • 2021-03-23
      相关资源
      最近更新 更多