【问题标题】:How to manipulate an array of structs with const members如何使用 const 成员操作结构数组
【发布时间】:2016-05-04 19:15:27
【问题描述】:

我试图了解“const”在 C 中的工作原理。 我想要创建的是一个polygon struct,它的成员不能被改变。

我从创建以下结构开始

struct vector2{
    float x;
    float y;
};

struct polygon2{
    const size_t count;
    struct vector2* const points;
};

为了创建一个多边形,我创建了以下函数:

struct polygon2* polygon2_create(size_t count)
{
    struct vector2* points = calloc(count, sizeof *points);
    struct polygon2 temp = {.count = count,
                            .points = points};
    struct polygon2* actual = malloc(sizeof *actual);
    memcpy(actual, &temp, sizeof(*actual));

    return actual;
}

I believe this function doesn't cause undefined behavior.

这样我可以做类似的事情

struct polygon2* poly = polygon2_create(30);
poly->points[3] = (struct vector2){7.1, 5.3};

但我做不到

poly->points = NULL;

也没有

poly->count = 3;

这很棒。我确定不会不小心更改struct polygon的内容。

但我也想让vector2 的成员成为常量。 如果我将vector2 更改为:

struct vector2{
    const float x;
    const float y;
};

我不能再这样做了:

poly->points[3] = (struct vector2){7.1, 5.3};

我想知道为什么。我预计将vector2的成员设为const我将无法做到这一点

poly->points[3].x = 3

但我仍然可以这样做

poly->points = otherpoint;

有人可以解释我缺少什么吗?以及如何实现以下目标:

  • 创建一个“不可变”vector2 struct
  • 创建一个polygon struct,其pointscount 成员无法更改,但points 指向的内容可以“交换”。

【问题讨论】:

  • const 代表常量值。a.k.a 一个不能改变其值的变量的值。例如:const int x=1;x=2; 会报错。在我的 gcc 版本中,错误将是 assignment of read-only variable x
  • 你不能使动态分配的结构不可变,因为你必须在分配后修改它以分配初始值。
  • @Mike:这是错误的! C 没有符号常量,除了 enum-constantsconst 限定对象仍然是变量,但程序员保证不会更改它们,即值 不会更改。如果程序员违反此合同,它可以改变,从而导致未定义的行为。编译器的诊断是一种礼貌,但不能保证,例如如果您抛弃 const 或使用指针(在某些情况下,即使没有强制转换,编译器也不会检测到这种情况)。

标签: c struct constants


【解决方案1】:

const 类型的限定意味着该类型的左值表达式不可修改。特别是,const 类型或具有至少一个 const 成员的复合类型的左值递归地不能是赋值运算符的左侧,指向此类对象的指针也不能是 free()d。

此外,合格的类型,包括const-qualified,与它们的非限定对应物和底层非限定类型的不同限定版本不同。这会影响具有限定成员的复合类型的兼容性。

另一方面,不要将const 误认为该值实际上是常量的承诺。可能是同一对象可以由多个左值指定,一些const 和其他非const。在这种情况下,可以通过指定它的任何非const 左值来修改该对象,并且即使通过指定它的const 左值也可以看到这些修改。

关于你的问题的细节:

  • 我同意您的函数polygon2_create() 是有效的并且具有明确定义的行为。特别是,结构的const 成员可以在初始化程序中初始化,并且诸如memcpy() 之类的函数可以修改存储可以通过const 左值引用的对象的内存。不过,您的编译器可能会警告 memcpy()
  • 更一般地说,您描述的初始化和分配行为和约束是正确的。
  • 至于poly->points[3] = (struct vector2){7.1, 5.3};,如果struct vector2 的成员是const,这怎么能被接受?如果允许,分配肯定会修改它们,而阻止这正是const 的重点。或者,如果您更喜欢引用而不是权威,C2011 6.3.2.1/1 指定如果结构类型具有任何 const 成员,则该类型的左值表达式不可修改。

听起来您对整体结构赋值的语义感到困惑。如果将一个结构分配给不同的结构,则不会将一个结构替换为另一个结构;而是将一个结构的 value 复制到另一个结构。这完全类似于对简单类型的赋值,例如int

你问过,

我怎样才能实现以下目标:

  • 创建一个“不可变的”vector2 结构

在可能的范围内,您已经知道如何做到这一点。如果您将所有成员设为const,则它们不能通过struct vector2 类型的表达式进行修改。然而,正如我之前所说,这并不意味着绝对不变。 C 没有这样的东西。

  • 创建一个多边形结构,其 points 或 count 成员不能更改,但 points 指向的东西可以“交换”。

我不确定我是否理解交换点的能力与您想要的不可修改级别是一致的。当然,如果struct vector2const 成员,那么您不能分配给该类型的左值表达式。不过,您仍然可以通过memcpy() 或通过转换为可修改类型来执行交换。然而,这些机制至少违反了const-ness 的精神。您的编译器可能会警告它们。

您可以考虑将pointsstruct vector2 * const 更改为struct vector2 ** const。然后,您可以交换可通过*points 访问的(非conststruct vector * 对象:

struct vector *temp = poly->points[3];
poly->points[3] = poly->points[2];
poly->points[2] = temp;

您对不变性的关注让我怀疑您是否具有 Java 背景;无论哪种方式,这都更像 Java,因为 Java 中的所有非基元都是引用,它们或多或少是指针。

不过,总的来说,我认为您过于关注不变性。 const-ness 会给你带来麻烦,尤其是在内存管理方面。考虑不这样做,至少对于您的结构成员本身。最多在函数参数类型上使用const 限定来表示函数不会修改实际参数,并且可能在全局变量上至少要警告它们被修改的任何可能性。

【讨论】:

  • 非常详尽的回答,谢谢。我实际上不喜欢Java。我对不变性的偏好来自功能背景。纯函数和不可变对象不仅美观,而且更易于推理,imo。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-02
  • 2019-04-15
  • 2012-03-02
  • 2021-07-12
  • 1970-01-01
  • 2017-06-11
  • 2020-05-06
相关资源
最近更新 更多