如何在堆上初始化结构体的常量成员

44

我想在堆上分配一个结构体,对其进行初始化,并从函数中返回指向它的指针。我想知道在这种情况下是否有一种方法可以初始化结构体中的const成员:

#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;
}

从这个结果中,我应该得出结论:在堆上分配和初始化包含const成员的结构体是不可能的吗?

4个回答

63

像这样:

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;
}

(请注意,与C++不同,C语言中不需要对malloc的返回值进行强制类型转换。这样做通常被认为是一种不良习惯,因为它可能掩盖其他错误。)


8
请注意,在 init 初始化中使用 .x 和 .y 是 C99 的语法,如果您的编译器不支持,当然可以使用未命名条目。 - Trent
3
此处返回的指针所指向的对象实际上从未被定义,因为它是由malloc()函数创建的。可以考虑将p定义为void *p;,并将两个sizeof *p实例更改为sizeof init - caf
1
@caf 是的,你的版本是正确的,我的评论是在我意识到这一点之前发表的。据我所知,这是使用分配的内存初始化const类型的唯一合法方式。 - this
3
是的,可以 - 在x86-64上使用最低级别的优化进行编译,gcc会将上述函数编译为一个不调用memcpy()的函数。尽管如此,一旦你调用了malloc()memcpy()就不太可能成为一个问题了。 - caf
5
没关系,因为 sizeof 明确地不评估其参数,所以表达式的行为仍然是定义好的。 - caf
显示剩余4条评论

12

如果这是C语言而不是C ++,我认为除了破坏类型系统外别无解决办法。

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;
}

1
如果这是C语言,那么对malloc的返回值进行强制类型转换是不必要且多余的。 - dreamlax

2

如果您坚持将const放在结构体中,那么您将需要进行一些强制类型转换才能解决该问题:

int *cheat_x = (int *)&p->x;
*cheat_x = 3;

3
那不会引发未定义行为吗? - dreamlax
3
如果你分配了内存,我认为这是可以的。 - NateS

1

我喜欢咖啡的方法, 但这个也发生在我身上

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;
}

1
抱歉,我这里看不到const。你能解释一下吗? - kevinarpe
1
@KCArpe:我分配并分配了一个与“ImmutablePoint”在结构上完全相同但可变的“struct”,然后在返回语句中转换指针,以便用户永远看不到可变版本。这是一种相当标准的滥用类型系统的方法。相对于caf的解决方案的缺点是它不是DRY(http://en.wikipedia.org/wiki/Don%27t%5Frepeat%5Fyourself):如果有人编辑“ImmutablePoint”,我也需要编辑“unconstpoint”。 - dmckee --- ex-moderator kitten
1
此外,为了避免有人更改ImmutablePoint的大小问题,最好添加一个assert(sizeof(ImmutablePoint) == sizeof(struct uconstpoint));,尽管编译器也可以使它们不同。 - Ryan Haining

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接