Why does the following code raise no Warnings ?
Compiled with -Wall, -Wpedantic, -Wextra, and none of them raise a warning.
int main()
{
const char *p;
char a[] = "hey";
p = a;
(void) p;
return 0;
}
I would expect some kind of warning, such as assignment -Wdiscarded-qualifiers
You're assigning from a char * to a const char *. This is safe because you adding the const qualifier, not removing it.
If you did this:
char *p;
const char a[] = "hey";
p = a;
Then you would get a warning about discarding the const qualifier.
You are not discarding a qualifier. You're adding one. This feature is actually pretty important. It's one of those few cases where C actually offers a reasonable option for the programmer to protect himself from himself. For instance, have a look at the prototypes for string manipulation functions in the standard library:
char * strcpy ( char * destination, const char * source );
This gives us the information that destination will be altered, but source will not.
From comment below:
Will it also require that the destination parameter cannot be a const char *
There are ways around it so it's not required per se. We're talking C after all. But if you are planning to use an argument as an output argument it should not be declared as const. Here is an example where I'm using a const argument as output parameter. It's a function that sets the length of a string to zero:
// Note: Bad code. Do not do this at home. Ok, do it at home,
// but do not do it at work.
void removeString(const char * s)
{
char *p = (char*) s;
p[0] = 0;
}
But in order to do this, you first have to declare a pointer to non-const pointing to s, and then you have to add the cast (char*) to get rid of the warning. This is easy to do, but it's quite hard to do by mistake, so it serves as a pretty good protection.
Declaring an argument as a pointer to const gives two things:
It tells the programmer using the function that the function will not use the argument as an output argument, unless the author of the function is evil or doesn't know what he is doing.
It makes it harder (but not impossible) to change something you should not change by mistake in the function.
assignment from const char pointer to char pointer gives no warnings
Your example does something opposite. You assign the pointer to the constant object with the pointer to non constant object. So you do not discard anything hence no worning
Related
The following code snippet (correctly) gives a warning in C and an error in C++ (using gcc & g++ respectively, tested with versions 3.4.5 and 4.2.1; MSVC does not seem to care):
char **a;
const char** b = a;
I can understand and accept this.
The C++ solution to this problem is to change b to be a const char * const *, which disallows reassignment of the pointers and prevents you from circumventing const-correctness (C++ FAQ).
char **a;
const char* const* b = a;
However, in pure C, the corrected version (using const char * const *) still gives a warning, and I don't understand why.
Is there a way to get around this without using a cast?
To clarify:
Why does this generate a warning in C? It should be entirely const-safe, and the C++ compiler seems to recognize it as such.
What is the correct way to go about accepting this char** as a parameter while saying (and having the compiler enforce) that I will not be modifying the characters it points to?
For example, if I wanted to write a function:
void f(const char* const* in) {
// Only reads the data from in, does not write to it
}
And I wanted to invoke it on a char**, what would be the correct type for the parameter?
I had this same problem a few years ago and it irked me to no end.
The rules in C are more simply stated (i.e. they don't list exceptions like converting char** to const char*const*). Consequenlty, it's just not allowed. With the C++ standard, they included more rules to allow cases like this.
In the end, it's just a problem in the C standard. I hope the next standard (or technical report) will address this.
To be considered compatible, the source pointer should be const in the immediately anterior indirection level. So, this will give you the warning in GCC:
char **a;
const char* const* b = a;
But this won't:
const char **a;
const char* const* b = a;
Alternatively, you can cast it:
char **a;
const char* const* b = (const char **)a;
You would need the same cast to invoke the function f() as you mentioned. As far as I know, there's no way to make an implicit conversion in this case (except in C++).
However, in pure C, this still gives a warning, and I don't understand why
You've already identified the problem -- this code is not const-correct. "Const correct" means that, except for const_cast and C-style casts removing const, you can never modify a const object through those const pointers or references.
The value of const-correctness -- const is there, in large part, to detect programmer errors. If you declare something as const, you're stating that you don't think it should be modified -- or at least, those with access to the const version only should not be able to modifying it. Consider:
void foo(const int*);
As declared, foo doesn't have permission to modify the integer pointed to by its argument.
If you're not sure why the code you posted isn't const-correct, consider the following code, only slightly different from HappyDude's code:
char *y;
char **a = &y; // a points to y
const char **b = a; // now b also points to y
// const protection has been violated, because:
const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it
// with &x which is const char* ..
// .. so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const
// variable. oops! undefined behavior!
cout << x << endl;
Non-const types can only convert to const types in particular ways to prevent any circumvention of const on a data-type without an explicit cast.
Objects initially declared const are particularly special -- the compiler can assume they never change. However, if b can be assigned the value of a without a cast, then you could inadvertently attempt to modify a const variable. This would not only break the check you asked the compiler to make, to disallow you from changing that variables value -- it would also allow you break the compiler optimizations!
On some compilers, this will print 42, on some 43, and others, the program will crash.
Edit-add:
HappyDude: Your comment is spot on. Either the C langauge, or the C compiler you're using, treats const char * const * fundamentally differently than the C++ language treats it. Perhaps consider silencing the compiler warning for this source line only.
This is annoying, but if you're willing to add another level of redirection, you can often do the following to push down into the pointer-to-pointer:
char c = 'c';
char *p = &c;
char **a = &p;
const char *bi = *a;
const char * const * b = &bi;
It has a slightly different meaning, but it's usually workable, and it doesn't use a cast.
I'm not able to get an error when implicitly casting char** to const char * const *, at least on MSVC 14 (VS2k5) and g++ 3.3.3. GCC 3.3.3 issues a warning, which I'm not exactly sure if it is correct in doing.
test.c:
#include <stdlib.h>
#include <stdio.h>
void foo(const char * const * bar)
{
printf("bar %s null\n", bar ? "is not" : "is");
}
int main(int argc, char **argv)
{
char **x = NULL;
const char* const*y = x;
foo(x);
foo(y);
return 0;
}
Output with compile as C code: cl /TC /W4 /Wp64 test.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
Output with compile as C++ code: cl /TP /W4 /Wp64 test.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
Output with gcc: gcc -Wall test.c
test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type
Output with g++: g++ -Wall test.C
no output
I'm pretty sure that the const keyword does not imply the data can't be changed/is constant, only that the data will be treated as read-only. Consider this:
const volatile int *const serial_port = SERIAL_PORT;
which is valid code. How can volatile and const co-exist? Simple. volatile tells the compiler to always read the memory when using the data and const tells the compiler to create an error when an attempt is made to write to the memory using the serial_port pointer.
Does const help the compiler's optimiser? No. Not at all. Because constness can be added to and removed from data through casting, the compiler cannot figure out if const data really is constant (since the cast could be done in a different translation unit). In C++ you also have the mutable keyword to complicate matters further.
char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000
What happens when an attempt is made to write to memory that really is read only (ROM, for example) probably isn't defined in the standard at all.
Compilation of given code sample with gcc/g++ succeeds. There is no error for strchr call, which obviously assignes const char * to char *.
I've found strchr is declared as char * strchr(const char *, int) on two different sources pubs.opengroup.org and cplusplus.com
If strchr is implemented as to cast away constness, then why's that so?
If goal was to provide function which works both on char * and const char * strings - it could have been implemented using two different function names.
Can you give more thorough explanation on this.
#include <string.h>
int main () {
const char *str = "Sample string";
char * ptr;
//ptr = str; // Error: discards const qualifier - both on gcc and g++
pch = strchr(str,'S'); // Succeeds in assigning <const char *> to <char *>
*pch = '4'; // Runtime error: segmentation fault
return 0;
}
Tried it on Win7 using MSYS2/mingw-w64 gcc_v5.3.0 and TDM-gcc32 v5.1.0 .
When you call strchr with a char* you want a char* back, so that you don't need to do a cast on the result. C doesn't support function overloading, so the same strchr function is used for searching in a char* and in a const char*. It returns a non-const pointer in all cases. If you call it with a const char* it is basically casting away the const so it is the callers responsibility to use it safely (e.g. by assigning the result to a const char* immediately, to avoid any unsafe use of the returned pointer).
If goal was to provide function which works both on char * and const char * strings - it could have been implemented using two different function names.
Yes, it could have been, but it isn't done like that. The original version of C didn't support const at all, so it was always the programmer's responsibility to avoid trying to modify string literals. If different function names were used then it would have broken existing code that used strchr safely. It would have slowed the adoption of the new const keyword if traditional C code like this suddenly stopped compiling:
char arr[] = "foo bar";
*strchr(arr, 'r') = 'z';
The "spirit of C" is that it doesn't try to stop you doing unsafe things, it's your job to write correct code.
In C++ strchr is overloaded to preserve constness:
char* strchr(char*, int);
const char* strchr(const char*, int);
This means it automatically does the right thing, and it's much harder to accidentally write unsafe code with strchr.
I have this very simple test function that I'm using to figure out what's going on with the const qualifier.
int test(const int* dummy)
{
*dummy = 1;
return 0;
}
This one throws me an error with GCC 4.8.3.
Yet this one compiles:
int test(const int* dummy)
{
*(char*)dummy = 1;
return 0;
}
So it seems like the const qualifier works only if I use the argument without casting to another type.
Recently I've seen codes that used
test(const void* vpointer, ...)
At least for me, when I used void *, I tend to cast it to char for pointer arithmetic in stacks or for tracing. How can const void prevent subroutine functions from modifying the data at which vpointer is pointing?
const int *var;
const is a contract. By receiving a const int * parameter, you "tell" the caller that you (the called function) will not modify the objects the pointer points to.
Your second example explicitly breaks that contract by casting away the const qualifier and then modifying the object pointed by the received pointer. Never ever do this.
This "contract" is enforced by the compiler. *dummy = 1 won't compile. The cast is a way to bypass that, by telling the compiler that you really know what you are doing and to let you do it. Unfortunately the "I really know what I am doing" is usually not the case.
const can also be used by compiler to perform optimization it couldn't otherwise.
Undefined Behavior note:
Please note that while the cast itself is technically legal, modifying a value declared as const is Undefined Behavior. So technically, the original function is ok, as long as the pointer passed to it points to data declared mutable. Else it is Undefined Behavior.
more about this at the end of the post
As for motivation and use lets take the arguments of strcpy and memcpy functions:
char* strcpy( char* dest, const char* src );
void* memcpy( void* dest, const void* src, std::size_t count );
strcpy operates on char strings, memcpy operates on generic data. While I use strcpy as example, the following discussion is exactly the same for both, but with char * and const char * for strcpy and void * and const void * for memcpy:
dest is char * because in the buffer dest the function will put the copy. The function will modify the contents of this buffer, thus it is not const.
src is const char * because the function only reads the contents of the buffer src. It doesn't modify it.
Only by looking at the declaration of the function, a caller can assert all the above. By contract strcpy will not modify the content of the second buffer passed as argument.
const and void are orthogonal. That is all the discussion above about const applies to any type (int, char, void, ...)
void * is used in C for "generic" data.
Even more on Undefined Behavior:
Case 1:
int a = 24;
const int *cp_a = &a; // mutabale to const is perfectly legal. This is in effect
// a constant view (reference) into a mutable object
*(int *)cp_a = 10; // Legal, because the object referenced (a)
// is declared as mutable
Case 2:
const int cb = 42;
const int *cp_cb = &cb;
*(int *)cp_cb = 10; // Undefined Behavior.
// the write into a const object (cb here) is illegal.
I began with these examples because they are easier to understand. From here there is only one step to function arguments:
void foo(const int *cp) {
*(int *)cp = 10; // Legal in case 1. Undefined Behavior in case 2
}
Case 1:
int a = 0;
foo(&a); // the write inside foo is legal
Case 2:
int const b = 0;
foo(&b); // the write inside foo causes Undefined Behavior
Again I must emphasize: unless you really know what you are doing, and all the people working in the present and in the future on the code are experts and understand this, and you have a good motivation, unless all the above are met, never cast away the constness!!
int test(const int* dummy)
{
*(char*)dummy = 1;
return 0;
}
No, this does not work. Casting away constness (with truly const data) is undefined behavior and your program will likely crash if, for example, the implementation put const data in ROM. The fact that "it works" doesn't change the fact that your code is ill-formed.
At least for me, when I used void*, I tend to cast it to char* for
pointer arithmetic in stacks or for tracing. How can const void*
prevent subroutine functions from modifying the data at which vpointer
is pointing?
A const void* means a pointer to some data that cannot be changed. In order to read it, yes, you have to cast it to concrete types such as char. But I said reading, not writing, which, again, is UB.
This is covered more in depth here. C allows you to entirely bypass type-safety: it's your job to prevent that.
It’s possible that a given compiler on a given OS could put some of its const data in read-only memory pages. If so, attempting to write to that location would fail in hardware, such as causing a general protection fault.
The const qualifier just means that writing there is undefined behavior. This means the language standard allows the program to crash if you do (or anything else). Despite that, C lets you shoot yourself in the foot if you think you know what you’re doing.
You can’t stop a subroutine from reinterpreting the bits you give it however it wants and running any machine instruction on them it wants. The library function you’re calling might even be written in assembler. But doing that to a const pointer is undefined behavior, and you really don’t want to invoke undefined behavior.
Off the top of my head, one rare example where it might make sense: suppose you’ve got a library that passes around handle parameters. How does it generate and use them? Internally, they might be pointers to data structures. So that’s an application where you might typedef const void* my_handle; so the compiler will throw an error if your clients try to dereference it or do arithmetic on it by mistake, then cast it back to a pointer to your data structure inside your library functions. It’s not the safest implementation, and you want to be careful about attackers who can pass arbitrary values to your library, but it’s very low-overhead.
char *p="orkut" vs const char *p="orkut"
whats the difference btwn these two...
EDIT
from bjarne stroustrup,3rd edition page 90
void f()
{
char* p="plato";
p[4]='e' // error: assign to const;result is undefined
}
this kind of error cannont be general b caught until run time and implementations differ in their enforcement of this rule
Same with const char *p="plato"
Thats why iam asking the diffrence... Whats the significance of const here..
The const char* variant is correct.
You should not change memory that comes from a string literal (referred to as static storage usually). It is read only memory.
The difference is that the char* variant will allow you to write the syntax to change the data that it points to by dereferencing it. What it actually does though is undefined.
//Option 1:
char *p = "orkut";
*p = 'x';//undefined behavior
//Option 2:
const char *q = "orkut";
*q = 'x';//compiling error
I would rather have option 2 happen to me.
A declaration of const char * p means that the thing p points at is const, ie should not change. I say should not because it is possible to cast away the constness. As has been pointed out, changing a string literal is undefined and often leads to an access violation/segmentation fault.
Not this declaration is different than char * const p which means that p itself it const rather than the thing p points at.
The problem that Stroustrup discusses in what you're quoting is that in C++ a string literal will readily convert to "an rvlaue of type "pointer to char" (4.2/2 "Array-to-pointer conversion"). This is specifically so that the very common idiom of pointing a char* to a literal string wouldn't cause a bazillion programs to fail to compile. (especially when C++ was initially evolving from C).
If you can get away with declaring you pointer as char const* (or the equivalent const char*), you'll help yourself from running into problems like those described in the Stroustrup quote. However, you might well run into irritating problems using the pointer with functions that aren't 100% const correct.
See this question.
Basically,
char *p="orkut";
In this case, p is supposed to be read-only, but this is not enforced by all compilers (or the standard).
note that more recent gcc implementations will not let you do
char *foo = "foo";
they insist on the const (certainly in -wall -werror mode)
The following code snippet (correctly) gives a warning in C and an error in C++ (using gcc & g++ respectively, tested with versions 3.4.5 and 4.2.1; MSVC does not seem to care):
char **a;
const char** b = a;
I can understand and accept this.
The C++ solution to this problem is to change b to be a const char * const *, which disallows reassignment of the pointers and prevents you from circumventing const-correctness (C++ FAQ).
char **a;
const char* const* b = a;
However, in pure C, the corrected version (using const char * const *) still gives a warning, and I don't understand why.
Is there a way to get around this without using a cast?
To clarify:
Why does this generate a warning in C? It should be entirely const-safe, and the C++ compiler seems to recognize it as such.
What is the correct way to go about accepting this char** as a parameter while saying (and having the compiler enforce) that I will not be modifying the characters it points to?
For example, if I wanted to write a function:
void f(const char* const* in) {
// Only reads the data from in, does not write to it
}
And I wanted to invoke it on a char**, what would be the correct type for the parameter?
I had this same problem a few years ago and it irked me to no end.
The rules in C are more simply stated (i.e. they don't list exceptions like converting char** to const char*const*). Consequenlty, it's just not allowed. With the C++ standard, they included more rules to allow cases like this.
In the end, it's just a problem in the C standard. I hope the next standard (or technical report) will address this.
To be considered compatible, the source pointer should be const in the immediately anterior indirection level. So, this will give you the warning in GCC:
char **a;
const char* const* b = a;
But this won't:
const char **a;
const char* const* b = a;
Alternatively, you can cast it:
char **a;
const char* const* b = (const char **)a;
You would need the same cast to invoke the function f() as you mentioned. As far as I know, there's no way to make an implicit conversion in this case (except in C++).
However, in pure C, this still gives a warning, and I don't understand why
You've already identified the problem -- this code is not const-correct. "Const correct" means that, except for const_cast and C-style casts removing const, you can never modify a const object through those const pointers or references.
The value of const-correctness -- const is there, in large part, to detect programmer errors. If you declare something as const, you're stating that you don't think it should be modified -- or at least, those with access to the const version only should not be able to modifying it. Consider:
void foo(const int*);
As declared, foo doesn't have permission to modify the integer pointed to by its argument.
If you're not sure why the code you posted isn't const-correct, consider the following code, only slightly different from HappyDude's code:
char *y;
char **a = &y; // a points to y
const char **b = a; // now b also points to y
// const protection has been violated, because:
const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it
// with &x which is const char* ..
// .. so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const
// variable. oops! undefined behavior!
cout << x << endl;
Non-const types can only convert to const types in particular ways to prevent any circumvention of const on a data-type without an explicit cast.
Objects initially declared const are particularly special -- the compiler can assume they never change. However, if b can be assigned the value of a without a cast, then you could inadvertently attempt to modify a const variable. This would not only break the check you asked the compiler to make, to disallow you from changing that variables value -- it would also allow you break the compiler optimizations!
On some compilers, this will print 42, on some 43, and others, the program will crash.
Edit-add:
HappyDude: Your comment is spot on. Either the C langauge, or the C compiler you're using, treats const char * const * fundamentally differently than the C++ language treats it. Perhaps consider silencing the compiler warning for this source line only.
This is annoying, but if you're willing to add another level of redirection, you can often do the following to push down into the pointer-to-pointer:
char c = 'c';
char *p = &c;
char **a = &p;
const char *bi = *a;
const char * const * b = &bi;
It has a slightly different meaning, but it's usually workable, and it doesn't use a cast.
I'm not able to get an error when implicitly casting char** to const char * const *, at least on MSVC 14 (VS2k5) and g++ 3.3.3. GCC 3.3.3 issues a warning, which I'm not exactly sure if it is correct in doing.
test.c:
#include <stdlib.h>
#include <stdio.h>
void foo(const char * const * bar)
{
printf("bar %s null\n", bar ? "is not" : "is");
}
int main(int argc, char **argv)
{
char **x = NULL;
const char* const*y = x;
foo(x);
foo(y);
return 0;
}
Output with compile as C code: cl /TC /W4 /Wp64 test.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
Output with compile as C++ code: cl /TP /W4 /Wp64 test.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
Output with gcc: gcc -Wall test.c
test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type
Output with g++: g++ -Wall test.C
no output
I'm pretty sure that the const keyword does not imply the data can't be changed/is constant, only that the data will be treated as read-only. Consider this:
const volatile int *const serial_port = SERIAL_PORT;
which is valid code. How can volatile and const co-exist? Simple. volatile tells the compiler to always read the memory when using the data and const tells the compiler to create an error when an attempt is made to write to the memory using the serial_port pointer.
Does const help the compiler's optimiser? No. Not at all. Because constness can be added to and removed from data through casting, the compiler cannot figure out if const data really is constant (since the cast could be done in a different translation unit). In C++ you also have the mutable keyword to complicate matters further.
char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000
What happens when an attempt is made to write to memory that really is read only (ROM, for example) probably isn't defined in the standard at all.