std::vector<std::string> 到 char* 数组

Posted

技术标签:

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

我有一个std::vector&lt;std::string&gt;,我需要将它用于C 函数的参数,该参数为char* foo。我有seen how 将std::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 【参考方案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];

【讨论】:

以上是关于std::vector<std::string> 到 char* 数组的主要内容,如果未能解决你的问题,请参考以下文章

从 std::vector<std::vector<float>> 转换为 float**

如何将 std::vector<std::vector<double>> 转换为 torch::Tensor?

C++随机选择std::vector的非空元素<std::vector>>

如何将 std::vector<std::vector<double>> 转换为 Rcpp::Dataframe 或 Rcpp::NumericMatrix

错误:没有匹配函数调用‘std::vector<std:

std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?