2

I've read the C FAQ on const, but I'm still confused.

I was under the (apparently mistaken) impression that const in a function declaration was essentially a promise that the function won't modify what you have marked as const. Thus passing in a const or not-const parameter is fine. But this:

#include <stdio.h>

extern void f1 ( const char * cc );
extern void f2 ( const char ** ccc );

int main ( void ) {
    char            c1[]    = "hello";
    const char *    c2      = "hello";
    char *          c3      = NULL;
    char **         v1      = NULL;
    const char **   v2      = NULL;

    f1( c1 );               /* ok */
    f1( c2 );               /* ok */
    f1( c3 );               /* ok */
    f1( "hello, world" );   /* ok */
    f2( v1 );               /* compiler warning - why? */
    f2( v2 );               /* ok */
    return 0;
}

warns thusly:

$ cc -c -o sample.o sample.c sample.c: In function 'main': sample.c:17:
warning: passing argument 1 of 'f2' from incompatible pointer type
sample.c:4: note: expected 'const char **' but argument is of type 'char **'
John Hascall
  • 9,176
  • 6
  • 48
  • 72

2 Answers2

3

Standard forbids this, because that would allow to violate constness of an object. Consider:

const char ch = 'X';
char* ch_ptr;
const char** ch_pptr = &ch_ptr; // This is not allowed, because...

*ch_pptr = &ch;
*ch_ptr = 'Q'; // ...this will modify ch, which should be immutable!

After this line:

const char** ch_pptr = &ch_ptr;

*ch_pptr and ch_ptr yield the same value (address), but they are of different types (const char* and char*). Then, you use ch_pptr to point const object, which automatically cause ch_ptr to point to the same memory location. By doing this, you allowed for any modification (using ch_ptr) of an object, that was initially declared an constant.

This error is very subtle, but after a while, you should be able to understand why it could be dangerous. That's why it is not allowed.


Yes, but why is not putting the const in the function declaration the "promise" that the function does not do that sort of evil?

Because it works the other way around - function, that does perfectly legal things may introduce invalid behavior. Look at this code:

static const char* sample_string = "String";

void f2 (const char ** ccc)
{
    *ccc = sample_string; //Perfectly valid - function does not do any "evil" things
}

int main ( void )
{
    char** v1 = NULL;

    f2(v1); // After this call, *v1 will point to sample_string

    (*v1)[0] = 'Q'; // Access violation error

    return 0;
}
Mateusz Grzejek
  • 11,698
  • 3
  • 32
  • 49
  • Yes, but why is not putting the const in the function declaration the "promise" that the function does not do that sort of evil? – John Hascall Aug 19 '15 at 13:48
  • Because it's not the function who would cause the harm - that's the case. See updated answer. – Mateusz Grzejek Aug 19 '15 at 13:56
  • Thank you, that makes sense where the answers to previous questons did not. – John Hascall Aug 19 '15 at 14:03
  • Oddly, using `const char * const * ccc` gives the same warning though it prevents the assignment to `*ccc` in `f2()` – John Hascall Aug 19 '15 at 14:10
  • Second `const` refers to constness of the *first-level pointer*, so it can't work. `const char** const` will be fine, however. – Mateusz Grzejek Aug 19 '15 at 14:14
  • Hmmm, "cdecl.org" disagrees -- it says 'const char * const * cccc' is 'declare cccc as pointer to const pointer to const char` and 'const char ** const cccc' is 'declare cccc as const pointer to pointer to const char' – John Hascall Aug 19 '15 at 15:03
0
f2( v1 );               /* compiler warning - why? */

compiler warning - why?

Because char** and const char** are different. They are not treated as same.

char** v1 // pointer to pointer to char 

Here you can change value of **v1 .

Whereas

const char** v2  //pointer to pointer to constant char 

Here you can't change value of **v2 and if you try to compiler issues a error.

ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • nope, that's not why. it should be fine to pass a modifiable object to a function that even promises not to modify it. The subtle problem lies elsewhere, one level of indirection above. – The Paramagnetic Croissant Aug 19 '15 at 13:39
  • @TheParamagneticCroissant Compiler won't treat them as same. So to that my answer was. – ameyCU Aug 19 '15 at 13:48