如何初始化堆上结构体的 const 成员

问题描述 投票:0回答:5

我想在堆上分配一个结构,初始化它并从函数返回指向它的指针。我想知道在这种情况下是否有办法初始化结构体的 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 成员的堆上分配和初始化一个结构体?

c
5个回答
64
投票

像这样:

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
的返回值,并且它通常被认为是不好的形式,因为它可以隐藏其他错误)。


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

2
投票

如果你坚持在结构中保留常量,你将不得不进行一些转换来解决这个问题:

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

1
投票

我喜欢caf的做法,但这也发生在我身上

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

0
投票

为了扩展已接受的答案,允许这样做的原因:

ImmutablePoint init = { .x = x, .y = y };
ImmutablePoint *p = malloc(sizeof *p);
memcpy(p, &init, sizeof *p);

是因为

malloc
返回的内存(或更准确地说,返回的对象)没有有效类型,因此允许写入。 那么一旦使用
memcpy
复制到源对象中,分配对象的有效类型就变成了
ImmutablePoint

这在 C 标准第 6.5p6 节中有详细说明:

访问其存储值的对象的有效类型是 对象的声明类型(如果有)。 87) 如果一个值为 通过左值存储到没有声明类型的对象中 非字符类型的类型,则为左值的类型 成为该访问的对象的有效类型 后续访问不会修改存储的值。 如果一个值为 使用

memcpy
memmove
复制到没有声明类型的对象中, 或者被复制为字符类型的数组,那么有效类型 该访问和后续访问的修改对象 不修改该值是对象的有效类型 如果有值,则复制该值。
对于对某个值的所有其他访问 没有声明类型的对象,该对象的有效类型是 只是用于访问的左值的类型。


87) 分配的对象没有声明类型。

© www.soinside.com 2019 - 2024. All rights reserved.