user268344
user268344

Reputation: 869

How to initialize const members of structs on the heap

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

Answers (5)

dbush
dbush

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 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.


87) Allocated objects have no declared type.

Upvotes: 0

John Knoeller
John Knoeller

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

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

R Samuel Klatchko
R Samuel Klatchko

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

caf
caf

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

Related Questions