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.
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".
"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.
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.
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....
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.
Related
I really do not know how to put a better title so please be patient with me and have mercy.
What I know.
When I do something like this:
#include <stdio.h>
int main ( void ){
const char *arr = "Hello";
arr[0] = 'B';
printf( "Arr = %s\n", arr );
}
I won't get a segfault because applying that const qualifier I made a promise to the compiler that I am not going to touch that value where arr points to.
At least on my system ( Linux mint 18.3 with GCC 7.2.0) I get:
program.c:6:16: error: assignment of read-only location ‘*arr’
arr[0] = 'B';
^
NEXT
When I do:
const char *const arr = "Hello";
Like in the following program:
#include <stdio.h>
int main ( void ){
const char *const arr = "Hello";
while ( *arr != '\0' ){
arr++;
}
printf( "Arr = %s\n", arr );
}
The compiler also knows that I promise to not increment the pointer and it see it:
program.c:7:16: error: increment of read-only variable ‘arr’
arr++;
^~
What I do not Understand.
How exactly do compilers treat a situation like this one:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr = calloc( 256 * sizeof( *arr ), sizeof( *arr ) );
strcpy ( (char*)arr , "Hello" );
printf( "Arr = %s\n", arr );
free ( (char*)arr );
}
Here I am forced to cast arr when I call strcpy() and same for free().
But why the compiler does not see (ignores) the fact that even if I made a "promise" that I will not try to modify that variable it ignores the const qualifier?
Moreover, when I do the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( void ){
const char *const arr;
while ( *arr != '\0' ){
arr++;
}
free ( (char*)arr );
}
The compiler sees that I am trying to increment the pointer:
program.c:9:16: error: increment of read-only variable ‘arr’
arr++;
^~
But ignores the fact that I made a promise that I will not modify the value.
Does the fact that the malloc is involved here have a meaning somehow to the compiler that it needs to ignore all those const qualifiers`? ...
Or am I missing something important here?
You are allowed to remove const from a pointer if it was not const originally.
calloc returns a void *. When you assign it to a const char *, you add const. But the rules of C allow you to add const temporarily and remove it later. You might want to do this, for example, when some routines create data, then give the data to some other routines that should only read the data, but, later, the pointer is passed back to be freed or further changed, in which case const must be removed.
In short, const is a convenience for software that wants to obey the rules. It asks the compiler to notify you when you accidentally try to write with a const pointer. It does necessarily stop you from deliberately breaking the rules by removing const inappropriately.
(To be more precise about the opening sentence: C is very flexible about pointer conversions. You can largely convert different types of pointers, as long as alignment requirements are met. It is actually using pointers to access memory that runs into trouble when done incorrectly.)
Part of this is very simple:
strcpy ( (char*)arr , "Hello" );
See that (char*)? When you do that, you take back the promise. If you had written
strcpy (arr, "Hello");
with no cast, the compiler would have objected.
The other part of this is understanding that const annotations on pointers in C are only weakly connected to whether or not the pointed-to memory location is writable. Heap blocks returned by malloc and calloc are always in a memory area that is writable. When you set a const pointer to point to a heap block returned by malloc, the compiler will object to any attempt to write through that pointer, but if you cast away the const (take back the promise), or if you just ignore the warning, the write will work fine at runtime, because the memory area is still writable even though the pointer was const.
Conversely, string literals are usually in a memory area that isn't writable. You can still refer to them using non-const pointers, and the compiler won't object to you writing through those pointers, but the writes won't work at runtime, because the memory area is read-only. And data objects declared with const (e.g. const int numbers[] = { 1, 2, 3 };) are also put in a read-only memory area, so again you can't write to them — whether or not you do it through a non-const pointer.
It is usually possible to change whether a memory area is writable, but you have to use operating system functions like mprotect. Casts don't do that for you -- they usually don't generate any code at all, in fact.
A const that applies directly to a variable definition means that the value of that variable can never change.
But when a pointer points to a const type, that does NOT mean the thing it points to will never change, only that it cannot be changed via that particular pointer.
For example, this program is entirely valid:
#include <stdio.h>
int main(void) {
char str[] = "abcd";
const char* p = str;
str[0] = 'x';
printf("%s\n", p); /* Prints "xbcd" */
return 0;
}
Note the data p points to was changed via its original object, even though p is declared as pointing to const. And then p continues to point at the object str[0] which has a new value.
In your calloc example, there is no const variable definition, so the data is not guaranteed to be unchanged. You made a promise not to change it via arr only. But then the explicit cast creates another pointer that doesn't have the const restriction, and it's fine to change that originally non-const data using it.
The cast to char * here
strcpy ( (char*)arr , "Hello" );
makes compiler ignore your promise in this place only.
If the object arr points to was originally created as non-const, then you're fine. If not, then it's undefined behaviour and you risk getting a segfault.
We are dealing with a constant pointer,
so the address it holds cannot change.
But the content of that referenced memory address is supposed to be mutable...
Still,I get compilation/segmentation fault errors when trying to do so.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char * const c_ptr = "firstValue"; // now c_ptr is a const ptr to an immutable string literal,
//we can't change it unless we declare char [] instead
printf("%s",c_ptr);
*c_ptr="hsdsdsd"; // better to use strcpy(c_ptr, "hsdsdsd");
printf("%s",c_ptr);
return 0;
}
main.c: In function 'main':
main.c:8:8: warning: assignment makes integer from pointer without a cast [enabled by default]
*c_ptr="hsdsdsd";
Segmentation fault (core dumped)
Well, the data that the pointer points at can change for a constant pointer, but not when you initialize it using a string literal. They have the rather curious property of having type char *, but being unable to change.
So, you can do:
char data[10] = "foobar";
char * const ptr = data;
printf("%s\n", ptr); // prints foobar
*ptr = 'z';
printf("%s\n", ptr); // prints zoobar
char * const c_ptr means a non-const pointer to const data. Meaning that you made the pointer variable itself read-only, but not the pointed-at data.
If you do *c_ptr = 'a' then the compiler wouldn't prevent it, because you told it that the pointed-at data is read/write. Which isn't true in this case, it is a string literal and writing to it will cause undefined behavior, which is why you get a crash.
C11 6.4.5/7
If the program attempts to modify such an array, the behavior is
undefined.
Fix the code by changing the declaration to const char* c_ptr.
Or alternatively const char* const c_ptr.
Now as it turns out, this is immutable, because if you would now attempt strcpy(c_ptr, "str") you would get an invalid pointer conversion, since the function expects a char*.
*c_ptr="hsdsdsd"; is nonsense and will not compile on a compliant C compiler, because you try to assign an address to a single char variable, which is not allowed. (A constraint violation of the simple assignment rules).
First, C strings are null terminated char arrays, and you cannot assign arrays, you have to change each element of it.
Also, generally the compiler put string literals like "firstValue" into a readonly memory section, so you cannot change it, you will have to use a char array to initialize the string.
char s[] = "firstValue";
const char*const ptr = s;
ptr[0] = 'z'; // change contents pointed by ptr, or:
strncpy(ptr, "abc", 3);
I implemented my own strcpys to find if there is any difference between src as const char* & char *, but don't find any difference between the following 2 & both worked the same.
char * my_strcpy(char*dest, char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
char * my_strcpy2(char*dest, const char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
Is there any reason that the strcpy takes the source pointer as const char* instead of char*?
Is there any reason that the strcpy takes the source pointer as char* instead of const char*?
The source pointer should be const char *. The reason is common for all functions (not just strcpy) that do not intend to change the source accidentally inside the function.
The practice applies to both library functions like strcpy or your own custom functions. As with library function like strcpy there is no chance that the source is accidentially changed. But for your own (or everyone else) custom function, anything can happen. And if you do modify it accidentially, then you would get a compile error telling you so. And that's when the const is making a difference.
"don't find any difference between the following 2" -- what could the differences be, in your wildest dreams?
The only difference the const declaration has is on the caller side. By the const parameter declaration the function promises not to change the memory accessed through the pointer. It promises only to read through it, which is consistent with the semantics of strcpy(). (Note: Whether the function actually does not write through the pointer is not guaranteed at all. But it promises.)
The caller can therefore call the function with a pointer to constant data and assume that the function will not attempt to write to it.
That is important syntactically, logically and materially:
The language permits the caller to provide a pointer to const data as the argument (it would not allow that for your first non-const version).
The caller can be sure that sensitive data is not altered inside the function as a side effect; imagine a pointer into kernel data structures here. (Counter-example: strtok() writes to the original string, which is therefore not declared const!)
The data may be physically read-only (like, it may be burned into the ROM of a controller), so that an attempt to write to it would cause, well, to quote the excellent Max Barry: a "catastrophic system failure. (...) I'm not saying it's a big deal."
For starters function strcpy returns pointer to the destination string. Your function should also return pointer to the destination string. It is a common convention for functions that process strings in the C Standard.
So the function can look the following way
char * my_strcpy( char *dest, const char *src )
{
char *p = dest;
while ( *p++ = *src++ );
return dest;
}
In this case it can be called for example the following way
const char *hello = "Hello, World!";
char s[14];
puts( s, hello );
Specifying the second parameter of the function as a pointer to a constant character string means 1) that the function guarantees that it will not change the pointed string and 2) allows to pass to the function constant strings.
A pointer to a non-constant object can be implicitly converted to a pointer to constant object. The reverse operation is not allowed without explicit casting.
So if the parameter is declared like pointer to a constant string then you can pass non-constant strings along with constant strings to the same function as arguments. Otherwise the function can not be called with constant strings because the compiler will issue an error saying that it unable to convert an object of type const char * to an object of type char *.
Your answers are very much sought to clear this major lacuna in my understanding about const that I realized today.
In my program I have used the statement const int *ptr=&i; but haven't used any const qualifier for the variable i.Two things are confusing me:
1) When I try to modify the value of i using ptr ,where I have used const int *ptr=&i;,I get the error assignment of read-only location '*ptr'|,even though I haven't declared the variable i with the const qualifier.So what exactly the statement const int *ptr=&i; mean and how does it differ from int * const ptr=&i;?
I had it drilled into my head that const int *ptr=&i; means that the pointer stores the address of a constant,while int * const ptr=&i; means the pointer is itself constant and can't change.But today one user told me in discussion(LINK) that const int *ptr means the memory pointed to must be treated as nonmodifiable _through this pointer_.I find this something new as this kinda means "some select pointer can't alter the value(while others can)".I wasn't aware of such selective declarations!!But a 180k veteran attested to it that that user is correct!!.So can you state this in a clearer,more detailed and more rigorous way?What exactly does ``const int *ptr=&i; mean?
2) I was also told that we can lie to the program in the statement const int *ptr=&i; by assigning the address of a non-constant to the pointer.What does it mean?Why are we allowed to do that?Why don't we get a warning if we assign the address of a non-constant to the pointer ptr which expects address of a constant?And if it is so forgiving about being assigned address of non-constant,why it throws an error when we try to change the value of that non-constant,which is a reasonable thing to do,the pointed variable being a non-constant?
#include <stdio.h>
int main ()
{
int i=8;
const int *ptr=&i;
*ptr=9;
printf("%d",*ptr);
}
error: assignment of read-only location '*ptr'|
The definition const int *ptr = &i; essentially says “I will not modify i through ptr.” It does not say that the int that ptr points to is const, it says that ptr should not be used to modify it.
This conversion is allowed because it makes sense: Since i is not const, I am allowed to modify it, but I can also choose not to modify it. No rule is broken when I choose not to modify i. And, if I create a pointer to i and say “I am not going to use this pointer to modify i”, that is fine too.
The reason you would want to do this is so that you can pass the address of an object to a routine that takes a pointer to a const object. For example, consider the strlen routine. The strlen routine does not modify its input, so its parameter is a pointer to const char. Now, I have a pointer to char, and I want to know its length. So I call strlen. Now I am passing a pointer to char as an argument for a parameter that is pointer to const char. We want that conversion to work. It makes complete sense: I can modify my char if I want, but the strlen routine is not going to, so it treats them as const char.
1) You get an error with *ptr = something; because you declared ptr as a pointer to const int, but then you violated your promise not to use ptr to modify the int. The fact that ptr points to an object that is not const does not negate your promise not to use ptr to modify it.
2) It is not a lie to assign the address of a non-const object to a pointer to const. The assignment does not say that the object is const, it says that the pointer should not be used to modify the object.
Additionally, although you have not asked this, the const attribute in C is not completely binding. If you define an object to be const, you should not modify it. However, there are other situations in which a pointer to const is passed to a routine that converts it to a pointer to non-const. This is effectively a defect in the language, an inability to retain all the information necessary to handle const in the ways we might prefer. An example is the strchr routine. Its declaration is char *strchr(const char *s, int c). The parameter s is const char * because strchr does not change its input. However, the pointer that strchr routines is derived from s (it points to one of the characters in the string). So, internally, strchr has converted a const char * to char *.
This allows you to write code that passes a char * to strchr and uses the returned pointer to modify the string, which is fine. But it means the compiler cannot completely protect you from mistakes such as passing a const char * to strchr and using the returned pointer to try to modify the string, which may be an error. This is a shortcoming in C.
Unravelling this:
const int *ptr means that "*ptr is a const int"; i.e. "ptr is a pointer to a const int". You can't write *ptr = 9 since *ptr is a constant. You can however write int j; ptr = &j; since you can allow the pointer ptr to point at a different int.
int * const ptr means "ptr is a constant pointer to an int". You can write *ptr = 9 since you are not changing the address that ptr is pointing to. You can't assign it to something else though; i.e. int j; ptr = &j; will not compile.
As for the second part (2), it's very useful to have const int* ptr, especially in function prototypes since it tells the caller of the function that the variable to which ptr is pointing will not be modified by the function. As for assignment, if you had int* m = &i, then ptr = m is ok but m = ptr is not ok since the latter will 'cast away the constness'; i.e. you've circumvented the const.
What does (char* )str do in the below code?
/**
* Main file
*/
#include <assert.h>
#include <mylib.h>
int main()
{
const char str[] = "this is my first lab\n";
int ret=1;
ret = my_print((char *)str, sizeof(str));
assert(!ret);
return 0;
}
This code is written by my instructor.
my_print is a function which receives a pointer to a string and the size of that string. I am confused on why do we have to use (char *)str to pass the string to the my_print function. What does it actually do?
It casts away the const.
This means it makes your program likely to crash in case my_print modifies that string since its memory may be marked as read-only. So it's generally a bad idea to remove the const modifier through a cast.
In your case it looks a bit like whoever implemented my_print didn't think that string to be printed would never have to be modified and thus didn't make it accept a const char * argument.
So what you should do instead of the cast is changing the definition of my_print to accept a const char * instead of a char * as its first parameter.
That is "type casting" (or "type conversion"). In other words, it tells the compiler to treat one type as another type.
What this specific conversion does is tell the compiler to treat the constant string as not constant. If the called function tries to modify the string, it may not work, or may even crash the program, as modifying constant data is undefined behavior.
It is a typecast, i.e. it changes the datatype. (char*) means type cast to type "pointer to char"