This question already has answers here:
Why am I getting an error converting a ‘float**’ to ‘const float**’?
(4 answers)
Closed 8 years ago.
In C++, why is it not possible to pass a char** as an argument to a function that accepts const char** , when a conversion from char* to const char* is possible, as shown below
void f1(const char** a)
{
}
void f2(const char* b)
{
}
int main(int argc, char const *argv[])
{
char* c;
f1(&c); // doesn't work
f2(c); //works
return 0;
}
The compiler output is
test.cpp: In function 'int main(int, const char**)':
test.cpp:15:10: error: invalid conversion from 'char**' to 'const char**' [-fpermissive]
test.cpp:1:6: error: initializing argument 1 of 'void f1(const char**)' [-fpermissive]
You need to protect contents on both levels of dereference of the pointer. With const char** you could actually modify contents on the 1st dereference.
char *tmp = "foo"; //Deprecated but it's ok for the example
void f1(const char** a)
{
a[0] = tmp; //this is legal
a[0][1] = 'x'; //this is not
}
And this is most probably not intended. It should look like this:
char *tmp = "foo"; //Deprecated but it's ok for the example
void f1(char const* const* a)
{
a[0] = tmp; // this is not legal any more
a[0][1] = 'x'; // this still upsets compiler
}
The compiler does not allow implicit conversion to such "partially" protected pointer types. Allowing such conversion could have nasty consequences as discussed in c++faq pointed out in comment by #zmb. This answer also cites how could you violate constness of an object if this was allowed, using char examples.
One can however implicitly convert to a "fully" protected pointer as shown in the 2nd code example so below code compiles.
void f1(char const* const* a){}
void f2(const char* b){}
int main(int argc, char const *argv[])
{
char* c;
f1(&c); // works now too!
f2(c); // works
return 0;
}
Actually there is a bunch of questions and answers on this matter lying around. E.g:
invalid conversion from ‘char**’ to ‘const char**’
Why am I getting an error converting a ‘float**’ to ‘const float**’?
EDIT: I got the 1st example wrong by a bit. Thank for pointing it out!
Related
I am learning C++. I am trying to pass a string to a function via const char* str. I understand the basic meaning of this passing.
In the function body, when I try char * ptrStr = str, I got error: cannot initialize a variable of type 'char *' with an lvalue of type 'const char *'. But when I try auto ptrStr = str, it works.
I have two questions here: (1) could you please help explain why the former fails while the latter works? (2) how to use smart pointers here?
#include<iostream>
#include<memory>
void func(const char* str){
char* ptrStr = str;
}
/*
void func(const char* str){
auto ptrStr = str;
}
*/
int main(){
func("5486321 ");
return 0;
}
I have the following variable.
char **arr
Then I want to perform some modification on array which means it can't be declared as a constant.
Now I have a function that accepts the argument of type const char ** arr. But I have no control over the signature of this function.
Now when I cast arr to const char ** arr g++ generates a warning which is [-Werror=cast-qual].
For more clarification consider following MCVE:
#include<cstdio>
void print(const char** x){
printf("%s", x[0]);
}
int main(int argc, char **argv){
if(argc>1){
print((const char **)argv);
}
return 0;
}
//Then compile it as follow:
$ g++ -Wcast-qual test.cpp
//gives the following output:
MCVE.cpp: In function ‘int main(int, char**)’:
MCVE.cpp:5:36: warning: cast from type ‘char**’ to type ‘const char**’ casts away qualifiers [-Wcast-qual]
const char ** q = (const char**) argv;
So my question is why this generates a warning? Is there any risk in doing this?
And how to achieve a behavior I want to achieve?
Allowing a cast from char** to const char** provides a loophole to modify a const char or a const char*.
Sample code:
const char c = 'A';
void foo(const char** ptr)
{
*ptr = &c; // Perfectly legal.
}
int main()
{
char* ptr = nullptr;
foo(&ptr); // When the function returns, ptr points to c, which is a const object.
*ptr = 'B'; // We have now modified c, which was meant to be a const object.
}
Hence, casting a char** to const char** is not a safe cast.
You can use
if(argc>1)
{
const char* ptr = argv[0];
print(&ptr);
}
for your code to compile without the cast-qual warning.
If you need to pass more than just the first argument, you'll need to construct an array of const char* and use it.
if(argc>1)
{
int N = <COMPUTE N FIRST>;
const char** ptr = new const char*[N];
for (int i = 0; i < N; ++i )
{
ptr[i] = argv[i];
}
print(ptr);
delete [] ptr; // Make sure to deallocate dynamically allocated memory.
}
I receive the following error
$ g++ test.cpp
test.cpp: In function ‘int test1(const int**, int)’:
test.cpp:11:14: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
a=v[i];
^
test.cpp: In function ‘int main()’:
test.cpp:31:20: error: invalid conversion from ‘int**’ to ‘const int**’ [-fpermissive]
cout<<test1(c,2)<<endl;
^
test.cpp:4:5: error: initializing argument 1 of ‘int test1(const int**, int)’ [-fpermissive]
int test1(const int **v,int num)
^
when compiling the following code:
#include <iostream>
using namespace std;
int test1(const int **v,int num)
{
int *a;
int result=0;
// do somethings ....
for(int i=0;i<num;i++)
{
a=v[i];
// do somethings ....
result+=*a;
}
return result;
}
void test2(const int num)
{
cout<<num<<endl;
}
int main()
{
int a =5;
int b =8;
int **c;
c=new int *[2];
c[0]=&a;
c[1]=&b;
cout<<test1(c,2)<<endl;
test2(a);
delete [] c;
return 0;
}
i give an int to test2 which asks for const int and it is ok. however test1 does not accept int ** instead of const int **.
in the above code even typecast does not work:
a=(int *)v[i];
AFAIK, const means that I promise that I will not change the value of v and i didnt. however, the compiler gives me error.
Just write
int const *a; // or const int *a; which is the same.
...then const correctness will be preserved. The compiler complains because you try to assign v[i], which is an int const *, to int *, through which the elements that v promised would not be changed could be changed. Since you don't attempt to do that later, just use an int const* to reassure the compiler.
Note that a will remain a pointer variable (so you will be able to reassign it), only it will point to integer constants (which you cannot then change through a). To declare a constant pointer, you would write
int *const a; // pointer constant to int variable,or
int const *const a; // pointer constant to int constant
The other error is similar in origin, although it is a bit more difficult to see why it is forbidden (since you're only adding const and don't try to take it away). Consider: Were an assignment from int** to int const ** allowed, you could write the following piece of code:
int const data[] = { 1, 2, 3, 4 }; // this is not supposed to be changed.
int *space;
int **p = &space;
int const **p2 = p; // this is not allowed. Were it allowed, then:
*p2 = data;
**p = 2; // this would write to data.
And that would be bad, mkay. If you instead write
int test1(const int *const *v, int num)
Now v is a pointer (variable) to pointer constant(s) to int constant(s). Since *v is then constant, the loophole is closed, and the compiler will accept it.
I receive the following error
$ g++ test.cpp
test.cpp: In function ‘int test1(const int**, int)’:
test.cpp:11:14: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
a=v[i];
^
test.cpp: In function ‘int main()’:
test.cpp:31:20: error: invalid conversion from ‘int**’ to ‘const int**’ [-fpermissive]
cout<<test1(c,2)<<endl;
^
test.cpp:4:5: error: initializing argument 1 of ‘int test1(const int**, int)’ [-fpermissive]
int test1(const int **v,int num)
^
when compiling the following code:
#include <iostream>
using namespace std;
int test1(const int **v,int num)
{
int *a;
int result=0;
// do somethings ....
for(int i=0;i<num;i++)
{
a=v[i];
// do somethings ....
result+=*a;
}
return result;
}
void test2(const int num)
{
cout<<num<<endl;
}
int main()
{
int a =5;
int b =8;
int **c;
c=new int *[2];
c[0]=&a;
c[1]=&b;
cout<<test1(c,2)<<endl;
test2(a);
delete [] c;
return 0;
}
i give an int to test2 which asks for const int and it is ok. however test1 does not accept int ** instead of const int **.
in the above code even typecast does not work:
a=(int *)v[i];
AFAIK, const means that I promise that I will not change the value of v and i didnt. however, the compiler gives me error.
Just write
int const *a; // or const int *a; which is the same.
...then const correctness will be preserved. The compiler complains because you try to assign v[i], which is an int const *, to int *, through which the elements that v promised would not be changed could be changed. Since you don't attempt to do that later, just use an int const* to reassure the compiler.
Note that a will remain a pointer variable (so you will be able to reassign it), only it will point to integer constants (which you cannot then change through a). To declare a constant pointer, you would write
int *const a; // pointer constant to int variable,or
int const *const a; // pointer constant to int constant
The other error is similar in origin, although it is a bit more difficult to see why it is forbidden (since you're only adding const and don't try to take it away). Consider: Were an assignment from int** to int const ** allowed, you could write the following piece of code:
int const data[] = { 1, 2, 3, 4 }; // this is not supposed to be changed.
int *space;
int **p = &space;
int const **p2 = p; // this is not allowed. Were it allowed, then:
*p2 = data;
**p = 2; // this would write to data.
And that would be bad, mkay. If you instead write
int test1(const int *const *v, int num)
Now v is a pointer (variable) to pointer constant(s) to int constant(s). Since *v is then constant, the loophole is closed, and the compiler will accept it.
I have a funciton like
void foo(const char *&str)
{
cout<<str<<endl;
}
used like:
void main()
{
foo("hello");
char *h = "hello"
foo(h);
}
but both got an error
"can't convert from const char[] to const char *&"
if I change foo to void foo(const char *), there is no error can't I use const char *& as parameter
You got an error because the conversion is illegal. The reason
it is illegal is simple: it breaks const without requiring
a const_cast.
As an example of why it is forbidden, imagine that foo was:
void
foo( char const*& str )
{
str = "abc";
}
int
main()
{
char* h;
foo( h );
*h = '1';
}
If you're not going to modify str in foo, pass by value.
Pass by reference will works if you have char const* const&,
but there's no reason to use it here. It works because the
additional const means that you can bind a temporary to it (as
in the case of foo( "hello" ), where the argument is
a temporary resulting from the conversion of char const[6] to
char const*, and foo( h ) works, because the implicit const
conversions will work (in C++, but not in C!), as long as you
add const everywhere (and not just at one level).
Also, your code also uses a deprecated conversion to initialize
h. You should get a warning here. And void main is an
error, and shouldn't compile.
Edit:
Just to be clear, there's no problem with:
void f( char const*& str );
But you can only call it with an lvalue of type char const*;
anything else will either result in an illegal implicit const
conversion, or try to initialize a non-const reference with an
rvalue, which is illegal.
This will work
int main()
{
const char *h = "hello";
foo(h);
}