常量表达式

来自cppreference.com
< c‎ | language


表达式的数种变体被称为常量表达式

预处理器常量表达式

#if#elif 之后的表达式必须展开成

字符常量在 #if 表达式中求值时,可能以源字符集、执行字符集或某个其他实现定义字符集转译。

#if 表达式中,对有符号类型以 intmax_t 的语义,而对无符号类型以 uintmax_t 的语义进行整数算术。

(C99 起)

整数常量表达式

整数常量表达式是仅由下列内容组成的表达式

(C11 起)
  • 具名的以及复合字面量常量,其为整数类型,或为算术类型且是转型的直接操作数
(C23 起)

整数常量表达式在编译时求值。下列语境要求被称为整数常量表达式的表达式:

(C99 起)
(C11 起)
  • 位精确整数类型(_BitInt(N))的位数 N
(C23 起)

静态初始化式

拥有静态和线程局域存储期或者以存储类说明符 constexpr 所声明 (C23 起)的对象的初始化式中使用的表达式,必须是下列表达式之一

1) 算术常量表达式,即由下列内容构成的任何算术类型表达式
(C11 起)
  • 算术类型的具名的以及复合字面量常量
(C23 起)
2) 空指针常量(比如 NULL
3) 地址常量表达式,即
  • 空指针
  • 指代静态存储期的对象的左值,或函数指代器,通过下列方式之一转换为指针
  • 用一元取址运算符
  • 转型整数常量到指针
  • 数组到指针或函数到指针隐式转换
4) 某完整对象类型的地址常量表达式加或减一个整数常量表达式
5) 具名常量,为以下标识符
  • 枚举常量
  • 预定义常量(truefalsenullptr
  • 以存储类说明符 constexpr 和对象类型声明的变量
或者(递归地)在结构体或联合体类型的具名常量上应用成员访问运算符 . 的后缀表达式。
6) 复合字面量常量,为
  • 带有存储类说明符 constexpr复合字面量
  • (递归地)在结构体或联合体类型的复合字面量常量上应用成员访问运算符 . 的后缀表达式。

结构体或联合体常量分别是具有结构体或联合体类型的具名常量或复合字面量常量。如果成员访问运算符 . 访问的是联合体常量的成员,则所访问的成员应当与该联合体常量的初始化式所初始化的成员相同。

(C23 起)
7) 实现所接受的其他形式之一的常量表达式。

不同于整数常量表达式,不要求静态初始化器在编译时求值;编译器有将这种初始化器转变为在程序启动前调用的可执行代码的自由。

static int i = 2 || 1 / 0; // 初始化 i 为值 1

浮点静态初始化器的值的精度决不低于在运行时执行的同一表达式,但可以高于后者。

浮点常量表达式

不用于静态初始化器中的浮点类型的算术常量表达式,始终如同在运行时求值,受当前舍入影响(若 Template:c/preprocessor/impl 为 ON),并报告 math_errhandling 中指定的错误。

void f(void)
{
#pragma STDC FENV_ACCESS ON
    static float x = 0.0/0.0; // 静态初始化器:不引发异常
    float w[] = { 0.0/0.0 }; // 引发异常
    float y = 0.0/0.0; // 引发异常
    double z = 0.0/0.0; // 引发异常
}

注解

若表达式求值到的值不能以其类型表示,则不能以之为常量表达式。

实现可以接受其他形式的常量表达式。然而,这些常量表达式不被认为是整数常量表达式、算术常量表达式或地址常量表达式,从而不能用于要求这些种类的常量表达式的语境。例如 int arr[(int)+1.0]; 声明一个 VLA。

引用

  • C23 标准(ISO/IEC 9899:2024):
  • 6.6 Constant expressions (第 TBD 页)
  • C17 标准(ISO/IEC 9899:2018):
  • 6.6 Constant expressions (第 76-77 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 6.6 Constant expressions (第 106-107 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 6.6 Constant expressions (第 95-96 页)
  • C89/C90 标准(ISO/IEC 9899:1990):
  • 3.4 CONSTANT EXPRESSIONS

参阅