ffhighwind
ffhighwind

Reputation: 165

How to convert a vector<string> to null terminated char **?

I'm trying to convert a std::vector<std::string> to a NULL terminated array of C-style strings (char *). Is it possible without copying with new/malloc?

Basically, I want to convert vec back into the EXACT SAME thing as arr without new/malloc.

#include <string>
#include <vector>
#include <stdio.h>
using namespace std;

void print(const char **strs)
{
   const char **ptr = strs;
   while(*ptr) {
      printf("%s ", *ptr);
      ++ptr;
   }
}

void print(std::vector<std::string> &strs) {
   for(auto iter = strs.begin(); iter != strs.end(); ++iter) {
      printf("%s ", iter->c_str());
   }
}

void main()
{
   const char *arr[] = { "a", "b", "c", "d", "e", "f", NULL };

   vector<string> vec;

   const char **str = arr;
   while(*str) {
      vec.push_back(*str);
      ++str;
   }
   vec.push_back((char *) NULL); //Doesn't work

   //print(vec);
   print((const char **) &vec[0]);
}

Upvotes: 1

Views: 3358

Answers (4)

Mike Seymour
Mike Seymour

Reputation: 254431

You'll need to build the array of pointers yourself, and you will need dynamic allocation if the size isn't known at compile time. (In this example, you could infer it from sizeof arr; but I'm assuming that arr isn't available when you need to reconstruct it).

You can use pointers to the strings managed by the vector, so you won't need to allocate any memory for them.

So you can get the array you need thusly:

std::vector<char const *> vec2;
vec2.reserve(vec.size()+1); // optional, but might improve efficiency
for (auto const & str : vec) {
    vec2.push_back(str.c_str());
}
vec2.push_back(nullptr);

print(vec.data());

Upvotes: 3

masoud
masoud

Reputation: 56479

It's not possible just by setting a pointer to the beginning of that vector, because vector<string> is not sequential characters as you expect.

                   addr1   addr2    addr3
                    ^        ^        ^
                    |        |        |
                +--------+--------+--------+----
                |   |    |   |    |   |    |
                |  std:: |  std:: |  std:: |
       +---->   | string | string | string | ...
       |        |        |        |        |
       |        +--------+--------+--------+----
       |
       |
       |
 vector<string> 

addr1, addr2 and addr3 are random addresses in memory.

So, the solution is iterating over the items, read the strings and characters and put them in your continues array.

Upvotes: 5

Mats Petersson
Mats Petersson

Reputation: 129324

Ok, so you can't store a "NULL" inside a std::vector<std::string>, which I think is what you are aiming to do.

The whole point with std::vector is that it keeps track of number of entries in std::vector::size()

However, if you really need to have a something that you can pass to a function like your print, then you could do something like this:

vector<const char *> v;

for(auto iter = vec.begin(); iter != vec.end(); ++iter) {
   v.push_back(iter->c_str());
}
v.push_back(NULL);

print(&v[0]);

Upvotes: 0

user529758
user529758

Reputation:

Is it possible without copying with new/malloc?

If your C++ compiler supports variable-length arrays as an extension, then it is. Else you will have to use dynamic memory allocation.

std::vector<std::string> vec;
// initialize `vec', etc.

const char *c_strings[vec.size()];
size_t idx = 0;
for (std::vector<std::string>::iterator it = vec.begin(); it != vec.end(); it++) {
    c_strings[idx++] = it->c_str();
}

Upvotes: 0

Related Questions