It's more work. Not only to put all the annotations correctly, but only because it causes some real headaches. It's easy (implicit) to transition from non-const to const 1 pointer level deep. But the other way around -- it's really awkward to "remove" a const.
The strstr() signature is probably the shortest example / explanation why. To implement strstr(), you have to hack the const away to create the return value. Alternatively, create a mutable_strstr() variant that does the exact same thing. This is the kind of boilerplate that we don't want in C (and that C is bad at generating automatically).
Think about it this way: Real const data doesn't exist. It always gets created (written) somewhere, and usually removed later. One way where this works cleanly is where the data is created at compile time, so the data can be "truly" const, and be put in .ro section, and automatically destroyed when the process terminates. But often, we have situations where some part of the code needs to mutate the data that is only consumed as read only by other parts of the code. One man's const data is another man's mutable data.
In C, the support for making this transition work fluently is just very limited (but I think it's not great in most other languages, either).
> The strstr() signature is probably the shortest example / explanation why. To implement strstr(), you have to hack the const away to create the return value.
It seems to me the "hacking" is exactly the side-effect that is wanted. It's like the requirement in Rust to do certain kinds of things in an `unsafe { }` block (or using the `unsafe` package in Go): not that you want the compiler to prevent you from doing things completely, but that you want the compiler to prevent you from doing things by accident.
> One man's const data is another man's mutable data.
Yes; and the point of `const` for function parameters is to make sure that data isn't mutated unexpectedly.
> It seems to me the "hacking" is exactly the side-effect that is wanted.
It is not. It's broken at the surface level. If you passed a pointer that is already const on your side, you get back a non-const pointer back that allows you to write to your const memory.
And the C library‘s hacks around not being able to overload functions (which is the only reason for strstr et al‘s weird signature) wouldn‘t stop me from using const. It can be really useful both for documentation and for correctness. Think memcpy, not strstr.
But read 2 sentences further, where I had addressed this already.
> Think memcpy, not strstr.
See my other comments, I do think that making const function parameters is generally good for documentation and compatibility. strstr() is only a showcase for the limitations. Typically, const works for function parameters but not data structures.
The strstr() signature is probably the shortest example / explanation why. To implement strstr(), you have to hack the const away to create the return value. Alternatively, create a mutable_strstr() variant that does the exact same thing. This is the kind of boilerplate that we don't want in C (and that C is bad at generating automatically).
Think about it this way: Real const data doesn't exist. It always gets created (written) somewhere, and usually removed later. One way where this works cleanly is where the data is created at compile time, so the data can be "truly" const, and be put in .ro section, and automatically destroyed when the process terminates. But often, we have situations where some part of the code needs to mutate the data that is only consumed as read only by other parts of the code. One man's const data is another man's mutable data.
In C, the support for making this transition work fluently is just very limited (but I think it's not great in most other languages, either).