【问题标题】:What are the differences between constant expressions and nonmodifiable lvalues?常量表达式和不可修改的左值有什么区别?
【发布时间】:2026-02-17 17:55:01
【问题描述】:

简而言之,来自 C:

常量表达式

编译器识别源代码中的常量表达式 并用它们的值替换它们。得到的常数值 必须在表达式的类型中是可表示的。您可以使用常量 允许使用简单常量的表达式。

运算符 常量表达式遵循与其他表达式相同的规则 表达式。因为常量表达式是在翻译时计算的 但是,它们不能包含函数调用或操作 修改变量,例如赋值。

  1. 什么是常量表达式?它没有定义常量表达式

  2. 常量表达式和不可修改的左值有什么区别(例如数组名​​,用const声明的左值)

  3. 常量表达式总是非左值吗?

  4. 不可修改的左值是常量表达式吗?

【问题讨论】:

  • 有什么帮助吗? *.com/q/3755524/2173917
  • 我认为很明显,重要的区别在于运行时间与翻译时间。
  • 在这种情况下,将常量表达式视为文字值或对文字值的操作。不可修改的左值是内存中的一个变量(这是查看左值的一种方式),它被定义为const
  • @MichaëlRoy:“定义为const”不一定,想想一个数组。
  • 我注意到您一直在询问有关 C 的不同书籍定义的问题。我不知道您的意图是什么。但是您经常遇到的问题是引用 C 标准。当您有这样的问题时,为什么不直接拿一份并尝试阅读它呢?而且你几乎从不接受答案。

标签: c


【解决方案1】:
  1. 常量表达式定义在C11 6.6:

    说明

    2。常量表达式可以在翻译期间而不是运行时进行评估,因此可以在常量可能存在的任何地方使用。

    约束

    3 常量表达式不得包含赋值、递增、递减、函数调用或 comma operators,除非它们包含在未计算的子表达式中。115)

    4 每个常量表达式的计算结果都应在其类型的可表示值范围内。

    在一个地方可能允许使用几种类型的常量表达式。有一些常量表达式的计算结果为

    • 算术常数表达式,
      • 这种情况的一个特殊情况是整数常量表达式,它可以作为(静态分配的)数组的大小或位域宽度。
    • 一个空指针常量,
    • 地址常量,或
    • 一个完整对象类型的地址常量加上或减去一个整数常量表达式。
  2. 左值或定位器值(可能)指定对象。您可以获取左值的地址。可修改和不可修改的左值之间的区别在于后者不能用作赋值的左侧。或者,C11 6.3.2.1p1

    可修改的左值是没有数组类型、没有不完整类型、没有 const 限定类型,并且如果它是结构或联合,则没有任何成员(包括递归,所有包含的聚合或联合的任何成员或元素)具有 const 限定类型。

    不可修改的左值是不可修改的左值。

    常量表达式只是……值。它们不驻留在内存中 - 没有数字 42 的地址 - &42 毫无意义。

  3. 因此,常量表达式总是非左值。它们不指定对象。不能取6 * 9的地址。

  4. 不,但是...数组类型的左值隐式衰减为指向数组的指针,而这些地址常量。然而,当它们因此被转换时,它们不再是左值。

【讨论】:

  • 谢谢。 “常量表达式只是......值。它们不驻留在内存中”。甚至不是内存的只读部分?那么常量表达式在哪里呢?
  • @Tim 数字 6 可以写在纸上(那么它就是一个对象),但是 6 的 idea 没有位置。好吧,常量表达式驻留在编译器的内存中;但是,如果您考虑位域中的常量表达式或数组的大小 - 数字本身可能根本不会直接存储在可执行文件中。
【解决方案2】:

什么是常量表达式?

§6.6- 常量表达式:

常量表达式可以在翻译期间而不是运行时进行评估,因此可以在常量可能存在的任何地方使用。

对常量表达式的约束之一是

常量表达式不应包含赋值、递增、递减、函数调用或逗号运算符,除非它们包含在未计算的子表达式中115)


常量表达式和不可修改的左值有什么区别?

不可修改的左值不是常量表达式。 不可修改的值是不能用于修改对象的左值

int const i = 5;

i 指的是一个 const 对象,但它是一个左值,一种特殊的左值,称为不可修改的左值。

标准中提到“左值”的地方,实际上是指“可修改的左值”(您不会在标准中找到这个,但为了清楚起见)

现在让我再解释一下。

int x = 5;
int const *p;

x 是一个非常量对象,它是一个可修改的左值。 p 是一个指向 const int 对象的指针。

p = &x;  // This will raise a warning though and hazardous to do.

以上赋值使用限定转换将指向int的指针类型的值转换为指向const int的指针类型的值。
@987654330 @ 和x 是指同一个对象的两个不同的表达式。可以使用 x 修改此 abject

--x;  

但这不能使用*p 来完成,因为它是不可修改的左值。

--(*p); // Error

常量表达式和不可修改的左值之间的另一个区别是

int const x = 5;
int *p;
/* Assigning address of a non-modifiable object */
p = &x      // Correct
/* Assigning address of a constant expression */
p = &5      // Wrong  

常量表达式总是非左值吗?

是的,常量表达式总是非左值,即右值。

不可修改的左值是常量表达式吗?

【讨论】:

  • 任何常量表达式不是nonmodifiable lvalue的例子?
  • @PeterJ;常量表达式永远不能是左值。不可修改的左值是一个左值,但它不能用于修改它所代表的对象。表达式5 是一个常量表达式,它是一个右值,而不是一个左值(不可修改的左值)。
  • 谢谢。 “常量表达式可以在翻译期间而不是运行时进行评估,因此可以在常量可能存在的任何地方使用。” (1) 如何判断表达式是否可以在翻译期间或仅在运行时进行评估? (2)“常数”指的是什么?与“常量表达式”有什么不同?
  • “常量表达式不应包含赋值、递增、递减、函数调用或逗号运算符”。为什么常量表达式不能包含其中的任何一个?他们的表达方式不是“恒定的”吗?
  • @Tim:常量表达式表示“仅值计算”。如果在某处分配了一个值,一些增加或减少(++--),那么那里的东西是 variable 的,因此整个表达式不是恒定的......另外,恒定意味着“编译-时间”。函数调用就像在运行时发生一样(尽管有时编译器在编译时解决这些问题,但从来不需要内联最简单的函数调用)。
【解决方案3】:

我认为有不止一个复杂的问题,让我尝试以一种简单的方式回答它(​​主要是通过公然从标准复制粘贴,只是在我自己的)。

1.什么是常量表达式?

C11,第 §6.5 章,Expressions

表达式是一系列运算符和操作数,指定计算一个 值,或指定对象或函数,或产生副作用,或 执行它们的组合。

第 6.6 章,Constant expressions

常量表达式可以在翻译期间而不是运行时计算,并且 因此可以在常量可能存在的任何地方使用。

还有,P4

每个常量表达式都应计算为可表示范围内的常量 其类型的值。

对于Lvalues,第 6.3.2 章,

左值是一个表达式(对象类型不是 void)可能 指定一个对象

对于对象,§3.15,

对象

执行环境中的数据存储区域,其内容可以表示 价值观

一些常量表达式,例如数组(数组名)、字符串字面量或地址常量,是左值。否则,对于整个不可修改的左值范围,由常量对象(或对象引用)和常量表达式组成。

【讨论】:

  • ...和数组。
  • 地址是常量&5左值吗?
  • @hacks umm..不确定我是否遗漏了什么,但&5 的地址如何保持不变? An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator;...所以,这个例子不适用于这种情况,对吧,先生?
【解决方案4】:

常量表达式是在编译过程中值已知(可以计算)的表达式

例子

int a = 5+5+3;  //5 + 5 +3 
char []= "Hello"; // string literal "hello"

不可修改的左值是常量对象和常量表达式

例子

const char c;
c = '3';

'3' = c;

可修改的左值必须是可寻址的(即,您可以使用 & 获取它们的地址)和可赋值,即它们可以位于赋值运算符的左侧。唯一的例外是没有地址的寄存器变量。

【讨论】:

  • ...和数组。
  • 我没有包含数组,因为没有语言结构允许这样的分配。在 C99 之前,结构也是如此。
  • 不可修改的左值是常量对象和常量表达式:不,它们不是。