How to initialize const in a struct in C (with malloc)

To expand on the answer by @Chris Dodd, I've been reading through the "language-lawyer" details of the standard and it appears that this code is well-defined:

struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

Alternatively, to dynamically create a complete struct object that's const qualified:

const struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

const struct deneme *read_only = mydeneme; 

Rationale:

The first thing one needs to establish when getting to the bottom of this, is if a so-called lvalue has a type and if so, does that type come with qualifiers or not. This is defined in C11 6.3.2.1/1:

An lvalue is an expression (with an object type other than void) that potentially designates an object; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a constqualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a constqualified type.

So clearly a lvalue does not only have a type, but also qualifiers. It is not a modifiable lvalue if it is const-qualified or if it is a struct with const-qualified members.

Moving on to the rules of "strict aliasing" and effective type, C11 6.5/7:

The effective type of an object for an access to its stored value is the declared type of the object, if any.87) If a value is stored into an object having no declared type through an lvalue having a type that is not a character 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. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.

  1. Allocated objects have no declared type.

This means that the allocated chunk returned by malloc has no effective type until something is stored inside that memory location through a lvalue write access, either through assignment or memcpy. It then gets the effective type of the lvalue used in that write access.

Notably, the type of the pointer pointing at that memory location is completely irrelevant. It might as well have been a volatile bananas_t* since it isn't used to access the lvalue (at least not yet). Only the type used for lvalue access matters.

Now this is where it gets fuzzy: it might matter if this write access is done through a modifiable lvalue or not. The rule of effective type above doesn't mention qualifiers and the "strict aliasing rule" as such doesn't care about if an object is qualified or not ("type" may alias "qualified type" and vice versa).

But there are other cases where it might matter if the effective type is read-only or not: most notably if we later attempt to do a non-qualified lvalue access to an object which effective type is const-qualified. (C11 6.7.3/6 "If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.") From the previously quoted part above lvalues, it makes sense for effective type to have a qualifier, even if the standard doesn't mention that explicitly.

Therefore to be absolutely sure, we have to get the type used for the lvalue access right. If the whole object is meant to be read-only, then the second snippet on top of this post should be used. Otherwise, if it is read/write (but potentially with qualified members), the first snippet should be used. Then it can never go wrong no matter how you read the standard.


You need to cast away the const to initialize the fields of a malloc'ed structure:

struct deneme *mydeneme = malloc(sizeof(struct deneme));
*(int *)&mydeneme->a = 15;
*(int *)&mydeneme->b = 20;

Alternately, you can create an initialized version of the struct and memcpy it:

struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

You can make deneme_init static and/or global if you do this a lot (so it only needs to be built once).


Explanation of why this code is not undefined behaviour as suggested by some of the comments, using C11 standard references:

  • This code does not violate 6.7.3/6 because the space returned by malloc is not "an object defined with a const-qualified type". The expression mydeneme->a is not an object, it is an expression. Although it has const-qualified type, it denotes an object which was not defined with a const-qualified type (in fact, not defined with any type at all).

  • The strict aliasing rule is never violated by writing into space allocated by malloc, because the effective type (6.5/6) is updated by each write.

(The strict aliasing rule can be violated by reading from space allocated by malloc however).

In Chris's code samples, the first one sets the effective type of the integer values to int, and the second one sets the effective type to const int, however in both cases going on to read those values through *mydeneme is correct because the strict-aliasing rule (6.5/7 bullet 2) permits reading an object through an expression which is equally or more qualified than the effective type of the object. Since the expression mydeneme->a has type const int, it can be used to read objects of effective type int and const int.


Have you tried to do like this:

int main(int argc, const char *argv[])
{
    struct deneme mydeneme = { 15, 20 };
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    memcpy(pmydeneme, &mydeneme , sizeof(mydeneme));
    return 0;
}

I have not tested but the code seems correct


Interesting I found this C99 way is working in clang but not in gcc

int main(int argc, const char *argv[])
{
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    *pmydeneme = (struct deneme) {15, 20};
    return 0;
}