I am integrating 2 programs and turning them into one (I know, not ideal). I expect command line args to look something like
bin/programName -optionsProgram1 --optionsProgram2
I have parsed the options out so I now have the separate sets of arguments, shown below.
bin/programName -optionsProgram1
bin/programName --opionsProgram2
This is great, just how I need them to be. The problem is that I did that with arguments being pushed onto vectors and now I need to use pointers to character arrays. Can anyone recommend a way to convert my vectors into char* in a better way? Also, these will have to be returned as char** in the end, please keep that in mind with your answers.
I have tried using strncpy and realized there was a type error.
My attempt is shown below
.h excerpt
std::vector<std::string> myVec; //hold the arguments, one at each pos
std::vector<char*> myCharArray; //intended location for result of conversion
.cpp excerpt
size_t vecSize = myVec.size()
size_t charVecSize;
myCharArray.resize(vecSize);
for(size_t i=0; i < vecSize; ++i)
{
csize = myVec.size()+1 //include null
myCharArray[i] = new char[csize]
strncpy(myCharArray[i], myVec[i].c_str(), csize);
myCharArray[i][csize-1] = '\0'
}
Try std::vector<const char*> populated with pointers from std::string::c_str(), eg:
std::vector<std::string> myVec;
std::vector<const char*> myCharArray;
// populate myVec as needed ...
...
// add +1 if you need the array to be null-terminated...
myCharArray.reserve(myVec.size()/*+1*/);
for (auto &s : myVec) {
myCharArray.push_back(s.c_str());
}
//myCharArray.push_back(nullptr);
or:
myCharArray.resize(myVec.size()/*+1*/);
for (size_t i = 0; i < myVec.size(); ++i) {
myCharArray[i] = myVec[i].c_str();
}
//myCharArray.back() = nullptr;
or:
#include <algorithm>
myCharArray.resize(myVec.size()/*+1*/);
std::transform(myVec.begin(), myVec.end(), myCharArray.begin(),
[](std::string &s){ return s.c_str(); });
//myCharArray.back() = nullptr;
Then you can use myCharArray.data() (which is const char **) as needed.
Related
I got an issue with sprintf buffer.
As you can see in the code down below I'm saving with sprintf a char array to the buffer, so pFile can check if there's a file named like that in the folder. If it's found, the buffer value will be assigned to timecycles[numCycles], and numCycles will be increased. Example: timecycles[0] = "timecyc1.dat". It works well, and as you can see in the console output it recognizes that there are only timecyc1.dat and timecyc5.dat in the folder. But as long as I want to read timecycles with a for loop, both indexes have the value "timecyc9.dat", eventhough it should be "timecyc1.dat" for timecycles[0] and "timecyc5.dat" for timecycles1. Second thing is, how can I write the code so readTimecycles() returns char* timecycles, and I could just initialize it in the main function with char* timecycles[9] = readTimecycles() or anything like that?
Console output
#include <iostream>
#include <cstdio>
char* timecycles[9];
void readTimecycles()
{
char buffer[256];
int numCycles = 0;
FILE* pFile = NULL;
for (int i = 1; i < 10; i++)
{
sprintf(buffer, "timecyc%d.dat", i);
pFile = fopen(buffer, "r");
if (pFile != NULL)
{
timecycles[numCycles] = buffer;
numCycles++;
std::cout << buffer << std::endl; //to see if the buffer is correct
}
}
for (int i = 0; i < numCycles; i++)
{
std::cout << timecycles[i] << std::endl; //here's the issue with timecyc9.dat
}
}
int main()
{
readTimecycles();
return 0;
}
With the assignment
timecycles[numCycles] = buffer;
you make all pointers point to the same buffer, since you only have a single buffer.
Since you're programming in C++ you could easily solve your problem by using std::string instead.
If I would remake your code into something a little-more C++-ish and less C-ish, it could look something like
std::array<std::string, 9> readTimeCycles()
{
std::array<std::string, 9> timecycles;
for (size_t i = 0; i < timecycles.size(); ++i)
{
// Format the file-name
std::string filename = "timecyc" + std::to_string(i + 1) + ".dat";
std::ifstream file(filename);
if (file)
{
// File was opened okay
timecycles[i] = filename;
}
}
return timecycles;
}
References:
std::array
std::string
std::to_string
std::ifstream
The fundamental problem is that your notion of a string doesn't match what a 'char array' is in C++. In particular you think that because you assign timecycles[numCycles] = buffer; somehow the chars of the char array are copied. But in C++ all that is being copied is a pointer, so timecycles ends up with multiple pointers to the same buffer. And that's not to mention the problem you will have that when you exit the readTimecycles function. At that point you will have multiple pointers to a buffer which no longer exists as it gets destroyed when you exit the readTimecycles function.
The way to fix this is to use C++ code that does match your expectations. In particular a std::string will copy in the way you expect it to. Here's how you can change your code to use std::string
#include <string>
std::string timecycles[9];
timecycles[numCycles] = buffer; // now this really does copy a string
I have the following code in which works fine.
const char** array = new const char*[_list.size()];
unsigned index = 0;
for (std::list<std::string>::const_iterator it = _list.begin(); it != _list.end(); ++it) {
array[index] = it->c_str();
index++;
}
My question is, how can I convert const char ** to const char *. Please help.
this is actually an XY question. Hameed should tell what problem he solves, not what step he got to finish to solve problem.What actually the task is? Convert list of strings to array of c strings?
if yes, then should be done somewhat in this way ( sadly, one should copy data)
char** arr = new char*[_list.size()];
int i = 0;
for(auto it = _list.begin(); it != _list.end(); ++it, +i)
{
const char *s = it->c_str(); // should not be modified or deleted
const size_t l = strlen(s);
arr[i] = new char [l+1];
std::copy(&s[0],&s[l], &(arr[i][0]));
}
...
Do you need get i-th element from list?
auto it = _list.begin();
std::advance(it, i);
const char *a = it->c_str();
if doing that in loop for each element, just increment it instead.
concatenate all strings in one? iterate through list, calculate total length, then allocate array of char of that length and copy string one by one. i have vague memory that boost got this as algorithm::join already, but stl require long and frilly code, can't devise that on phone, whoever can edit answer is welcome. in different form that question with dozen solutions presents on stack: How to implode a vector of strings into a string (the elegant way)
I have a vector<std::string> variable. I need to pass it onto a method which accepts char**as an input parameter.
how to do this ? If possible I need to pass a writable one.
Update 1:
In a tool for creating a service method, i give parameters as std::vector, but it sets automatically the qualifier as &, which means my method definition generated by the tool will look as:
std::string SvcImpl::myMethodname ( const std::string par1, const std::vector< std::string >& par2, const std::vector< std::string >& par3 )
{
}
This method gets called automatically with values in the patameter passed.
Now from inside this method I'm going to call a method in a dll in a lib folder which looks like:
int method_to_be_called(char* par1, char ** par2, char ** par3, void* pRetValue);
for par1 --> I'm passing (char*)par1.c_str()
I need to know how to pass variables for par2 and par3 and for pRetValue.
values for par2 and par3 are available in vector but the last parameter pRetValue is an output parameter that i need to return it as std::string.
sorry if i am very confusing or asking very basic questions.
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.
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
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);
I believe this is rather easy and can be done without too much of complexity.
std::vector<std::string> vector = {"a", "std::vector", "of", "std::string"};
// Result char**.
char** result = new char*[vector.size()];
for (int index = 0; index < vector.size(); index++) {
result[index] = const_cast<char*>(vector[index].c_str());
}
// Use the result.
delete[] result;
// Deallocate the memory from heap after usage.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I prefer char* instead std::string, so I wrote a function combining char pointers. I created a new project and tested it. It works fine. But when I try to use it in some larger GUI project, my program crashes.
Here's an example (working) code:
#include <Windows.h>
#include <vector>
char *StringJoin(const char *String, const char* ...)
{
va_list ArgList;
va_start(ArgList, String);
std::vector<const char *> StringData;
std::vector<unsigned int> StringLen;
StringData.push_back(String);
unsigned int SingleLength = strlen(String);
StringLen.push_back(SingleLength);
unsigned int TotalLength = SingleLength;
while (1)
{
const char* Val = va_arg(ArgList, const char*);
if (!Val)
break;
StringData.push_back(Val);
SingleLength = strlen(Val);
StringLen.push_back(SingleLength); // In larger projects it crashes here
TotalLength += SingleLength;
}
va_end(ArgList);
char *NewString = new char[TotalLength + 1];
unsigned int VectorSize = StringData.size();
unsigned int NewLength = 0;
for (unsigned int Element = 0; Element < VectorSize; Element++)
{
memcpy(NewString + NewLength, StringData[Element], StringLen[Element]);
NewLength += StringLen[Element];
}
NewString[TotalLength] = '\0';
StringData.clear();
StringLen.clear();
return NewString;
}
int main(void)
{
char* New = StringJoin("Does ", "it ", "works ", "for ", "you ", "?");
printf("%s\n", New);
system("PAUSE");
return 1;
}
Is my code safe and stable?
You can't use the !Val condition:
const char* Val = va_arg(ArgList, const char*);
if (!Val)
break;
When using variable argument list you need to know exactly how many arguments have been passed, or (update) when to stop processing the arguments, using e.g. NULL / nullptr (or any other) sentinel.
You definitely should use variadic templates if your compiler supports it.
The following quite simple function takes an arbitrary number of strings and concatenates it in a loop. It supports both std::string and char* for the arguments (and even mixed):
template<class ...Strings>
std::string StringJoin(Strings ...strings) {
std::string joined;
for (auto s : { strings... })
joined += s;
return joined;
}
Live Demo
Now if you want the result to be a char*, you need to think about where you allocate the memory and where you deallocate it, i.e. you need to manually manage memory. Unless you use smart pointers:
template<class ...Strings>
std::unique_ptr<char[]> StringJoin(Strings ...strings) {
std::string joined;
for (auto s : { strings... })
joined += s;
std::unique_ptr<char[]> buf(new char[joined.size() + 1]);
memcpy(buf.get(), joined.data(), joined.size());
buf[joined.size()] = '\0';
return buf;
}
Live Demo
How can I make an array of n strings using char**?
char** lit;
*lit = (char*)calloc(this->nr_param, sizeof(char*));
for(int i = 0; i < this->nr_param; i++)
lit[i] = (char*) calloc(this->nr_param, sizeof(char));
Is this the way?
If so, how can i access elements? Lets say my array will contain the following elements:
aaab, abba, baab;
I want this structure:
lit[0] = "aaab";
lit[1] = "abba";
lit[2] = "baab";
It's ok how I declared them?
Like this:
// allocate memory for n char pointers dynamically.
char ** lit = static_cast<char**>(::operator new(n * sizeof(char*)));
for (unsigned int i = 0; i != n; ++i)
{
lit[i] = static_cast<char*>(::operator new(length_of_string_i)); // #1
// populate lit[i] with data
}
You need some method of determining the length of the ith string, which you need to paste appropriately in the line marked #1. Note that sizeof(char) == 1, so you don't need to multiply anything in the inner allocation.
(You can use std::malloc instead of ::operator new if you prefer, but then you have to #include <cstdlib>.) Don't forget to clean up when you're done!
This is of course only the literal translation of what you asked for. In C++, you would usually prefer object creation over raw memory allocation, which looks like this:
// construct n char pointers dynamically
char ** lit = new char*[n];
for (unsigned int i = 0; i != n; ++i)
{
lit[i] = new char[length_of_string_i];
// populate lit[i] with data
}
But you should seriously never use array-new. It's not a good concept, and rarely good C++.
So, you shouldn't be doing this at all, and instead you should use:
std::vector<std::string> lit(n);
You can use c++ vector and strings in a similar way:
#include <vector>
#include <string>
#include <iostream>
int main() {
std::vector<std::string> lit;
lit.push_back("aaab");
lit.push_back("aab");
lit.push_back("aabb");
lit[0][0] = 'z';
std::cout << lit[0] << std::endl;
}