c++ 使用已声明和已初始化但未定义的变量是否为undefined行为?

p8h8hvxi  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(169)

在C++中,你可以在c.h中声明一个类C,它包含static const int var = 3;的声明和初始化。

class C {
public:
...
    static const int var = 3
...
};

字符串
除非您将该变量设置为inline,或者在c.cpp中显式定义它,

...
const int C::var;
...


它是未定义的。

  • 那么,即使在不与内存地址相关的事情中使用var,就像for循环中的计数器一样,也是未定义的行为吗?
  • 编译器如何使用未定义变量的值?预处理器宏是否替换了它的值?
  • 除了在类声明中使用静态int变量的情况之外,还有其他情况可以声明和初始化但未定义的变量吗?
  • 这在C编程语言中会发生吗?

我知道一定有某种机制来实现这一点,因为我见过使用未定义的静态常量变量的C++代码。但我希望引用标准。

yqhsw0fo

yqhsw0fo1#

根据[class.static.data]/4第1句,仅当var具有const限定的整数或枚举类型并由 * 常量表达式 * 初始化时,才允许将此静态数据成员结构作为一般规则的例外。
根据[basic.def.odr]/10(由[class.static.data]/4第2句重申),需要var的定义,如果var是 * odr-使用 。否则,程序就是IFNDR(格式错误,不需要诊断)。
一般来说,如果一个变量是在一个 * discovered statement
之外的 potentially-evaluated expression 中命名的,那么它总是被odr使用的。
但是,因为varconst整型或枚举类型,并且用常量表达式初始化,所以它 * 可用于常量表达式 *(并且不能有可变的子对象),因此即使var是在可能求值的表达式中命名的,[basic.def.odr]/4.2中的异常也可能适用。如果在表达式中对var所做的只是立即读取其值(lvalue-to-rvalue conversion),而不是例如:形成对其的指针/引用。因为变量是用常量表达式初始化的,所以编译器可以用给定给var的编译时常量值替换变量的使用。
正如@LanguageLawyer在这个答案下的评论中指出的那样,该标准目前在[intro.object]/1中说 definitions 可以创建对象,但不是说 declarations 通常可以。这是一个问题,因为即使没有var的定义,你没有违反ODR,你仍然不会有一个对象,但是左值到右值的转换是根据阅读 object 的值来指定的(或者当ODR异常应用时,根本不依赖于解释)。如果没有定义的行为,程序在计算表达式时仍然会有未定义的行为。然而,这显然是无意的,并且是标准中的缺陷。
在[basic.def.odr]/4.3中还有一个小的例外,当变量被直接命名为 discarded-value expression 时,例如当将var;作为语句写入时。在这种情况下,编译器既不需要var的值也不需要var的地址。
这些都与预处理器无关,它在解析实际的C++代码之前专门工作。
这是我认为唯一的例外,你可以有一个初始化声明的变量,而不是在同一时间的变量的定义。它的存在只是因为历史原因,在constexprinline可以用于变量以使静态数据成员的值在常量表达式中可用之前。

guicsvcw

guicsvcw2#

这在C语言中会发生吗?*
在C语言中,如果在头文件中定义了变量static int var = 3;,那么包含该头文件的每个C源文件都会有自己的本地副本(如果定义了var)。没有 * 声明和初始化,但未定义变量 *。
请注意,如果编译器被配置为生成有用的警告,则会报告未使用的static变量,因此您应该会收到多个警告,除非您在每个源文件中都使用var
全局const定义在C和C++中有细微的不同语义,但这是一个不同的问题。

相关问题