17

Why my compiler(GCC) doesnt implicitly cast from char** to const char**?

Thie following code:

#include <iostream>

void print(const char** thing) {
    std::cout << thing[0] << std::endl;
}

int main(int argc, char** argv) {
    print(argv);
}

Gives the following error:

oi.cpp: In function ‘int main(int, char**)’:
oi.cpp:8:12: error: invalid conversion from ‘char**’ to ‘const char**’ [-fpermissive]
oi.cpp:3:6: error:   initializing argument 1 of ‘void print(const char**)’ [-fpermissive]
M.M
  • 138,810
  • 21
  • 208
  • 365
André Puel
  • 8,741
  • 9
  • 52
  • 83
  • 8
    [Why am I getting an error converting a `Foo**` → `Foo const**`?](http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17) – fredoverflow Aug 10 '11 at 18:52
  • There is no such thing as an "implicit cast". A `cast` is an explicit operator that specifies a conversion. There can also be implicit conversions. ("cast" is the operator, "conversion" is the operation.) – Keith Thompson Aug 10 '11 at 19:03
  • @Keith: I think that terminology is not a problem. After all, we say "up-cast" and not "up-conversion". Or, at least, I say that. :-) – Cheers and hth. - Alf Aug 10 '11 at 19:06
  • 1
    @André: it isn't; `Foo**` and `const Foo*` differ in the number of levels of indirection. You *can* convert from `Foo*` to `const Foo*`, because that conversion doesn't make it possible to modify a read-only object; you're just taking a pointer to a read-write object and promising not to modify it. – Keith Thompson Aug 10 '11 at 19:09
  • http://kera.name/articles/2009/12/a-question-on-indirect-constness/ – Lightness Races in Orbit Mar 29 '13 at 17:22
  • Note: I have added `c` tag in because the gist of the problem is the same in both languages (no point having two separate threads IMHO), although the solution suggested by Alf only works in C++ unfortunately. In C you're stuck with an ugly cast. – M.M Jan 13 '15 at 09:11

4 Answers4

20

Such a conversion would allow you to put a const char* into your array of char*, which would be unsafe. In print you could do:

thing[0] = "abc";

Now argv[0] would point to a string literal that cannot be modified, while main expects it to be non-const (char*). So for type safety this conversion is not allowed.

sth
  • 222,467
  • 53
  • 283
  • 367
  • Very nice explanation I didn't think of it this way, thanks. Changing it to `char *const *thing` works as expected. However then also `const char *const *thing` should work, but doesn't. I'd be greatful for any more info on that. – nert Feb 10 '16 at 11:19
  • So it works for C++ but doesn't for C, so I posted new question for that because I can't find any answer: https://stackoverflow.com/questions/35319842/why-c-doesnt-allow-implicit-conversion-from-char-to-const-char-const-and – nert Feb 10 '16 at 15:56
6

@Fred Overflow's link to the FAQ is a complete answer. But (sorry Marshall) it's not the most clear explanation. I don't know if mine is more clear, but I hope so.


The thing is, if p is a char* pointer, then it can be used to modify whatever it's pointing at.

And if you could obtain a pointer pp that points to p, but with pp of type char const**, then you could use pp to assign to p the address of a const char.

And with that, you could then use p to modify the const char. Or, you would think you could. But that const char could even be in read-only memory…

In code:

char const        c = 'a';
char*             p = 0;
char const**      pp = &p;               // Not allowed. :-)

*pp = &c;        // p now points to c.
*p = 'b';        // Uh oh.


As a practical solution to your code that does not compile, …
#include <iostream>

void print(const char** thing) {
    std::cout << thing[0] << std::endl;
}

int main(int argc, char** argv) {
    print(argv);    // Dang, doesn't compile!
}

just do …

#include <iostream>

void print( char const* const* thing )
{
    std::cout << thing[0] << std::endl;
}

int main( int argc, char** argv )
{
    print( argv );    // OK. :-)
}

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Since this thread is linked as duplicates from both C and C++ posts: note that the final trick does not work in C. (There's no good reason for it to not work in C, it's probably just an oversight by the standards committee). – M.M Jan 13 '15 at 09:08
  • @MattMcNabb just *added* the C tag. This answer was fine and complete when I wrote it. It doesn't work for C, it doesn't work for Pascal, it doesn't work for Haskell, or any other language one adds tags for. – Cheers and hth. - Alf Jan 14 '15 at 03:45
3

Note, that although

void dosmth(const char** thing);

int main(int argc, char** argv) {
  dosmth(argv);

is forbidden, you can and should do

void dosmth(const char* const* thing);

int main(int argc, char** argv) {
  dosmth(argv);

Which is probably what you wanted anyway. The point here is that thing now refers to a const char* array which is itself immutable and which referenced values char are themselves immutable. So, for a "look at it, but do not change it" scenario, const char* const* is the type to use.

Note: I used the more common (but in my opinion inferior) standard of trying to write the const modifier as left as possible. Personally, I recommend writing char const* const* instead of const char* const* as it is hugely more concise that way.

bitmask
  • 32,434
  • 14
  • 99
  • 159
  • I also thought this should work as well, but gcc wants the first const not to be there. Particularly: `const char* const* thing` yields an error, but `char* const* thing` does not. – nert Feb 10 '16 at 11:16
  • So it works for C++ but doesn't for C, so I posted new question for that because I can't find any answer: https://stackoverflow.com/questions/35319842/why-c-doesnt-allow-implicit-conversion-from-char-to-const-char-const-and – nert Feb 10 '16 at 15:56
2

Because it might allow us to modify a constant value. Read here to understand why: http://c-faq.com/ansi/constmismatch.html