Adding a qualifier to a pointed-to type is an implicit conversion, whereas removing a qualifier needs an explicit cast.
The prototype of bsearch()
is written in a way that allows both of the following usages without explicit casting:
int needle = 0xdeadbeef;
int foo[42] = { ... };
int *p = bsearch(&needle, foo, 42, sizeof *foo, cmpi);
const int bar[42] = { ... };
const int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
However, this means that it's possible to use bsearch()
- as well as many other libc functions - to remove const-qualification without a warning, eg if we had written
int *q = bsearch(&needle, bar, 42, sizeof *bar, cmpi);
This is perfectly legal: undefined behaviour only occurs if we actually used q
to modifiy bar
.
You should also keep in mind that const-qualifying a pointer parameter only affects which arguments are accepted without a cast, but does not guarantee that the function won't modify the pointed-to object. That is merely a convention followed by virtually all existing code, but it is not enforced by language semantics.
In particular, compilers can't use this information for optimizations in the calling code - the compiler needs to see the function body because it's legal to remove the const-qualification from the pointer and modify the pointed-to object if the object itself wasn't declared const
.
In the past, I assumed that additionally restrict-qualifying the pointer argument would enforce the immutability, but a careful re-reading of section 6.7.3.1 leads me to believe that this is not the case: The constraints placed on objects pointed-to by restrict-qualified pointers only come into effect if the pointer is actually used to access the object, but calling code can't make that assumption from the prototype alone...