You just encountered one of the many cases C allows you to shoot your foot - for good and bad.
Some general remarks about your assumptions: const
is a guarantee you give to the compiler. So you have to make sure not to violate the contract. C does not have true constants [1]. Semantically const
is a qualifier` which allows the compiler additional error-checking. But that requires the type of the argument to be known to the compiler. This is true for functions with a proper prototype, but (normally [2]) not for those with a variable number of argument ("variadic functions"), as their types are not given at compile-time (and not explicitly available at run-time).
In
scanf("%d",&arr[i]);
You actually pass a problematic (see below) pointer type to scanf
. The function itself does not check, but just expects the correct type. It cannot, because C does not provide the type of an object at run-time.
A modern compiler should warn about argument type missmatch for printf
and scanf
. Always enable warnings (for gcc at least use -Wall -Wextra -Wconversions
) and pay heed to them.
Edit: After heavy discussion, I have to change my mind. It seems to be not undefined behaviour [3] for the reason given initially: passing a const int *
to scanf
which expects a int *
.
This because the object malloc
ed in func
has no effective type until the first write (6.5p6). That occurs in scanf
using an int *
. Thus the object has type int
- no const
. However, your further accesses through a const int *
are valid. 6.7.3p6 only makes the other direction undefined behaviour (for good reason).
Getting through undetected is only possible for variadic functions, because there is no information about the expected type available in the function declaration. Consinder something like:
void f(int *p)
{
*p = 0;
}
int main(void)
{
const int *p = ...;
f(p);
}
Here the compiler will generate a warning. Variadic functions are of the cases C cannot check for qualifier-correctness (this includes e.g. volatile
, too). There are more and some are quite subtle.
The only case of undefined behaviour here is to pass an incompatible differently qualified pointer than expected (6.7.6.1p2).
Recomendation: Enable warnings, but do not rely on the compiler detecting all flaws (not only true for const-correctness). If you need more saffety, C is not the right language. There are good reasons higher-level languages like Python, Java, etc. exist (C++ is somewhere in-between). OTOH the open ends in C allow things very hard to accomplish (if at all) in these languages where required. As allways: know your tools.
Note: You should not cast the result of malloc
& friends in C. And sizeof(char)
is useless. It is defined by the standard to yield 1
.
[1] As violating the contract is undefined behaviour, the compiler is actually free to store such data in read-only memory. This is vital for microcontrollers which run code and read some data straight from ROM, for example.
[2] Modern compilers can parse the format-string of printf
and scanf
family for the argument types and warn about missmatch. That requires this string to be a string literal (not a variable), though. That is a courtesy of the compiler writes a these functions are widely used.
[3] Basically undefined behaviour means anything can happen - Your computer might run away, nasal daemons may appear, or it might work. But all not guaranteed reliable or deterministic. So next time you start something else might happen.