I am currently writing an assignment for my class that is supposed to act as a very basic shell. I am nearly finished, but I am running into an issue with execvp and my character array of parameters. Here is a light snippet of my code.
//Split the left content args
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(s);
}
//Get the split string and put it into array
const char* cmd_left[v.size()+1];
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = v.at(i).c_str();
}
cmd_left[v.size()] = 0;
v.clear();
And this is utilized by...
execvp(cmd_left[0], cmd_left);
My error is
assign3.cxx:96:34: error: invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]
I understand that the problem is that my character array isn't full of constant data, so I need to essentially go from const char* to const char* const. I read something about const_cast, but I wasn't sure if that is what I need to be doing.
If you would be so kind, could you help me get my array of character arrays to be properly accepted by that function? If you need me to post more of my code, let me know.
Thanks
The problem is you cannot pass const variable to function expecting non-const argument.
other word, const char * is a subset of char *.
remove the const
/*const*/ char* cmd_left[v.size()+1];
add const_cast here
cmd_left[i] = const_cast<char *>( v.at(i).c_str() );
other parts of your code look suspicious, but this will make it compile
Without any const_cast:
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(s);
}
//assuming v is not empty! which you were already
string command = v[0]; //store the command in a separate variable (this makes a copy of the string)
char* cmd_left[v.size()+1]; //not a (const char)*
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = new char[v[i].size()+1];
strcpy(cmd_left[i], v[i].c_str()); //copy contents of each string onto a new buffer
}
cmd_left[v.size()] = NULL;
v.clear(); //if you really want to; not necessary from the code you posted
//...
execvp(command.c_str(), cmd_left);
It is not easy, sometimes not possible to create a const dynamic array of elements because all the elements have to declared within the initializer {}.
But luckily you could tell the compiler that the array you are passing is going to be const at least for the certain duration. You could do the following this would yield
&((char* const) (const_cast<char*>(cmd_left[0]) ))
The const_cast inside would remove the const-ness of the array of characters std::string is owning. So, it is quite possible that function might change the contents of array of characters behind the back of std::string. When behaviour of functions taking such argument is known then this might be ok.
If you want to create a const array of char* without resorting to const_cast or managing memory using new/delete, you could use std::vector > instead of vector of strings.
istringstream iss(left);
while(getline(iss, s, ' ')){
v.push_back(std::vector<char>(s.length()+1));
strcpy(&v.back().front(),s.c_str());
}
//Get the split string and put it into array
char* cmd_left[v.size()+1];
for(unsigned int i = 0; i < v.size(); i++){
cmd_left[i] = &v.at(i).front();
}
cmd_left[v.size()] = 0;
v.clear();
execvp(cmd_left[0], &((char* const)cmd_left[0]));
Hope this helps.
Related
I'm working on a project for school, and we just found out that outtextxy() (a function from graphics.h, which we must use) requires as the text parameter a char array.
Here is its declaration: void outtextxy (int x, int y, char *textstring)
The issue is that we need to print out a number of type double, including the decimal point. I have previously tried making it work using knowledge from other similar questions, but none has worked.
Here are is my latest attempt, which resulted in a Segmentation Fault:
char *DoubleToString(long double x)
{
char s[256]="\000";
std::ostringstream strs;
strs << x;
string ss = strs.str();
for(int i=0; i < ss.length(); i++)
s[i] = ss[i];
return s;
}
NOTE: I am still somewhat new to programming and I don't exactly know what ostringstream and the bitshift-looking operation are doing, but I tried to copy-paste that part in hopes of it working.
... requires as the text parameter a char array.
Ok, then use a std::string:
std::string DoubleToString(long double x)
{
std::ostringstream strs;
strs << x;
return strs.str();
}
If you need the underlying character array use the strings data() method. It does return a pointer to the first element of the strings character array. For example:
std::string s = DoubleToString(3.141);
function_that_needs_pointer_to_char( s.data() );
Note that before C++17 data returned a const char* (and since C++11 the character array is null-terminated, as one would expect ;).
I know it is undefined behaviour, but it works. And I only need to pass the returned char* to outtextxy(), and not manipulate it later on, since I have the double variable stored in an object.
char *DoubleToString(long double x)
{
char s[256]="\000";
std::ostringstream strs;
strs << x;
string sd = strs.str();
strcpy(s, sd.data());
return s;
}
I have a string
const std::string myString = "my string";
that never changes.
Also, I have the following function:
void myFunction(const std::string str);
I want to go through the string char by char and do something with each substring (I pass each substring to myFunction).
This is the code I came up with first:
for (std::size_t i = 0; i < myString.length(); ++i) {
myFunction(myString.at(i));
}
but it gave me the error "cannot convert const char to string". So I came up with the following code:
for (std::size_t i = 0; i < myString.length(); ++i) {
char currentChar[2] = {myString.at(i), '\0'};
myFunction(currentChar);
}
which works but I don't feel good about it.
Is this solution correct? Or is it just coincidence that it works (like undefined behavior but I'm lucky that it works when I test it)?
Also, should the passed string in myFunction be const?
Yes, your solution is correct. It is neither efficient nor beautiful, but it works.
myFunction takes a string. You cannot pass it a character. A character and a one-character string are different things. Further, there is no constructor which transforms character to string.
Since myFunction requires a string, you should give it a string, or at least const char* which can be implicitly converted to a string.
Are you sure that myFunction really needs a string, rather than a character?
On a side note, are you sure that myFunction should not receive string by reference?
Let's say I have a string called garbage.
Whatever's in garbage, I want to make a char array out of it. Each element would be one char of the string.
So, code would be similar to:
const int arrSize = sizeof(garbage); //garbage is a string
char arr[arrSize] = {garbage};
But, this will give an error "cannot convert string to char in initialization".
What is the correct way to do this? I just want to feed the darn thing a string and make an array out of it.
C++ std::string maintains an internal char array. You can access it with the c_str() member function.
#include <string>
std::string myStr = "Strings! Strings everywhere!";
const char* myCharArr = myStr.c_str();
Keep in mind that you cannot modify the internal array. If you want to do so, make a copy of the array and modify the copy.
I think what you're after is a lot simpler:
char arr[] = "some interesting data (garbage)";
You could just use garbage.data(), which is already a pointer to the first element of an array of chars containing your string data.
Or here is a heavy way to do so.
#include <string>
std::string temp = "something";
char* myChar = new char(temp.length());
for(int i = 0; i < temp.length(); ++i){
a[i] = temp[i];
}
Consider the following code:
char CeaserCrypt(char str[256],int key)
{
char encrypted[256],encryptedChar;
int currentAsci;
encrypted[0] = '\0';
for(int i = 0; i < strlen(str); i++)
{
currentAsci = (int)str[i];
encryptedChar = (char)(currentAsci+key);
encrypted[i] = encryptedChar;
}
return encrypted;
}
Visual Studio 2010 gives an error because the function returns an array. What should I do?
My friend told me to change the signature to void CeaserCrypt(char str[256], char encrypted[256], int key). But I don't think that is correct. How can I get rid of the compile error?
The return type should be char * but this'll only add another problem.
encrypted is "allocated" on the stack of CeaserCrypt and might not be valid when the function returns. Since encrypted would have the same length as the input, do:
int len = strlen(str);
char *encrypted = (char *) malloc(len+1);
encrypted[len] = '\0';
for (int i = 0; i < len; i++) {
// ...
}
Don't forget to deallocate the buffer later, though (with free()).
EDIT: #Yosy: don't feel obliged to just copy/paste. Use this as a pointer to improve your coding practice. Also, to satisfy criticizers: pass an already allocated pointer to your encryption routine using the above example.
It wants you to return a char* rather than a char. Regardless, you shouldn't be returning a reference or a pointer to something you've created on the stack. Things allocated on the stack have a lifetime that corresponds with their scope. After the scope ends, those stack variables are allowed to go away.
Return a std::vector instead of an array.
std::vector<char> CeaserCrypt(char str[256],int key)
{
std::vector<char> encrypted(256);
char encryptedChar;
int currentAsci;
encrypted[0] = '\0';
for(int i = 0; i < strlen(str); ++i)
{
currentAsci = (int)str[i];
encryptedChar = (char)(currentAsci+key);
encrypted[i] = encryptedChar;
}
return encrypted;
}
There's another subtle problem there though: you're casting an integer to a character value. The max size of an int is much larger than a char, so your cast may truncate the value.
Since you're using C++ you could just use an std::string instead. But otherwise, what your friend suggested is probably best.
There are a few problems here. First up:
char CeaserCrypt(char str[256],int key)
As others have pointed out, your return type is incorrect. You cannot return in a single character an entire array. You could return char* but this returns a pointer to an array which will be allocated locally on the stack, and so be invalid once the stack frame is removed (after the function, basically). In English, you'll be accessing that memory address but who knows what's going to be there...
As your friend suggested, a better signature would be:
void CeaserCrypt(char* encrypted, const char str*, const size_t length ,int key)
I've added a few things - a size_t length so you can process any length string. This way, the size of str can be defined as needed. Just make sure char* encrypted is of the same size.
Then you can do:
for(int i = 0; i < length; i++)
{
// ...
For this to work your caller is going to need to have allocated appropriately-sized buffers of the same length, whose length you must pass in in the length parameter. Look up malloc for C. If C++, use a std::string.
If you need C compatibility make encrypted string function argument.
If not, than use C++ std::string instead C style string.
And also In your code encrypted string isn't ending with '\0'
The problem with the original code is that you are trying to return a char* pointer (to which your local array decayed) from a function that is prototyped as one returning a char. A function cannot return arrays in C, nor in C++.
Your friend probably suggested that you change the function in such a way, that the caller is responsible for allocation the required buffer.
Do note, that the following prototypes are completely equal. You can't pass an array as a parameter to normal function.
int func(char array[256]);
int func(char* array);
OTOH, you should (if you can!) decide the language which you use. Better version of the original (in C++).
std::vector<unsigned char> CeaserCrypt(const std::string& str, const int key)
{
std::vector<unsigned char> encrypted(str.begin(), str.end());
for (std::vector<unsigned char>::iterator iter = vec.begin();
iter != vec.end(); ++iter) {
*iter += key;
}
return vec;
}
Do note that overflowing a signed integer causes undefined behavior.
VS2010 is "yelling" at you because you are trying to return a value that is allocated on the stack, and is no longer valid once your function call returns.
You have two choices: 1) Allocate memory on the heap inside your function, or 2) use memory provided to you by the caller. Number 2 is what your friend in suggesting and is a very good way to do things.
For 1, you need to call malloc() or new depending on whether you are working in C or C++. In C, I'd have the following:
char* encrypted = malloc(256 * sizeof(char));
For C++, if you don't want to use a string, try
char* encrypted = new char[256];
Edit: facepalm Sorry about the C noise, I should have looked at the question more closely and realized you are working in C++.
You can just do your Ceaser cipher in place, no need to pass arrays in and out.
char * CeaserCrypt(char str[256], int key)
{
for(unsigned i = 0; i < strlen(str); i++)
{
str[i] += key;
}
return str;
}
As a further simplification, skip the return value.
void CeaserCrypt(char str[256], int key)
{
for(unsigned i = 0; i < strlen(str); i++)
{
str[i] += key;
}
}
well what you're returning isn't a char, but a char array. Try changing the return type to char*(char* and a char array are ostensibly the same thing for the compiler)
char* CeaserCrypt(char str[256],int key)
EDIT: as said in other posts, the encrypted array will probably not be valid after the function call. you could always do a new[] declaration for encrypted, remembering to delete[] it later on.
I have this C-styled piece of initialization code:
const char * const vlc_args[] =
{
"-I", "dummy",
"--ignore-config",
"--extraintf=logger",
"--verbose=2"
"--plugin-path=/usr/lib/vlc"
};
//tricky calculation of the char space used
libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args, &exc);
Since I need to make the --plugin-path parameter dynamic, I can't use a static array anymore. So I came up with a C++ alternative:
std::string pluginpath = "test";
libvlc_exception_t exc;
std::vector<std::string> args;
args.push_back("-I");
args.push_back("dummy");
args.push_back("--ignore-config");
args.push_back("--extraintf=logger");
args.push_back("--verbose=2");
args.push_back("--ipv4");
args.push_back("--plugin-path=" + pluginpath);
std::string combinedString;
for (size_t idx = 0; idx < args.size(); ++idx)
{
combinedString.append(args[idx]);
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
}
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;
size_t size = combinedString.size();
const char * data = combinedString.c_str();
libvlc_new(size, &data, &exc); // => error occurs here (not at end of scope or anything)
But this results in a segmentation fault. So there must be an error in my code, which I can't seem to find.. Can anyone spot it?
Solved!
Thanks to Joseph Grahn and Jason Orendorff. My idea on the memory layout of the C-style array was wrong. I thought all data was organized as a big sequential block. In reality it's a list of pointers to the first character of each individual string.
This code works:
std::vector<const char*> charArgs;
for (size_t idx = 0; idx < args.size(); ++idx)
{
charArgs.push_back(&(args[idx][0]));
}
mVLCInstance = libvlc_new(charArgs.size(),
&charArgs[0],
&mVLCException);
I think Josef Grahn is right: the API wants an actual array of pointers.
If you don't need to add arguments programmatically, you can just go back to using an array:
std::string pluginpath = "test";
std::string pluginpath_arg = "--plugin-path=" + pluginpath;
const char *args[] = {
"-I", dummy, "--ignore-config", ..., pluginpath_arg.c_str()
};
libvlc_exception_t exc;
libvlc_new(sizeof(args) / sizeof(args[0]), args, &exc);
EDIT: There might also be a problem with using c_str() here. This is true if VLC keeps the pointer and uses it again later; I can't tell if that's the case from the docs.
You are appending all arguments into a single string, then you pass a pointer to the const char * string to libvlc_new as if it were an array of char *.
(I'm not sure this is the problem, but it seems a bit strange.)
Have you tried:
libvlc_new(size, data, &exc);
instead of
libvlc_new(size, &data, &exc);
It seems you use the null bytes to make the string act like an array of characters, but then you pass a pointer to the char* "array" instead of just the array.
The library call expects a pointer to an array of const char* (that is several pointers), but you pass it a single pointer. That more characters got appended to the end of that string doesn't matter.
To dynamically build an array of the required pointers you could use another vector:
// store c_str() pointers in vector
std::vector<const char*> combined;
for (size_t idx = 0; idx < args.size(); ++idx) {
combined.push_back(args[idx].c_str());
}
// pass pointer to begin of array (it is guaranteed that the contents of a vector
// are stored in a continuous memory area)
libvlc_new(combined.size(), &(combined[0]), &exc);
Jason Orendorff's remark is also valid here: This will not work if libvlc_new stores the passed pointer internally for later use.
Regarding the segmentation violation
No solution, as there will probably be more problems
You are sending only 1 string in. (not sure if it is allowed by libvlc_new) So the first parameter should be set to 1, ie size = 1. I believe this will solve the segmentation problem. But I doubt libvlc_new can be called with just one line of multiple parameters.
In the original code sizeof(vlc_args)/sizeof(vlc_args[0]) will have the number of parameters as entries in the vector. In your example equal 6.
Your code
size_t size = combinedString.size(); // a long string, size >> 1
const char * data = combinedString.c_str(); // data is a pointer to the string
libvlc_new(size, &data, &exc);
// size should be 1 because data is like an array with only one string.
// &data is the adress to the "array" so to speak. If size > 1 the function
// will try to read pass the only string available in this "array"
I think Jason Orendorff has a good solution to fix it all...
I had this same issue; I wanted to dynamicly generate the arguments, but in a safe c++ way.
The solution I hit on was to use the unique_ptr<[]> new to C++ 11. For example:
unique_ptr<char *[]> vlc_argv;
int vlc_argc;
...
auto vlc(libvlc_new(vlc_argc, vlc_argv.get()));
This gives me a nice RAII object to hold my arguments in that I can still pass into libvlc_new(). Since the arguments are raw pointers in argv, managed by the OS, we can just use those in our vlc_argv directly.
As a further example assume I process the first few args out of argv (somewhere in the "..." area above), and want to pass everything from next_arg on to libvlc_new():
vlc_argv = std::unique_ptr<char *[]>(new char *[2 + argc - next_arg]);
vlc_argv[0] = argv[0];
for (vlc_argc=1; vlc_argc <= argc - next_arg; vlc_argc++) {
vlc_argv[vlc_argc] = argv[next_arg + vlc_argc - 1];
}
vlc_argv[vlc_argc] = 0;
The problem is due to the use of c_str() and storing the pointer.
See stringstream, string, and char* conversion confusion
EDIT Forget what I said... it was late... see the comments :)