I have this simple structure I want to initialize on the heap and return as a pointer in a function.
struct entry {
const char* const key; // We don't want the key modified in any way
const void* data; // But the pointer to data can change
struct entry* next;
};
There is one problem, I cannot calloc
it and initialize members one by one because key
is a const pointer. I found somewhere this syntax that works:
struct entry* entry = calloc(1, sizeof(struct entry));
*entry = (struct entry) { .key = key, .data = data, .next = NULL };
But I don't know what is going on with it: does it create an "anonymous" struct that is then copied to the place where *entry
lives? Is that safe to use or should I prefer creating a local struct that is then copied with memcpy
to the right location?
The assignment you presented is not correct and should not compile.
The correct way of initializing an allocated struct with const members is to allocate some memory, create a temporary struct entry object, and then use memcpy to copy the object to the allocated memory:
void* mem = malloc( sizeof( struct entry ) );
struct entry temp = { key , data , NULL };
memcpy( mem , &temp , sizeof( temp ) );
struct entry* e = mem;
This line:
*entry = (struct entry) { .key = key, .data = data, .next = NULL };
uses the assignment operator. The conditions for assignment operator (C11 6.5.16/2) include:
Constraints
An assignment operator shall have a modifiable lvalue as its left operand.
The definition of modifiable lvalue can be found in 6.3.2.1/1:
A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified 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 const-qualified type.
So *entry
is not a modifiable lvalue, because its type is a struct that has a member with const-qualified type. Therefore it is a constraint violation to have *entry
appear on the left-hand-side of the assignment operator.
The clang compiler (all versions I tried) appears to not give any diagnostic message for this constraint violation; this is clearly a compiler bug. gcc does give a diagnostic.
Regarding the second part of the question:
should I prefer creating a local struct that is then copied with memcpy to the right location?
As also explained by 2501, you can do this in C when writing into space allocated by the malloc
family. (If you had declared a named struct entry
object then it is less clear whether memcpy
ing over it is permitted).