添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a std::vector<std::string> that I need to use for a C function's argument that reads char* foo . I have seen how to convert a std::string to char* . As a newcomer to C++ , I'm trying to piece together how to perform this conversion on each element of the vector and produce the char* array.

I've seen several closely related SO questions, but most appear to illustrate ways to go the other direction and create std::vector<std::string> .

What is the exact C interface. We can do several different things depending on where the const are and how the function treats the memory during use (C functions can do nasty things like call realloc). Martin York Aug 13, 2011 at 6:03 char*. Sorry for the confusion. I was accidentally looking at a function that takes char** fnames as an argument and later calls ModelInitialize. Christopher DuBois Aug 13, 2011 at 6:46 There is clearly not enough information in just the function signature to determine the correct course of action. Is ownership of fnames transfered into ModelInitialize ? (if so: how must it have been allocated?) Is the calling code meant to delete , free or otherwise deallocate the Model returned from ModelInitialize ? (if so: how must it be deallocated?) Must fnames be a null-terminated string? In what ways may fnames be modified? Mankarse Aug 13, 2011 at 7:43

You can use std::transform as:

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

Which requires you to implement convert() as:

char *convert(const std::string & s)
   char *pc = new char[s.size()+1];
   std::strcpy(pc, s.c_str());
   return pc; 
       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];

Output:

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

Online demo : http://ideone.com/U6QZ5

You can use &vc[0] wherever you need char**.

Note that since we're using new to allocate memory for each std::string (in convert function), we've to deallocate the memory at the end. This gives you flexibility to change the vector vs; you can push_back more strings to it, delete the existing one from vs, and vc (i.e vector<char*> will still be valid!

But if you don't want this flexibility, then you can use this convert function:

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

And you've to change std::vector<char*> to std::vector<const char*>.

Now after the transformation, if you change vs by inserting new strings, or by deleting the old ones from it, then all the char* in vc might become invalid. That is one important point. Another important point is that, you don't need to use delete vc[i] in your code anymore.

@Christopher: Because we're using new to allocate memory for char*, that is why we're doing delete vc[i]. But we're not allocating memory for char**, hence we're not doing delete vc. – Nawaz Aug 13, 2011 at 7:04 This code leaks if the new in convert throws. It would be much better to use a std::vector<char>. – Mankarse Aug 13, 2011 at 7:32

The best you can do is allocate an std::vector of const char* the same size as your vector. Then, walk each element of the vector, calling c_str() to get the string array and storing it the corresponding element of the array. Then you can pass the pointer to the first element of this vector to the function in question.

The code would look like this:

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

Note that you cannot allow the original vector of strings to be resized between the time you fetch the const char*s from the std::strings, and the time you call the C-function.

Doesn't c_str() return a const char? Will that be a problem if I just need a char*? (I've included the exact interface in the comments.) – Christopher DuBois Aug 13, 2011 at 6:16 you could also do std::vector<const char *>cStrArray( origVector.size()+1, NULL); and then in the iterator use cStrArray[i]=origVector[i].c_str(); This can help with functs like execv(). But as the note above says, we could use more info about ModelInitialize. – don bright Jul 14, 2012 at 21:44
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());

EDIT:

Here's how you would free these data structures assuming vec still has the correct number of elements, if your C function modifies this array somehow you may need to get the size another way.

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

EDIT Again:

It may not be necessary to copy the strings if your C function does not modify the strings. If you can elaborate on what your interface looks like I'm sure we could provide you with better help.

You need to show how to delete that array, particularly since it's so complicated. Don't forget to use delete[]. – Nicol Bolas Aug 13, 2011 at 6:03

A C++0x solution, where elements of std::string are guaranteed to be stored contiguously:

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

If the function null terminates its argument, then after the call (s.begin(), s.end()) might not be meaningful. You can post-process to fix that:

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

A more elaborate version that separately copies each string into a 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* is also the same as char*, only different in the const_ness, your interface method accepts both const and non-const string.

Doesn't c_str() return a const char? Will that be a problem if I just need a char*?

Yes, it returns a const string and no there should no problem

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

Returning a local object is n't accepted by many people andthis tiny example is provided as an as-is.

This code will not compile. It is neither legal nor safe to assign a pointer-to-const to a pointer-to-non-const. – Mankarse Aug 13, 2011 at 7:28 "Returning a local object isn't accepted by many people" // No, this is rubbish. Returning a reference or pointer to a local object isn't accepted by the language or by compilers. But this is not the same. – Lightness Races in Orbit Aug 13, 2011 at 15:52 And not only is this code non-valid, but even if you'd actually casted away the constness, it would be a horrendously silly thing to do. – Lightness Races in Orbit Aug 13, 2011 at 15:52 Thanks Tomalak for your corrections and comments, please don't be too harsh, it's another idea, how to make it safer still depends on the OP's actual coding experience... – Marc Aug 13, 2011 at 15:59 "const char* is also the same as char*, only different in the const_ness" that is as helpful as stating that "apples are the same as oranges, just different" – 463035818_is_not_an_ai Nov 14, 2017 at 13:51

The elements of a vector are stored contiguously, so the best and easy way is:

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

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.