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?
Related
This is a constructor that I found from the internet, but it didn't have enough descriptions so I couldn't understand how it's even possible for this constructor to have string as a parameter.
For instance, if I say MyString str1("hello world this "hello world" goes into the constructor, but I have no idea how this string can get into this const pointer parameter. Can anyone explain how this is possible?
MyString::MyString(const char* str) {
string_length = strlen(str);
string_content = new char[string_length];
for (int i = 0; i != string_length; i++) string_content[i] = str[i];
}
When you get a const char* you are getting what is called a C string, which is a pointer to an array of characters that ends with a special value '0/' which you dont have to type. All strings that you write as literals in the code are of this form.
Dont confuse C strings with the C++ class strings (that works with C strings underneath) since are different.
So what is happening is the constructor doesn't actually receive a string, it receives a pointer that points to something of type char (and what is a string if not a sequence of chars, and I mean the concept of string and not the string object in C++ itself).
When you do "MyString str1("hello world")" what is actually passed to the constructor is a pointer that points to the first char of the string, in this case the "h" char.
I run this test:
TEST_F(CHAR_TESTS, wtf){
char letter[3] = {'A','B','C'};
char types[2] = {'o','s'};
char tmp[3];
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<2;k++){
tmp[0]=letter[i];
tmp[1]=letter[j];
tmp[2]=types[k];
std::string combination(tmp);
std::cout << combination << std::endl;
}
}
}
}
For some reason, this print this:
AAo~
AAs~
ABo~
ABs~
ACo~
ACs~
BAo~
BAs~
BBo~
BBs~
BCo~
BCs~
CAo~
CAs~
CBo~
CBs~
CCo~
CCs~
I do not think it is an issue with the printing itself, as I ended up doing this after noticing some tests comparing strings generated from char arrays were not passing, and I could not figure out why. So it feels like indeed the "combination" strings do not end up having the expected content.
The same code in a "regular" executable (not a gtest) print out what is expected (the 3 chars without the weird supplementary chars).
Unfortunately std::string does not have a constructor that takes a reference to array of char. So you end up invoking the const char* constructor, and this one requires the pointer to point to the first element of a null terminated string. Your char arrays aren't null-terminated, so you end up with undefined behaviour.
You should null-terminate tmp, which you can do by declaring it with one extra character, and setting it to '\0'. You can achieve that like this
char tmp[4] = {};
The constructor in std::string combination(tmp); only works if tmp is nul terminated. Otherwise the constructor cannot find the length of the string.
However, you can help std::string by explicitly providing the size of the buffer:
std::string combination(tmp, sizeof(tmp));
This constructor is primarilly intended to construct a std::string from a part of a C-style string, but also works if given the full length.
char tmp[3]; is not null-terminated use char tmp[4] = {0};
I'm just starting c++ and am having difficulty understanding const char*. I'm trying to convert the input in the method to string, and then change the strings to add hyphens where I want and ultimately take that string and convert it back to char* to return. So far when I try this it gives me a bus error 10.
char* getHyphen(const char* input){
string vowels [12] = {"A","E","I","O","U","Y","a","e","i","o","u","y"};
//convert char* to string
string a;
int i = 0;
while(input != '\0'){
a += input[i];
input++;
i++;
}
//convert a string to char*
return NULL;
}
A: The std::string class has a constructor that takes a char const*, so you simply create an instance to do your conversion.
B: Instances of std::string have a c_str() member function that returns a char const* that you can use to convert back to char const*.
auto my_cstr = "Hello"; // A
std::string s(my_cstr); // A
// ... modify 's' ...
auto back_to_cstr = s.c_str(); // B
First of all, you don't need all of that code to construct a std::string from the input. You can just use:
string a(input);
As far as returning a new char*, you can use:
return strdup(a.c_str()); // strdup is a non-standard function but it
// can be easily implemented if necessary.
Make sure to deallocate the returned value.
It will be better to just return a std::string so the users of your function don't have to worry about memory allocation/deallocation.
std::string getHyphen(const char* input){
Don't use char*. Use std::string, like all other here are telling you. This will eliminate all such problems.
However, for the sake of completeness and because you want to understand the background, let's analyse what is going on.
while(input != '\0'){
You probably mean:
while(*input != '\0') {
Your code compares the input pointer itself to \0, i.e. it checks for a null-pointer, which is due to the unfortunate automatic conversion from a \0 char. If you tried to compare with, say, 'x' or 'a', then you would get a compilation error instead of runtime crashes.
You want to dereference the pointer via *input to get to the char pointed to.
a += input[i];
input++;
i++;
This will also not work. You increment the input pointer, yet with [i] you advance even further. For example, if input has been incremented three times, then input[3] will be the 7th character of the original array passed into the function, not the 4th one. This eventually results in undefined behaviour when you leave the bounds of the array. Undefined behaviour can also be the "bus error 10" you mention.
Replace with:
a += *input;
input++;
i++;
(Actually, now that i is not used any longer, you can remove it altogether.)
And let me repeat it once again: Do not use char*. Use std::string.
Change your function declaration from
char* getHyphen(const char* input)
to
auto hyphenated( string const& input )
-> string
and avoid all the problems of conversion to char const* and back.
That said, you can construct a std::string from a char_const* as follows:
string( "Blah" )
and you get back a temporary char const* by using the c_str method.
Do note that the result of c_str is only valid as long as the original string instance exists and is not modified. For example, applying c_str to a local string and returning that result, yields Undefined Behavior and is not a good idea. If you absolutely must return a char* or char const*, allocate an array with new and copy the string data over with strcpy, like this: return strcpy( new char[s.length()+1], s.c_str() ), where the +1 is to accomodate a terminating zero-byte.
Below code will work when I call like this:
char arr[] = "foobar";
reverse(arr);
but it won't work when I call like this as it is pointing to read only portion
char*a = "foobar";
reverse(a);
Now my question is that is there any way I can avoid user to call like this?
void reverse(char *str)
{
char * end = str;
char tmp;
if (str)
{
while (*end)
{
++end;
}
--end;
while (str < end)
{
tmp = *str;
*str++ = *end;
*end-- = tmp;
}
}
}
char arr[] = "foobar";
is array of chars, containing these chars: f, o, o, b, a, r, \0. While
char* a = "foobar";
is wrong. "foobar" here is a string literal, and this statement must be
const char* a = "foobar"; // note the const
You cannot change string literals.
That is a common mistake - make difference between a pointer and an array.
And no, there's no way to prevent the user to call reverse with a string literal. The "user" is responsible for their actions.
If a is defined as it must be (using const), the compiler will tell "the user" something like invalid conversion from ‘const char*’ to ‘char*’
No, there is no way to guarantee that pointer being passed to function is valid. It is impressibility of caller to provide valid data. You can even do something like this
int i = 0xABCD;
reverse((char*) i);
Which doesn't make much sense but there is no way to check for such things in reverse.
Use a std::string. Foregoing any corruption, a std::string is a continuous block of memory with a known size.
You can even use std::reverse.
Besides with the correct settings, compilers will prevent you from assigning a string literal to a char* variable.
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.