tags:

views:

175

answers:

5

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.

A: 

const char* is the same as char const* and not char* const. So this means a pointer to something you can't change.

Elaborating after your edit. The first form inhibits the data to be changed (unless you cast implicitly or explicitly) the second inhibits the pointer itself to be changed.

What you do in your edited version is to change the pointer. This is legal here. If you'd inhibit both you'd have to write char const* const.

Jens Gustedt
I tried it with ``char const *ptr`` and got the same result as ``const char *ptr``. Unlike "So this means a pointer to something you can't change." I am able to change the value in both cases.
Andrew-Dufresne
you change the local copy of an argument, which is a pointer to "const" data. It's fine, you're not saying the pointer can't be changed, and in fact, it can. See the Question+Answer given as link in the comment to your question
ShinTakezou
+11  A: 

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".

Alex B
Kindly have a look at edit of my question and guide me further. Thanks
Andrew-Dufresne
@andrew-dufresne, I've added some examples.
Alex B
+1 Thanks a lot
Andrew-Dufresne
Note that the type of a string literal in C is an array of `char`, not `const char` (as in C++).
caf
@caf, I just learned that the same applies to C++, prior to C++0x. Assigning a string literal to a non-const char does not give any errors or warnings in both C and C++ with `-Wall -Wextra -ansi -pedantic`!
Alex B
I'll take your word for it - I certainly don't claim to be a C++ expert! The reason it is that way in C, at least, is that string literals (and C programs using them) pre-date the existence of `const`.
caf
+1  A: 

IIRC the const means that the value of the parameter may not be changed. I your case, the value is the address the pointer points to. strcpy doesn't change the address the pointer points to, but the memory the pointer points to.

Using const here makes sure that the memory reference (address) isn't changed. The memory referenced may be changed, however. This is what's happening here. Assigning a new address to the pointer would result in an error.

SIDE NOTE
I'd consider your foo method insecure. You'd better pass the maximum length of str as well and perform a length check, otherwise you'll be open for buffer overflows.

EDIT
I took the following from this site:

The const char *Str tells the compiler that the DATA the pointer points too is const. This means, Str can be changed within Func, but *Str cannot. As a copy of the pointer is passed to Func, any changes made to Str are not seen by main....

Thorsten Dittmar
You said "const here makes sure that the memory reference isn't changed". This time, I didn't use `strcpy()`. I used `str="Jurassic park";` in `foo()` and it too got printed. Although the memory referenced did not changed, but the memory reference did change, as `str` pointed to "Jurassic park" in `foo()`, when it is supposed to constantly point to `"Its just to fill the space"`. Please explain, I would be grateful.
Andrew-Dufresne
When I look at your edit, this is exactly what I'd have expected. The caller can be sure that the pointer is not changed after calling the function. And from your sample output, the pointer isn't changed from the view of the caller.
Thorsten Dittmar
I edited my post and added information from http://www.lix.polytechnique.fr/~liberti/public/computing/prog/c/C/SYNTAX/const.html. Basically what they say is that when using const, a copy of the original pointer is passed. While that pointer can be changed in your function, the original pointer will not be changed - this is what you're seeing.
Thorsten Dittmar
-1 You have it the wrong way around `const char*` means the data pointed to will not change, not the pointer itself. However, strcpy() does not know you have given it a const char*, so it goes ahead and overwrites the data (if it can) anyway.
JeremyP
+1 Thanks Thorsten.
Andrew-Dufresne
+3  A: 

"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.

atis
+4  A: 

1- What does const char *str in prototype actually means? Does this mean that function cannot change the contents of str?

Yes! This means we cannot change the contents of something(either a char or an array of chars) pointed to by str.

If that is so then how come the above program changes the value?

Thats because strcpy()'s prototype is char * strcpy ( char * destination, const char * source );

In your code there is an implicit conversion from const char* to char* type because strcpy() requires its first argument to be of the type char*.

Technically speaking your code is incorrect rather dangerous. If you try the same code in C++ you'll surely get an error. C++ doesn't allow such implicit conversions but C does.

Prasoon Saurav