Reputation: 869
I would like to allocate a structure on the heap, initialize it and return a pointer to it from a function. I'm wondering if there's a way for me to initialize const members of a struct in this scenario:
#include <stdlib.h>
typedef struct {
const int x;
const int y;
} ImmutablePoint;
ImmutablePoint * make_immutable_point(int x, int y)
{
ImmutablePoint *p = (ImmutablePoint *)malloc(sizeof(ImmutablePoint));
if (p == NULL) abort();
// How to initialize members x and y?
return p;
}
Should I conclude from this that it is impossible to allocate and initialize a struct on the heap which contains const members?
Upvotes: 46
Views: 23543
Reputation: 224082
To expand on the accepted answer, the reason why this is allowed:
ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);
Is because memory returned by malloc
(or more accurately, the object returned) has no effective type and is therefore allowed to be written to. Then once memcpy
is used to copy in the source object, the effective type of the allocated object becomes ImmutablePoint
.
This is spelled out in section 6.5p6 of the C standard:
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
ormemmove
, 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.
87) Allocated objects have no declared type.
Upvotes: 0
Reputation: 34148
If this is C and not C++, I see no solution other than to subvert the type system.
ImmutablePoint * make_immutable_point(int x, int y)
{
ImmutablePoint *p = malloc(sizeof(ImmutablePoint));
if (p == NULL) abort();
// this
ImmutablePoint temp = {x, y};
memcpy(p, &temp, sizeof(temp));
// or this
*(int*)&p->x = x;
*(int*)&p->y = y;
return p;
}
Upvotes: 12
Reputation: 101181
I like caf's approach, but this occured to me too
ImmutablePoint* newImmutablePoint(int x, int y){
struct unconstpoint {
int x;
int y;
} *p = malloc(sizeof(struct unconstpoint));
if (p) { // guard against malloc failure
*p.x = x;
*p.y = y;
}
return (ImmutablePoint*)p;
}
Upvotes: 1
Reputation: 76541
If you insist on keeping the const in the structure, you are going to have to do some casting to get around that:
int *cheat_x = (int *)&p->x;
*cheat_x = 3;
Upvotes: 2
Reputation: 239071
Like so:
ImmutablePoint *make_immutable_point(int x, int y)
{
ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
if (p == NULL) abort();
memcpy(p, &init, sizeof *p);
return p;
}
(Note that unlike C++, there is no need to cast the return value of malloc
in C, and it is often considered bad form because it can hide other errors).
Upvotes: 64