convert vector into char** C++

前端 未结 3 1806
慢半拍i
慢半拍i 2020-11-27 20:24

I have a vector variable. I need to pass it onto a method which accepts char**as an input parameter.

how to do this ? If

相关标签:
3条回答
  • 2020-11-27 20:46

    Galik's answer has a number of safety issues. Here is how I would do it in Modern C++:

    #include <iostream>
    #include <string>
    #include <vector>
    
    void old_func(char** carray, std::size_t size)
    {
        for(std::size_t i(0); i < size; ++i)
            std::cout << carray[i] << '\n';
    }
    
    void other_old_func(const char** carray, std::size_t size)
    {
        for(std::size_t i(0); i < size; ++i)
            std::cout << carray[i] << '\n';
    }
    
    int main()
    {
        {
            std::cout << "modifiable version\n";
            std::vector<std::string> strings{"one", "two", "three"};
            std::vector<char*> cstrings{};
    
            for(auto& string : strings)
                cstrings.push_back(&string.front());
    
            old_func(cstrings.data(), cstrings.size());
    
            std::cout << "\n\n";
        }
        {
            std::cout << "non-modifiable version\n";
            std::vector<std::string> strings{"four", "five", "six"};
            std::vector<const char*> cstrings{};
    
            for(const auto& string : strings)
                cstrings.push_back(string.c_str());
    
            other_old_func(cstrings.data(), cstrings.size());
            std::cout << std::endl;
        }
    }
    

    No messy memory management or nasty const_casts.

    Live on Coliru.

    Outputs:

    modifiable version
    one
    two
    three
    
    
    non-modifiable version
    four
    five
    six
    
    0 讨论(0)
  • 2020-11-27 20:49

    It is possible to solve the problem without copying out all the std::strings as long as the function does not modify the passed in char**. Otherwise I can see no alternative but to copy out everything into a new char**` structure (see second example).

    void old_func(char** carray, size_t size)
    {
        for(size_t i = 0; i < size; ++i)
            std::cout << carray[i] << '\n';
    }
    
    int main()
    {
        std::vector<std::string> strings {"one", "two", "three"};
        std::vector<char*> cstrings;
        cstrings.reserve(strings.size());
    
        for(size_t i = 0; i < strings.size(); ++i)
            cstrings.push_back(const_cast<char*>(strings[i].c_str()));
    
        // Do not change any of the strings here as that will
        // invalidate the new data structure that relies on
        // the returned values from `c_str()`
        //
        // This is not an issue after C++11 as long as you don't
        // increase the length of a string (as that may cause reallocation)
    
        if(!cstrings.empty())
            old_func(&cstrings[0], cstrings.size());
    }
    

    EXAMPLE 2: If the function must modify the passed in data:

    void old_func(char** carray, size_t size)
    {
        for(size_t i = 0; i < size; ++i)
            std::cout << carray[i] << '\n';
    }
    
    int main()
    {
        {
            // pre C++11
            std::vector<std::string> strings {"one", "two", "three"};
    
            // guarantee contiguous, null terminated strings
            std::vector<std::vector<char>> vstrings;
    
            // pointers to rhose strings
            std::vector<char*> cstrings;
    
            vstrings.reserve(strings.size());
            cstrings.reserve(strings.size());
    
            for(size_t i = 0; i < strings.size(); ++i)
            {
                vstrings.emplace_back(strings[i].begin(), strings[i].end());
                vstrings.back().push_back('\0');
                cstrings.push_back(vstrings.back().data());
            }
    
            old_func(cstrings.data(), cstrings.size());
        }
    
        {
            // post C++11
            std::vector<std::string> strings {"one", "two", "three"};
    
            std::vector<char*> cstrings;   
            cstrings.reserve(strings.size());
    
            for(auto& s: strings)
                cstrings.push_back(&s[0]);
    
            old_func(cstrings.data(), cstrings.size());
        }
    }
    

    NOTE: Revised to provide better code.

    0 讨论(0)
  • 2020-11-27 20:54

    The top rated answers for this question expect you to pass in a size with your char** parameters. But in method_to_be_called() there is no way to pass in a size for par2 and par3 so these lists of c-style strings probably expect to be null terminated. In other words the last string (char*) in the list of strings (char **) needs to be a null pointer. This is a common paradigm in many c libraries.

    int method_to_be_called(char* par1, char ** par2, char ** par3, void* pRetValue);
    

    The most expedient way around this is probably to go with a more c-style answer.

    //directly create char** par2
    std::vector<std::string> par2Vect{"one", "two", "three"};
    char ** par2 = (char**) malloc( sizeof(char*)*(par2Vect.size() + 1) );
    for(size_t i = 0; i < par2Vect.size(); ++i)
    {
        par2[i] = strdup(par2Vect[i].c_str());
    }
    // set the last entry to null to signify the end of the list
    par2[par2Vect.size()] = nullptr;
    
    // call your library
    method_to_be_called(..., par2,...);
    
    // delete par2
    for(size_t i = 0; i < par2Vect.size(); ++i)
    {
        // free memory for each c-style string
        free(par2[i]);
    }
    // free memory for outer char* array 
    free(par2);
    
    0 讨论(0)
提交回复
热议问题