c++: why can't we convert char ** to const char ** [duplicate] - c++

This question already has answers here:
Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"
(5 answers)
Closed 7 years ago.
I know that const char * p means we can't change the value which is pointed by p through p.
Now I'm writing a function which will take the parameters of the function main. Meaning that this function will take a char **. So I write this function like this:
void func(const char **);.
But when I pass the parameter of main to it, I get an error:
error C2664: 'void func(const char **)' : cannot convert argument 1 from 'char **' to 'const char **'
I just want to initialize a const pointer with a non-const pointer. This should work. If we do the opposite thing, we should get some error. But now I don't know why I get this error.

I just want to initialize a const pointer with a non-const pointer. This should be work.
That's not what you're trying to do, no.
You're trying to initialise a non-const pointer to pointer to const char, with a non-const pointer to pointer to char. That is never performed implicitly. Why? It's well documented elsewhere because it's not completely intuitive to all but, in short, if converting char** to const char** were allowed, then const-correctness would be violated.
It's not the same as converting char** to char** const, which is what you think you're trying to do.
It's unfortunate that main's arguments do not already have const strewn about them. Sorry. Fortunately, you can work around it with a const_cast:
void foo(const char** argv) {}
int main(int argc, char** argv)
{
foo(const_cast<const char**>(argv));
}
This is one of those situations in which using const_cast to hack around pre-existing silliness, contrary to the wishes of the type system, is actually okay.

Related

How can I make my strchr function take both 'const char' and 'char' arrays as first parameter?

I am expecting my function to work just the same as the function "strchr" I am using from the cstring / string.h library.
I understand that I cannot cast a " const char* " variable / array to " char* ". Yet, how is the predefined "strchr" function able to be passed both "const char" and "char" data type arrays and work just fine ? How could I change mine so that it works, too ?
char* my_strchr(char *place_to_find, char what_to_find)
{
for(int i=0;place_to_find[i];i++)
if(what_to_find==place_to_find[i]) return place_to_find+i;
return NULL;
}
...
int main()
{
const char vocals1[]="AEIOUaeiou";
char vocals2[]="AEIOUaeiou";
cout<<strchr(vocals1,'e');
cout<<strchr(vocals2,'e');
cout<<my_strchr(vocals1,'e');
cout<<my_strchr(vocals2,'e');
return 0;
}
As you could probably already tell, my third cout<< does not work.
I am looking for a way to change my function ( I am guessing the first parameter should somehow be typecast ).
How can I make my strchr function take both 'const char' and 'char' arrays as first parameter?
You could change the argument to be const char*. That would allow passing both pointers to char as well as const char.
However, you return a non-const pointer to that array, which you shouldn't do if the pointer is to a const array. So, when passing a const char*, your function should also return const char*
Yet, how is the predefined "strchr" function able to be passed both "const char" and "char" data type arrays and work just fine ?
There are two predefined std::strchr functions. One that accepts and returns char* and another that accepts and returns const char*:
const char* strchr(const char* str, int ch);
char* strchr( char* str, int ch);
If you would wish to return char* in case of char* argument, you need to have a different function for each case, like the standard library has. You can use an overload like the standard library does, or you can use a function template to generate both variations without repetition:
template<class Char>
Char* my_strchr(Char *place_to_find, char what_to_find)
Note that the C version of the function is declared char *strchr(const char *str, int ch). This makes the single function usable in both cases, but is unsafe, since the type system won't be able to prevent the user of the function from modifying though the returned non-const pointer even when a const array was passed as an argument.
Make two overloads, the way it is done in the C++ standard library:
char* my_strchr( char *place_to_find, char what_to_find)
const char* my_strchr(const char *place_to_find, char what_to_find)
Even though in your case only the second overload would be sufficient (demo) you would not be able to support an important use case, when you need to find a character and then replace it:
// This would not work with only one overload:
char *ptr = my_strchr(vocals2,'e');
if (ptr) {
*ptr = 'E';
}
That is why the non-const overload is necessary.
Note: I assume that you are doing this as a learning exercise, because C-style string functions are no longer necessary for new development, having been replaced with std::string functionality.
Short answer: make the type of the place_to_find const char*
The reason for your error is that you cannot implicitly convert a pointer to a const char to a pointer to a non-const char. If you could, then you could change the char that the pointer points to and it would defeat the purpose of having it a const char type in the first place.
You can implicitly convert a pointer to a non-const char to a pointer to a const char because it does not remove any restrictions.
L.E.: Also, the return value should be a const char*, because again, if you don't, it would remove the const restriction which is not allowed. The only problem with that is that you would not be able to modify the array through the pointer returned. If you also want that, then you would have to overload the method on both char and const char.

Can I have a pointer on pointer on const in C++?

Basically I am wondering whether something like
f(const void* a){
// This is also what `(char**) a` would do behind the scenes
char** string_a_ptr {reinterpret_cast<char**>(const_cast<void*>(a))};
// ...
is possible while preserving the const qualifier.
My question came up during an exercise about C-Arrays in C++. Because of C, I had to pass a function pointer with const void* arguments. In this case it was pointing on an array of C-strings. To cast it to the correct type I had to cast the const away. Is there a way in C++ to get something like a "(const char*)*"` pointer?
I have tried out const char** (without the const_cast) which yields
reinterpret_cast from type ‘const void*’ to type ‘const char**’ casts away qualifiers
and (const*)*, which gives a syntax error
expected type-specifier before ‘(’ token
This is a purely academic question. I am aware, that the problem can be solved easier and saver using std::string and #include <algorithm>.
While I am at it, are there advantages of sometimes using C-constructions in C++ or should they always be avoided if possible? I only did some small scale exercise problems with C code in C++, but it already causes quite a few headaches and forces me to look really closely under the hood, even though I have written a fair amount of C code in my life.
Or is it would you rather recommend using the extern C qualifier and writing C code in pure C?
That would be a char* const *.
There are three places in a double pointer to put const:
const char**
Pointer, to a pointer, to a character you cannot change.
char *const *
pointer, to a pointer you cannot change, to a character.
char ** const
pointer you cannot change, to a pointer, to a character.
You can mix these to make different parts const.
I believe this is what you're looking for:
const char * const * string_a_ptr = static_cast<const char * const *>(a);
[Live example]
You could even drop the first const if you want (and it makes sense).
Explanation:
const void * a means that a points to something (void) which is const. Therefore, if you cast a to another pointer, it must also point to something that is const. In your case, you're casting it to a pointer to char*. That char* must therefore be const. The syntax for creating a constant pointer (not a pointer to a constant) is to put the const to the right of the *. Hence, char* const *.

Passing argv as const [duplicate]

This question already has answers here:
constness and pointers to pointers
(4 answers)
non-const pointer argument to a const double pointer parameter
(1 answer)
Closed 8 years ago.
I want to pass argv to another function, and can do it with no problems when I define the function like this:
void function(char** argv);
and call it from main with:
function(argv);
However, I would like to keep everything const where possible (I don't actually plan to change argv, or the value of either of the pointers). My problem is that as soon as I add the keyword const anywhere to argv in the function declaration I get conversion errors, e.g. this code
void function(const char** argv);
gives the compile error:
error: invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]
I've tried putting const in different places and get similar errors. Is there a way to pass argv while keeping the contents and pointers all constant?

Explain: Converting 'char **' to 'const char **', Conversion loses qualifiers [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Implicit cast from char** to const char**
Given the following code:
void foo( const char ** buffer );
void bar()
{
char * buffer;
foo( &buffer );
}
Why is it that if the foo() function has a const char * parameter the compiler doesn't complain when passing in a char * variable into it? But when using char **, it cannot convert it to const char **? Does the compiler add any const qualifiers in the former case?
I've read section 4.4 of the C++ standard and it just confused me further.
Yes, you cannot implicitly convert from a T ** to a const T **, because the compiler can no longer guarantee that the const-ness won't be violated.
Consider the following code (borrowed from the C FAQ question on exactly this topic: Why can't I pass a char ** to a function which expects a const char **?):
const char c = 'x';
char *p1;
const char **p2 = &p1; // 3
*p2 = &c;
*p1 = 'X'; // 5
If the compiler allowed line 3, then line 5 would end up writing to a const object.
Consider:
char const someText[] = "abcd";
void
foo( char const** buffer )
{
*buffer = someText;
}
void
bar()
{
char* buffer;
foo( &buffer );
*buffer = 'x';
}
If this were legal, it would be possible to modify a const object
without an intervening const_cast. The conversion is forbidden
because it violates const-ness.
You are probably confusing the level of indirection the const applies to.
A char** can be described as pointer to a pointer to a character whereas a const char** can be described as pointer to a pointer to a constant character.
So when we write this differently, we have pointer to A (where A = pointer to character) and we have pointer to B (where B = pointer to a constant character).
Clearly now (I hope) A and B are distinct types, as such a pointer to A can not be assigned toa a pointer to B (and vice versa).

What's char* const argv[]?

I'm studying linux C++ programing when I see
int execve(const char *path,
char *const argv[],
char *const envp[]);
I don't understand what is char *const argv[] . I know char *const foo is a const pointer to char. And const char *foo is a pointer to a const char. But what's char *const argv[]?
Is it an array of const pointers to char or an array of pointers to const char?
And I have a vector<string> now, how to convert it to char *const argv[]?
Is it an array of const pointers to char or an array of pointers to const char?
Read types right to left:
const always binds to the left (unless it is on the very left)
char *const argv[]
^^ Array of
^^^^^ const
^ pointer to
^^^^ char
An array of "const pointer" to char
So the pointers in the array can not be modified.
But what they point at can be modified.
Note: As pointed out by Steve below. Because this is being used as a parameter. It actually decays to a "A pointer to "const pointer" to char" or "char* const *".
And I have a vector<string> now, how to convert it to char *const argv[]?
int execve(const char *path,
char *const argv[],
char *const envp[]);
int main()
{
std::vector<std::string> data; /// fill
std::vector<char*> argv;
std::vector<char*> envp;
for(std::vector<std::string>::iterator loop = data.begin(); loop != data.end(); ++loop)
{
argv.push_back(&(*loop)[0]);
}
// I also bet that argv and envp need to be NULL terminated
argv.push_back(NULL);
envp.push_back(NULL);
execve("MyAppName", &argv[0], &envp[0]);
}
cdecl.org says:
char *const argv[]
declare argv as array of const pointer to char
// says: argv is a constant pointer pointing to a char*
int main(int c, char *const argv[]);
You won't be able to change "argv" within the function.
As you are just learning C, i recommend you to really try to understand the differences between arrays and pointers first instead of the common things.
In the area of parameters and arrays, there are a few confusing rules that should be clear before going on. First, what you declare in a parameter list is treated special. There are such situations where things don't make sense as a function parameter in C. These are
Functions as parameters
Arrays as parameters
Arrays as parameters
The second maybe is not immediately clear. But it becomes clear when you consider that the size of an array dimension is part of the type in C (and an array whose dimension size isn't given has an incomplete type). So, if you would create a function that takes an array by-value (receives a copy), then it could do so only for one size! In addition, arrays can become large, and C tries to be as fast as possible.
In C, for these reasons, array-values are not existent. If you want to get the value of an array, what you get instead is a pointer to the first element of that array. And herein actually already lies the solution. Instead of drawing an array parameter invalid up-front, a C compiler will transform the type of the respective parameter to be a pointer. Remember this, it's very important. The parameter won't be an array, but instead it will be a pointer to the respective element type.
Now, if you try to pass an array, what is passed instead is a pointer to the arrays' first element.
Excursion: Functions as parameters
For completion, and because i think this will help you better understand the matter, let's look what the state of affairs is when you try to have a function as a parameter. Indeed, first it won't make any sense. How can a parameter be a function? Huh, we want a variable at that place, of course! So what the compiler does when that happens is, again, to transform the function into a function pointer. Trying to pass a function will pass a pointer to that respective function instead. So, the following are the same (analogous to the array example):
void f(void g(void));
void f(void (*g)(void));
Note that parentheses around *g is needed. Otherwise, it would specify a function returning void*, instead of a pointer to a function returning void.
Back to arrays
Now, i said at the beginning that arrays can have an incomplete type - which happens if you don't give a size yet. Since we already figured that an array parameter is not existent but instead any array parameter is a pointer, the array's size doesn't matter. That means, the compiler will translate all of the following, and all are the same thing:
int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);
Of course, it doesn't make much sense to be able to put any size in it, and it's just thrown away. For that reason, C99 came up with a new meaning for those numbers, and allows other things to appear between the brackets:
// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory.
int main(int c, char *argv[static 5]);
// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);
// says the same as the previous one
int main(int c, char ** const argv);
The last two lines say that you won't be able to change "argv" within the function - it has become a const pointer. Only few C compilers support those C99 features though. But these features make it clear that the "array" isn't actually one. It's a pointer.
A word of Warning
Note that all i said above is true only when you have got an array as a parameter of a function. If you work with local arrays, an array won't be a pointer. It will behave as a pointer, because as explained earlier an array will be converted to a pointer when its value is read. But it should not be confused with pointers.
One classic example is the following:
char c[10];
char **c = &c; // does not work.
typedef char array[10];
array *pc = &c; // *does* work.
// same without typedef. Parens needed, because [...] has
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;