'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 |
