10

The problem comes from an exercise on C++ Primer 5th Edition:

Write a program to assign the elements from a list of char* pointers to C-style character strings to a vector of strings.

----------------Oringinal Question------------

First I try the following somewhat direct way:

vector<char *> vec = {"Hello", "World"};
vec[0][0] = 'h';

But compiling the code I get a warning:

temp.cpp:11:43: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
     vector<char *> vec = {"Hello", "World"};
                                           ^ 

And running the ./a.out I get a

Segmentation fault (core dumped)

I think it is because I try to write to a const char. So I try another way:

char s1[] = "Hello", s2[] = "World";
vector<char *> vec = {s1, s2};
vec[0][0] = 'h';

It is OK this time. But it seems a little tedious. Is there any other elegant way to initialize a vector with string literal?

Wolf
  • 9,679
  • 7
  • 62
  • 108
Warbean
  • 547
  • 2
  • 5
  • 19
  • 4
    You cannot change the contents of a string literal. Please use `std::vector` (`std::string` can be constructed from a string literal) [live example](http://coliru.stacked-crooked.com/a/e0a7821f557b64b8) – Borgleader Jan 23 '15 at 13:00
  • 3
    String literals are `array of n const char`, you can find more details [here](http://stackoverflow.com/q/21529194/1708801). The conversion was ok in C but is not allowed in C++, exactly because of the issue with attempting to modify them which is undefined behavior. – Shafik Yaghmour Jan 23 '15 at 13:02
  • @Borgleader I know it is better to use vector but the exercise on C++ primer asks me to convert a vector into vector. So I wonder how to initialize it. – Warbean Jan 23 '15 at 13:05
  • @ShafikYaghmour Thanks. I have read that link but I just want to know the proper way to initialize a vector. – Warbean Jan 23 '15 at 13:07
  • Also from what i have seen `{"foo", "bar"}` will be converted to a list of `std::strings` first by the compiler. then you are trying to assign `std::string` to a `char*` which fails. – NathanOliver Jan 23 '15 at 13:07
  • Can you clarify what you expect the ownership semantics to be? Should the vector's elements own the memory to which they point, or is it OK to point them to something which will outlive the `vector`? – Angew is no longer proud of SO Jan 23 '15 at 13:07
  • @NathanOliver Why on Earth would `{"foo", "bar"}` generate a list of `std::string`s? Can you elaborate? – Angew is no longer proud of SO Jan 23 '15 at 13:08
  • @Angew I think the vector merely own the pointer to char. – Warbean Jan 23 '15 at 13:10
  • Can you specify which exercise in the *C+ Primer* and briefly explain the goal it is not clear from your comment what the goal is. – Shafik Yaghmour Jan 23 '15 at 13:10
  • @ShafikYaghmour Very nice of you! Fifth edition Exercise 9.14: Write a program to assign the elements from a list of char* pointers to C-style character strings to a vector of strings. – Warbean Jan 23 '15 at 13:12
  • @Warbean `std::vector vec(argv, argv + argc);` Just need to pass in some command line arguments ;) – Borgleader Jan 23 '15 at 13:17
  • @Warbean `Write a program to assign the elements from a list of char* pointers to C-style character strings to a vector of strings` - Please add this task description to the question! Thanks. – Wolf Jan 23 '15 at 13:18
  • @Angew The article I i had in my head is: http://stackoverflow.com/questions/24112281/c11-initializer-list-fails-but-only-on-lists-of-length-2. After rereading it I could be mistaken. – NathanOliver Jan 23 '15 at 13:37
  • @NathanOliver That question deals with a `std::vector`. The accepted answer even says that "the type of `"a"` is `const char[2]`." A string literal is *not* a `std::string`, nor automatically converted to one unless such a conversion is requested (by e.g. initialising a `std::string` with it). – Angew is no longer proud of SO Jan 23 '15 at 13:40

3 Answers3

6

I think the char vs const char difference doesn matter much in this task.

For the actual copy, use a fill constructor with iterator arguments:

vector<const char*> vc = {"hello","world"};
vector<string> vs(vc.begin(), vc.end());

See a working example.

If there's a need for editable chars in the source, just use the second version you posted:

char s1[] = "Hello", s2[] = "World";
vector<char *> vec = {s1, s2};

Supplement: The arguments of main, argc and argv, are a great example of

a list of char* pointers to C-style character strings

See how argc and argv get translated into a vector of string.

Wolf
  • 9,679
  • 7
  • 62
  • 108
  • Why *editable* in the source array? This seems not to be covered explicitly in the task description, I guess it focuses on transferting C-strings in the new world of C++? – Wolf Jan 23 '15 at 13:28
  • I thought its usage could be more general. Maybe I need to focus on the transfering as you say. – Warbean Jan 23 '15 at 13:33
  • @Warbean I "found" a list of char* pointers you get for free :-) – Wolf Jan 26 '15 at 11:45
5

Here's one way:

template <size_t N>
void append_literal(std::vector<char*>& v, const char (&str)[N]) {
    char* p = new char[N];
    memcpy(p, str, N);
    v.push_back(p);
}

std::vector<char*> v;
append_literal(v, "Hello");
append_literal(v, "World");

Just remember to:

void clear(std::vector<char*>& v) {
    for (auto p : v) delete[] p;
}

Although from the wording of the question, syntactically it's the same work either way if it was a vector<const char*> as if it were a vector<char*> anyway (you're not modifying the source when you're copy, so doesn't matter if you could modify the source), so I would stick to the exercise as if you just did:

std::vector<const char*> v{"Hello", "World!"};
Barry
  • 286,269
  • 29
  • 621
  • 977
0

You might try something like this:

// utility function to create char*'s
template<std::size_t Size>
char* make_rptr(const char (&s)[Size])
{
    char* rptr = new char[Size];
    std::strcpy(rptr, s);
    return rptr;
}

int main()
{
    // initialize vector
    std::vector<char*> v {make_rptr("hello"), make_rptr("world")};

    // use vector
    for(auto&& s: v)
        std::cout << s << '\n';

    // ...

    // remember to dealloacte
    for(auto&& s: v)
        delete[] s;
}
Galik
  • 47,303
  • 4
  • 80
  • 117