考虑一下:
void* x = &x;
printf("%p\n", x);
令人惊讶的是,它编译并运行并输出:
7ffb2f7248
当
x
尚未创建时,x
怎么可能获取自己的地址?
C++ 处理这段代码的方式有什么不同吗?
C++ 2018 6.3.2 [basic.scope.pdecl] 1 说:
名称的声明点紧随其名称之后 完整的声明符(第 11 条)和其 initializer 之前(如果有), 除下文所述外。
“下面提到的”项目在这里不适用,因此,在
void* x = &x;
中,x
在第一个 x
(即 声明符)之后和 = &x
(初始化器)之前声明,因此 x
可以在初始化器中引用,本质上就好像代码是 void *x; x = &x;
。
C 2018 6.2.1 7 说:
…任何其他标识符的作用域都在其声明符完成后开始。
所以这与 C++ 本质上是一样的。 (“任何其他标识符”之前讨论的标识符类型是结构体、联合、枚举标记和枚举常量,其范围在标记或枚举常量之后开始。)
此答案适用于 C 标签。
表达方式:
void* x = &x;
是一个 init-declarator declarator = 初始化器
这可以在 N1570(C 标准草案)第 6.7 节中找到,6.7.6 说:
完整声明符是不属于另一个声明符一部分的声明符。 完整的结束 声明符是一个序列点。
6.2.1/7 说:
结构、联合和枚举标签的作用域从声明标签的类型说明符中标签出现之后开始。 ...
... 任何其他标识符的作用域都在其声明符完成后开始。
这些部分告诉我们对象是在初始化之前创建的。因此,初始化器可以使用创建的对象的地址。
第 6.3.2.3 节中的指针:
指向 void 的指针可以与因此 - 由于任何指针都可以转换为 void 指针 -指向任何对象类型的指针相互转换。 指向任何对象类型的指针都可以转换为指向 void 的指针,然后再转换回来;结果应等于原始指针。
x
的地址可以分配给 void 指针。所以对于你的代码:
首先创建对象,然后初始化。
void* x = &x;
^^^^^^^ ^^^^^
| |
| step 2: initialize object
|
step 1: create object, i.e. end of full declarator
对于此代码,它与作业几乎相同。
void* x; // Create
x = &x; // Assignment instead of initialization
// But in this case it works just the same
为
x
分配存储只是因为它被此声明定义为存在。 初始化是一个单独的步骤,遵循存储分配。 因此,
x
的地址可以在其自身初始化时得知。(请注意,在初始化指向的值之前,无法安全地取消引用此类指针,但没有理由不能在初始化之前获取该地址。)
当x还没有创建的时候,x怎么可能获取它自己的地址呢?因为对象不需要存在就能知道它将在哪里创建。