Needing to use char** in C++ instead of std::string* - c++

I am working on an assignment for my operating systems class. We have the option of using C or C++, so I decided to use C++ since I practised it on the job a bit more recently than C.
I will need to call (from "
$ man execvp " on Linux)
int execvp(const char *file, char *const argv[]);
which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
My question is: what is the proper way to make/use char* arrays instead of string arrays in C++? Most people tend to say malloc is not used any more in C++ (which I tried now with some complications)
char** cmdList = (char**)malloc(128 * sizeof(char*));
but I don't know how to make a char* array without. Is it still appropriate to solve this how I would in C even though I'm using C++? I haven't ever run into a circumstance where I couldn't use string in C++.
Thanks for everyone's time.

If you put your arguments into a std::vector<std::string>, as you should in C++, then you need a small conversion to get to the char** that execvp wants. Luckily, both std::vector and std::string are continuous in memory. However, a std::vector<std::string> isn't an array of pointers, so you need to create one. But you can just use a vector for that too.
// given:
std::vector<std::string> args = the_args();
// Create the array with enough space.
// One additional entry will be NULL to signal the end of the arguments.
std::vector<char*> argv(args.size() + 1);
// Fill the array. The const_cast is necessary because execvp's
// signature doesn't actually promise that it won't modify the args,
// but the sister function execlp does, so this should be safe.
// There's a data() function that returns a non-const char*, but that
// one isn't guaranteed to be 0-terminated.
std::transform(args.begin(), args.end(), argv.begin(),
[](std::string& s) { return const_cast<char*>(s.c_str()); });
// You can now call the function. The last entry of argv is automatically
// NULL, as the function requires.
int error = execvp(path, argv.data());
// All memory is freed automatically in case of error. In case of
// success, your process has disappeared.

Instead of malloc use new[]:
char ** cmdlist = new char*[128];
There is no need for sizeof, since new knows the size of the type it creates. For classes this also calls the default constructor if it exists. But be careful: If there is no (public) default constructor for a type you can not use new[].
Instead of free use delete[] to release your memory when you are done:
delete[] cmdlist;
Of course, you could also use a vector. This has the advantage that the memory used to store the vector's content is automatically released when the vector is destroyed.
#include <vector>
...
std::vector<char*> cmdlist(128, nullptr); // initialize with nullpointers
// access to entries works like with arrays
char * firstCmd = cmdList[0];
cmdlist[42] = "some command";
// you can query the size of the vector
size_t numCmd = cmdlist.size();
// and you can add new elements to it
cmdlist.push_back("a new command");
...
// the vector's internal array is automatically released
// but you might have to destroy the memory of the char*s it contains, depending on how they were created
for (size_t i = 0; i < cmdlist.size(); ++i)
// Free cmdlist[i] depending on how it was created.
// For example if it was created using new char[], use delete[].

Assuming you have an std::vector<std::string> args variable that represents the argument list, you could do the following to get a C-style array of strings:
auto argvToPass = std::make_unique<const char*[]>(args.size() + 1);
int i = 0;
for (const auto& arg : args)
{
argvToPass[i++] = arg.c_str();
}
// make we have a "guard" element at the end
argvToPass[args.size()] = nullptr;
execvp(yourFile, argvToPass.get());

You can create an array of const char* and still use strings by using string.c_str() the code will look like this
const char ** argv = new const char*[128];
string arg1 = "arg";
argv[0] = arg1.c_str();

If you want to get fancy (and safe) about it you can use std::unique_ptr to good effect to ensure everything gets properly deleted/freed in the event of an error or an exception:
// Deleter to delete a std::vector and all its
// malloc allocated contents
struct malloc_vector_deleter
{
void operator()(std::vector<char*>* vp) const
{
if(!vp)
return;
for(auto p: *vp)
free(p);
delete vp;
}
};
// self deleting pointer (using the above deleter) to store the vector of char*
std::unique_ptr<std::vector<char*>, malloc_vector_deleter> cmds(new std::vector<char*>());
// fill the vector full of malloc'd data
cmds->push_back(strdup("arg0"));
cmds->push_back(strdup("arg1"));
cmds->push_back(strdup("arg2"));
// did any of the allocations fail?
if(std::find(cmds->begin(), cmds->end(), nullptr) != cmds->end())
{
// report error and return
}
cmds->push_back(nullptr); // needs to be null terminated
execvp("progname", cmds->data());
// all memory deallocated when cmds goes out of scope

which (unless I am mistaken) means I need a C-style char* array (a string array in C), and will not be able to use std::string from C++.
No you can still use string from C++. The string class has a constructor that takes C-string:
char str[] = "a string";
string cppStr(str);
Now you can manipulate your string using the string class in C++.

Related

execvp() using vector<string *>

_arguments = std::vector<std::string *>();
I have above vector declared and I want to use it for execvp()
int execvp(const char *file, char *const argv[]);
How do I convert the type to fit into this function?
It should be something like this
execvp(_arguments[0], _arguments)
You will need to create a new array of pointers to the strings' data, instead of to the std::strings themselves. There are many ways to do this, but one simple way is to use another std::vector and a structured for loop:
assert(_arguments.size() > 0); // else the executable will be invalid
std::vector<const char *> args;
args.reserve(_arguments.size() + 1);
for(string *sp: _arguments) {
args.push_back(sp->c_str());
}
args.push_back(nullptr); // needed to terminate the args list
execvp(args[0], args.data());
You can't use it directly. You'll need to copy pointers to each string's internal buffer into another vector.
Assuming arguments_ is initialized like this:
std::vector<std::string*> arguments_ = {
new std::string("mycmd"),
new std::string("arg1"),
new std::string("arg2")
}
You can call execvp something like this:
// execvp needs a NULL terminator, so make argv one element longer
// than arguments_
std::vector<char*> argv(arguments_.size() + 1);
// copy pointers to each string's buffer into the new vector
std::transform(arguments_.begin(), arguments_.end(), argv.begin(),
[](std::string* arg) {
return arg->data();
});
// The last element of argv was implicitly initialized to a null pointer
// so this isn't actually necessary.
// I just included it to be explicit
argv.back() = nullptr;
// pass a pointer to the array of char*s to execvp
execvp(argv[0], argv.data());
Note that this example relies on the non-const qualified overload of std::string::data added in C++17. Use &(*arg)[0] instead if you're using an older revision of the C++ standard.
_arguments = std::vector<std::string *>();
The standard string class manages its internal data as char array.
To access the internal data, call the method data(). The return type is const char*.
By the way, the pointer type for std::string is not necessary.
Also have a look on the reference: http://www.cplusplus.com/reference/string/string/data/

Convert to std::string and get const char * in one line

I have a number that I need to convert to a const char * (an API I'm using them requires const char * as input to many of its functions). The following works:
int num = 5;
std::string s = std::to_string(5);
const char * p = s.c_str();
as suggested by answers like those in how to convert from int to char*?, but it involves creating the seemingly unnecessary variable s, so I tried the following, but it doesn't work (p points to an empty string afterwards):
int num = 5;
const char * p = std::to_string(num).c_str();
Is there a clean way I can accomplish this? Why doesn't the second example work? The behavior is very similar to what happens if I made this obvious mistake:
const char * p;
{
std::string tempStr( "hi" );
p = tempStr.c_str( );
// p points to "hi" string.
}
// now p points to "" string.
Which makes me suspect that the issue std::to_string(num) immediately goes out of scope or something similar because it's not used to directly initialize anything.
std::string encapsulates managing dynamic memory (created with new[] and delete[]). Let's break it down.
const char * p = std::to_string(num).c_str();
Create a std::string (with a human-readable representation of num).
Get the new[]ly allocated const char* to the string.
Assign that value to p.
Destroy the std::string → delete[] the allocated const char*.
p points to... deallocated data
If you are using a pointer, the data that the pointer points to must exist throughout the lifetime of that pointer.
So, no, there is no way around this other than new[]ing a copy of the string, which you will have to explicitly delete[] later. And at that point, you've thrown the baby out with the bath and have no need to use std::string.
Create a string that lives at least as long as you want to refer to its internal data.
Just use std::string it does everything you want and everything that you would have to do manually if you don't use it.
When you need to pass a const char* to a const char* function simply use std::string::c_str() like this:
some_api_function(mystring.c_str()); // passes a const char*
What you need is a function which returns a char* which holds your value and can be used to manage its lifetime. The problematic version is broken because the char* points to memory which it does not manage.
For example:
std::unique_ptr<char[]> str(int32_t x)
{
std::unique_ptr<char[]> res(new char[12]);
snprintf(res.get(), 12, "%d", x);
return res;
}
Usestd::string everywhere and don't use const char* when not nessecary. They are basically the same thing. I use const char* only when I'm using a file-path.
Use std::string everywhere and your program should work.

How to copy a string to newly allocated memory?

In below code example, memory for an integer is dynamically allocated and the value is copied to the new memory location.
main() {
int* c;
int name = 809;
c = new int(name);
cout<<*c;
}
But, when I try to do the same with a char string it doesn't work.
Why is this?
int main() {
char* p;
char name[] = "HelloWorld";
p = new char(name);
cout << p;
}
Your second example doesn't work, because char arrays work differently than integer variables. While single variables can be constructed this way, this doesn't work with (raw) arrays of variables. (As you have observed.)
In C++ you should try to avoid handling pointers and raw arrays as much as you can. Instead, you'd rather use the standard library containers to take a copy of that string to an array of dynamically allocated memory. std::string and std::vector<char> are especially suitable in this case. (Which one should be preferred depends a bit on the semantics, but probably it's the std::string.)
Here's an example:
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
int main(){
char name[] = "Hello World";
// copy to a std::string
std::string s(name);
std::cout << s << '\n';
// copy to a std::vector<char>
// using strlen(name)+1 instead of sizeof(name) because of array decay
// which doesn't occur here, but might be relevant in real world code
// for further reading: https://stackoverflow.com/q/1461432
// note that strlen complexity is linear in the length of the string while
// sizeof is constant (determined at compile time)
std::vector<char> v(name, name+strlen(name)+1);
std::cout << &v[0] << '\n';
}
The output is:
$ g++ test.cc && ./a.out
Hello World
Hello World
For reference:
http://en.cppreference.com/w/cpp/string/basic_string
http://en.cppreference.com/w/cpp/container/vector
Your second code snippet does not work because new int(name) initializes an int from an int, while new char(name) tries to initialize a char from a char[11] array.
There is no array constructor taking an array in C++. In order to make a copy of an array, you must allocate an array, and then copy data into it:
p = new char[sizeof(name)];
std::memcpy(p, name, sizeof(name));
In the first case you allocate memory for a single int object, and initialize with a single int value. Great, this works.
In the second case you allocate memory for a single char object, and initialize it with an array of characters. It does not work, an array of objects does not fit in a memory of a single object. Besides, the array has a different type, so the initialization is ill-formed.
To allocate memory for an array of characters (such as a string), you can use new[]:
char* ptr = new char[11]{"HelloWorld"};
PS. The GNU compiler (until the current version 7 at least) and clang (until version 4) have a bug which breaks the above initialization. A workaround is to copy the string after allocation.
PPS. While it is useful to learn these things, don't do manual memory management in actual programs. Use RAII containers such as std::string for strings and std::unique_ptr for single dynamic objects.
Your code doesn't work as you are trying to initialize a char instead of array of characters. In order to dynamically allocate memory, you need to allocate the memory and then copy over the content.
p = new char[strlen(name) +1];
std::strcpy(p, name);

Insert char * with new into a vector C++

I have code sample like below..
std::vector<char*> vNameList;//it will be defined globally..
int main(int argc, char* argv[])
{
CollectName();
for(int i = 0; i<(int)vNameList.size(); i++)
{
printf("\n %s" , vNameList[i]);//Here gabage values are getting printed on console
}
return 0;
}
void CollectName()
{
char *Name = new char[sizeof(NAME)+1];//NAME datatype is defined having size of 32 char
//processing for Name is performed , which includes assigning Name variable with value..
//now insert it into vector
vNameList.push_back(Name);
delete[] Name; //at this Name value inserted into vector become garbage
}
I believe if we initialize char * with new it must be deleted to avoid memory leaks. But this is leading to modifying values from a vector.
Please guide me so that I can correct my code which will give me correct values.
I have some restriction to use Char * only , so suggest way to achieve this using char*.
You can correct your code by deleting the pointers only after you're done using them (assuming you actually have code which uses the pointers. Your example code doesn't print anything at all, garbage or otherwise).
But it's usually better design to store character strings in a std::string and when you store std::strings in a std::vector, you no longer need to manage the memory manually.
It is because the name of array is treated as pointer in c++(or c, it is inherted).
Therefore when you do
vNameList.push_back(Name);
It inserts the char * into vector,i.e the pointer to the first char(ans hence the string) to the vector, but you delete the pointer afterwards , therefore you get junk values. However if you don't delete the pointer , it works just fine as the pointer still exists , but this way you will not free up memory. Therefore : DONOT USE THIS
here it is : LIVE EXAMPLE
To avoid this hassle : you should use
std::vector<std::string> vNameList;
instead.

help with fixing memory leak

i have a member function in which i need to get some char array at run time
My fear
Is if i try
delete buffer;
then i cant
return buffer;
But how to i release the memory i allocated with
char * buffer= new char[size]
The class
class OpenglShaderLoader
{
char * getLastGlslError()
{
char * buffer;//i don't know the size of this until runtime
int size;
glShaderiv(hShaderId,GL_INFO_LOG_LENGTH,&size);//get size of buffer
buffer= new char[size];
//.. fill in the buffer
return buffer;
}
}
You should return a std::vector<char>. That way, when the caller finishes using the vector, its contents are freed automatically.
std::vector<char> getLastGlslError()
{
int size;
glShaderiv(hShaderId, GL_INFO_LOG_LENGTH, &size);
std::vector<char> buffer(size);
// fill in the buffer using &buffer[0] as the address
return buffer;
}
There is a simple adage - for every new there must be a delete, in your case, in relation to the class OpenglShaderLoader, when you call getLastGlsError, it returns a pointer to the buffer, it is there, that you must free up the memory, for example:
OpenglShaderLoader *ptr = new OpenglShaderLoader();
char *buf = ptr->getLastGlsError();
// do something with buf
delete [] buf;
You can see the responsibility of the pointer management rests outside the caller function as shown in the above code example/
You'd need another method, such as:
void freeLastGlslError(const char* s)
{
delete [] s;
}
But since you're using C++, not C, you shouldn't return a char*. For an object-oriented design, use a string class that manages the memory for you, like std::string. (Here's the litmus test to keep in mind: if memory is being freed outside of a destructor, you're probably doing something inadvisable.)
Here's a trick how to do it:
class A {
public:
A() : buffer(0) { }
char *get() { delete [] buffer; buffer = new char[10]; return buffer; }
~A() { delete [] buffer; }
private:
char *buffer;
}
When you return that pointer, whatever you're returning the pointer to should assume responsibility over that resource (i.e. delete it when done with it).
Alternatively, you can use a smart pointer to automatically delete the memory for you when nothing points to it.
Creating and returning a stl container or class (e.g. std::vector, std::string) is also a viable option.
Don't return a primitive char*. Encapsulate it in a class.
Assuming that the char array is really not a NULL terminated string, you need to include the size of it on return anyway. (It is sort of messy to continuously call glShaderiv to get the length, especially if it has performance implications. Easier to store the size with the allocation.)
Some have suggested using std::string or std::vector as the return. While each of these will work to a varying degree, they don't tell you what it is that is in each instance. Is it a string you print or is it an array of signed 8 bit integers?
A vector might be closer to what you need, but when you're looking at the code a year from now you won't know if the output vector of one method contains shader info when compared to another method that also returns a vector. There may also be implications of vector that make it undesirable for things like filling the buffer by passing a pointer to a device driver method since the storage is technically hidden.
So putting the return in a class that allocates your buffer and stores the size of the allocation allows you to let the return instance go out of scope and delete the buffer when the caller is done with it.
now body mentioned managed pointers yet?
If you don't need the features of a vector then ::array_ptr<char> might also help rather than rolling your own as in tp1's answer. Depending on version of compiler, available in boost/TR1/std.
boost::array_ptr<char> getLastGlslError()
{
int size;
glShaderiv(hShaderId, GL_INFO_LOG_LENGTH, &size);
boost::array_ptr<char> buffer = new char[size];
return buffer;
}