'sprintf into char* var[1] fails with Segmentation fault [closed]

consider code:

using std::cout; using std::cerr;
using std::endl; using std::string;
using std::vector;

// . . .

    char* envp[10];

    vector<string> lines;
    char* c_line = nullptr;
    size_t len = 0;

    while ((getline(&c_line, &len, input_file)) != -1) {
        string line;
        lines.push_back(line.assign(c_line));
    }

    fclose(input_file);
    free(c_line);

    sprintf(envp[0], "SERVER=%s", lines[0].data());

    // printing envp[0] here OK

    sprintf(envp[1], "DOMAIN=%s", lines[1].data());

    // printing envp[1] never happened - Segmentation fault prints in output instead

I am C# dev I havent used C for couple decades. Something obvious is missing. Mem allocation?

P.S. I am mixing "old" char * for strings with std strings as the app uses 3rd party dll with chars*

EDIT declaring char envp[10][512]; fails down the line when I try to assing to the 3rd party property somedllObj.envps = envp; with cannot convert char[10][512] to char**



Solution 1:[1]

I do not recommend mixing std::string and old C-strings in such a wild manner. Instead I'd rely on C++ classes as long as possible:

std::ifstream inputFile("path to file");
if(!inputFile)
{
    // error handling
}

std::vector<std::string> lines;
std::string tmp;
while(std::getline(inputFile, tmp))
{
    lines.emplace_back(std::move(tmp)); // since C++11, not copying the strings...
}

if(!inputFile.eof())
{
    // not the entire file read
    // -> error handling!
}

// TODO: size check; what, if not sufficient lines in file?

lines[0] = std::string("SERVER=") + lines[0];
lines[1] = std::string("DOMAIN=") + lines[1];

std::vector<char*> envp;
envp.reserve(lines.size());
for(auto& l : lines) // C++11: range based for loop...
{
    // note that this ABSOLUTELY requires the library not
    // modifying the strings, otherwise undefined behaviour!
    envp.emplace_back(const_cast<char*>(l.c_str()));

    // UPDATE: this works as well:
    envp.emplace_back(l.data());
    // the string is allowed to be modified, too – apart from
    // the terminating null character (undefined behaviour!)
}

// so far, pure C++...
// now we just get the pointers out of the vector:

libraryFunction(envp.size(), envp.data()); 

Bonus: No manual memory management at all...

(Note: Assuming the strings are not modified in the library!)

Solution 2:[2]

Assuming servers and domains are in the input file, they do not have whitespaces, thus getline is not necessary.

vector<string> lines;
std::ifstream input_file("path to file");
std::copy(std::istream_iterator<std::string>(input_file),
          std::istream_iterator<std::string>(),
          std::back_inserter(lines));
lines[0] = "SERVER="s + lines[0];
lines[1] = "DOMAIN="s + lines[1];

std::vector<char*> envp;
for(auto& line : lines)
    envp.emplace_back(line.data());
envp.emplace_back(nullptr);  // Usually env arrays end with nullptr, thus +1 to array size
somedllObj.envps = envp.data();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2