Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I able to change the contents of const char *ptr?

Tags:

c

pointers

I passed a pointer ptr to a function whose prototype takes it as const.

foo( const char  *str );

Which according to my understanding means that it will not be able to change the contents of ptr passed. Like in the case of foo( const int i ). If foo() tries to chnage the value of i, compiler gives error.
But here I see that it can change the contents of ptr easily.
Please have a look at the following code

foo( const char  *str )
{
        strcpy( str, "ABC" ) ;
        printf( "%s(): %s\n" , __func__ , str ) ;
}

main()
{
        char ptr[  ] = "Its just to fill the space" ;
        printf( "%s(): %s\n" , __func__ , ptr ) ;
        foo( const ptr ) ;
        printf( "%s(): %s\n" , __func__ , ptr ) ;
        return;
}

On compilation, I only get a warning, no error:

warning: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type

and when I run it, I get an output instead of Segmentation Fault

main(): Its just to fill the space
foo(): ABC
main(): ABC

Now, my questions is
1- What does const char *str in prototype actually means?
Does this mean that function cannot change the contents of str? If that is so then how come the above program changes the value?
2- How can I make sure that the contents of the pointer I have passed will not be changed?

From "contents of the pointer" in the above stated question, I mean "contents of the memory pointed at by the pointer", not "address contained in the pointer".

Edit

Most replies say that this is because of strcpy and C implicit type conversion. But now I tried this

foo( const char  *str )
{
        str = "Tim" ;
//      strcpy( str, "ABC" ) ;
        printf( "%s(): %s\n" , __func__ , str ) ;
}

This time the output is, with no warning from compiler

main(): Its just to fill the space
foo(): Tim
main(): Its just to fill the space

So apparently, memory pointed to by str is changed to the memory location containing "Tim" while its in foo(). Although I didn't use strcpy() this time.
Is not const supposed to stop this? or my understanding is wrong?

To me it seems that even with const, I can change the memory reference and the contents of memory reference too. Then what is the use?

Can you give me an example where complier will give me error that I am trying to change a const pointer?

Thanks to all of you for your time and effort.

like image 469
Andrew-Dufresne Avatar asked Jul 12 '10 12:07

Andrew-Dufresne


2 Answers

Your understanding is correct, const char* is a contract that means you can't change memory through this particular pointer.

The problem is that C is very lax with type conversions. strcpy takes a pointer to non-const char, and it is implicitly converted from const char* to char* (as compiler helpfully tells you). You could as easily pass an integer instead of pointer. As a result, your function can't change content pointed by ptr, but strcpy can, because it sees a non-const pointer. You don't get a crash, because in your case, the pointer points to an actual buffer of sufficient size, not a read-only string literal.

To avoid this, look for compiler warnings, or compile, for example, with -Wall -Werror (if you are using gcc).

This behaviour is specific to C. C++, for example, does not allow that, and requires an explicit cast (C-style cast or a const_cast) to strip const qualifier, as you would reasonably expect.

Answer to the extended question

You are assigning a string literal into a non-const char, which, unfortunately, is legal in C and even C++! It is implicitly converted to char*, even though writing through this pointer will now result in undefined behaviour. It is a deprecated feature, and only C++0x so far does not allow this to happen.

With that said, In order to stop changing the pointer itself, you have to declare it as const pointer to char (char *const). Or, if you want to make it that both the contents pointed by it and the pointer itself don't change, use a const pointer to const char (const char * const).

Examples:

void foo (
        char *a,
        const char *b,
        char *const c,
        const char *const d)
    {
    char buf[10];
    a = buf; /* OK, changing the pointer */
    *a = 'a'; /* OK, changing contents pointed by pointer */

    b = buf; /* OK, changing the pointer */
    *b = 'b'; /* error, changing contents pointed by pointer */

    c = buf; /* error, changing pointer */
    *c = 'c'; /* OK, changing contents pointed by pointer */

    d = buf; /* error, changing pointer */
    *d = 'd'; /* error, changing contents pointed by pointer */
}

For all error lines GCC gives me "error: assignment of read-only location".

like image 142
Alex B Avatar answered Sep 18 '22 23:09

Alex B


"const" is really a compile-time thing, so don't expect a segfault unless the pointer points to some invalid memory. When using const pointers in a way that could potentially alter whatever they point to (in this case passing it to strcpy which accepts non-const), will generate a warning.

like image 34
atis Avatar answered Sep 21 '22 23:09

atis