【问题标题】:What is the use of the const keyword when assigning structure values?分配结构值时 const 关键字有什么用?
【发布时间】:2021-10-12 09:31:07
【问题描述】:

这个程序中const关键字有什么用?

#include <stdio.h>

struct x{
    int a;
    int b;
};

int main()
{
    struct x xx;
    xx = (const struct x) {0};
    xx.a = 10;
    printf("%d, %d", xx.a, xx.b);
}

在没有const 关键字的情况下运行时输出没有差异。它在这里有什么意义?

【问题讨论】:

  • const 在这里没有任何意义。这就像写(const int)42 一样没用。
  • 虽然编译、链接和运行这样一个小实验是一个好的开始,但它不会给出最终答案。你需要参考标准。你在那里发现了什么?
  • "不使用 const 关键字运行时输出没有差异。"正确的。这就是(编译时)类型限定符的全部意义:如果它编译,那么应该没有运行时差异(除了潜在的 UB 和编译器优化)。
  • 一些非常糟糕的嵌入式系统编译器可能会决定将复合文字放在 RAM 中而不是只读闪存中。这就是在此处键入 const 的唯一正当理由 - 例如,假设复合文字被放置在 .bss 中并从那里复制到堆栈变量中。
  • 更可能的是,一些困惑的人喜欢到处乱发垃圾邮件const,比如声明本地函数参数const,即使它们是按值复制的。

标签: c constants structure


【解决方案1】:

C 2018 6.3.2.1 2 表示,当任何对象在表达式中用作其值时,将删除诸如 const 之类的限定符:

除非它是sizeof 运算符、一元&amp; 运算符、++ 运算符、-- 运算符或. 运算符或赋值运算符的左操作数,没有数组类型的左值被转换为存储在指定对象中的值(并且不再是左值);这称为左值转换如果左值具有限定类型,则该值具有左值类型的非限定版本;此外,如果左值具有原子类型,则该值具有左值类型的非原子版本;否则,该值具有左值的类型。

因此,在xx = (const struct x) {0}; 中,(const struct x) {0}; 是创建const struct x 对象的复合文字。但是,在取这个对象的值时,const 被删除,结果是一个struct x 值。 (结构的“值”是其所有成员的值的集合。)

因此,在此使用中,const 最终没有任何效果。但是,复合文字确实会创建一个 const 对象。在其他用途​​中可以看到这种效果。例如,const struct x *p = &amp; (const struct x) {0};,我们获取复合结构的地址。然后,如果我们尝试删除const 并修改结构,就像((struct x *) p)-&gt;a = 3; 一样,C 标准没有定义行为。编译器可能已将该结构放置在只读内存中,并且尝试修改它可能会产生一个陷阱。或者,在优化过程中,编译器可以使用结构为const 的事实来忽略它可能更改的任何可能性。例如,在:

const struct *p = & (const struct x) {0};
((struct x *) p)->a = 3;
printf("%d\n", p->a);

程序可能会打印“0”,因为在printf 中的p-&gt;a 中,编译器使用了p-&gt;a 被定义为const 零这一事实。

【讨论】:

    【解决方案2】:

    但是这里有什么意义呢?

    无。

    当您像 xx = something; 一样分配给 xx 时,您需要 something 具有基本类型 struct x。这是由于演员阵容。所以如果你这样做了

    (struct x) {0};
    

    将创建一个struct x 类型的匿名对象,并将其分配给xx,例如:

    xx = (struct x) {0};
    

    现在在强制转换中添加一个 const 限定符意味着匿名对象是一个常量。但是,它是否恒定并不重要,因为您无论如何都不写它。对匿名对象执行的唯一操作是在分配给xx 期间执行的读取操作。对于赋值,源对象(RHS 对象)是否为常量没有区别。

    所以:不,因为您的代码 const 没有意义。

    我们可以在重要的地方构建代码吗?

    是的,考虑一下这个(混淆的无用的)代码:

    int y;
    y = ++(struct x) {0, 42}.b;
    printf("%d\n", y);
    

    它将创建一个具有值.a=0.b=42 的匿名对象,然后递增并返回.b。换句话说,它将打印 43。

    如果您将 const 添加到演员表中,您将收到如下编译器错误:

    main.cpp:26:9: error: increment of member 'b' in read-only object
       26 |     y = ++(const struct x) {0, 42}.b;
          |         ^~
    

    所以这里const 关键字确实很重要。

    【讨论】:

    • 这不是演员表,而是复合文字。除了作为另一个结构/联合的未命名成员之外,C 没有匿名结构。
    【解决方案3】:

    这将零初始化一个临时的struct x

    xx = (const struct x) {0};
    

    然后该临时文件将用于复制分配xx。无论临时对象是否为const,它都会这样做。


    一个流行的编译器 (gcc) 的例子表明,它会简单地将 xx 中的两个成员清零,在 const 和非const 的情况下,而不实际创建临时对象。编译后的程序的行为就像一样:

    xor     edx, edx
    xor     esi, esi
    

    【讨论】:

      猜你喜欢
      • 2011-07-30
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多