On 2025-01-07, Julio Di Egidio <
julio@diegidio.name> wrote:
Hi everybody,
>
I am back to programming in C after many years:
indeed I have forgotten so many things, including
how much I love this language. :)
>
In particular, I am using C90, and compiling with
`gcc ... -ansi -pedantic -Wall -Wextra` (as I have
the requirement to ideally support any device).
>
To the question, I was reading this, but I am not
sure what the quoted passage means:
>
Matt Stancliff, "So You Think You Can Const?",
<https://matt.sh/sytycc>
<< Your compiler, at its discretion, may also choose
to place any const declarations in read-only storage,
so if you attempt to hack around the const blocks,
you could get undefined behavior. >>
An object defined with a type that is const-qualified
could be put into write-protected storage.
I do not understand if just declaring that a pointer
is to constant data may incur in that problem even
if the pointed data was in fact allocated with malloc.
I would say of course not, but I am not sure.
A pointer whose referenced type is const does not define an object of
that type. A const-qualified object may point to data which is not const
qualified. It may be converted to a pointer from whose type the
qualifier is removed, and then the converted pointer can be used to
modify the data.
>
E.g. consider this little internal helper of mine
(which implements an interface that is public to
do an internal thing...), where I am casting to
pointer to non-constant data in order to free the
pointed data (i.e. without warning):
>
```c
static int MyStruct_free_(MyStruct_t const *pT) {
assert(pT);
>
free((MyStruct_t *)pT);
The prototype of free is
void free(void *ptr);
when it comes to pointers, the C language permits implicit conversions
from "pointer to T" to "pointer to const T". Implicit meaning that
no cast is required: you simply pass the "T *" value as a "const T *"
function argument, or assign it to a "const T *" variable, etc.
If yuo have some malloced storage which you are referencing with a
"const T *" type, then you have a constraint violation if you free
that pointer; hence the cast is required.
Objects coming from malloc are not defined by a declaration.
ISO C defines the term /effective type/ (// indicates italics)
for the purposes of stating some rules regarding expressions accessing
objects. "The /effective type/ of an object that is not a byte array,
for an access to its stored value, is the declared type of the object"
says the N3301 draft of C23 in section 6.5.1 Expressions/General.
A footnote to this sentence clarifies that "allocated objects have no
declared type", almost certainly meaning dynamically allocated by
the malloc family.
A chunk of memory from malloc is a kind of byte array, so the
subsequents words apply to it:
"If a value is stored into a byte array through an lvalue having a type
that is not a byte type, then the type of the lvalue becomes the
effective type of the object for that access and for subsequent accesses
that do not modify the stored value."
When we write values into the bytes of a malloced object, it takes on
that type for subsequent reads.
In the same section, rules are given regarding what type an expression
may have which is accessing an object, in relation to that object's
effective type.
Indeed, the rules prohibit an object whose effective type is some "const
T" from being accessed as a plain "T".
However: it is not possible for a dynamically allocated object to
have an effective type of "const T"!!!
The reason is simple: the effective type of an allocated is established
when an object is written, and then holds for subsequent reads. An
object cannot be written through a "const T" lvalue.
How you got the "const MyStruct_t *" pointer is that you first
treated the object as "MyStruct_t *", and filled in its members.
Then you cast the pointer to "const MyStruct_t *".
Casting a pointer doesn't do anything to the referenced object's
effective type; it is not a write operation on the object.
Assuming, as said, that the data was originally
allocated with malloc, is that code safe or
something can go wrong even in that case?
So yes, it is safe to treat malloced objects as const and then remove
the const qualifier (as inescapably required by the API) when freeing.
-- TXR Programming Language: http://nongnu.org/txrCygnal: Cygwin Native Application Library: http://kylheku.com/cygnalMastodon: @Kazinator@mstdn.ca