【问题标题】:Is it true that if the address of an uninitialized variable is taken, it is initialized to default value?如果取了一个未初始化变量的地址,是否会被初始化为默认值?
【发布时间】:2020-01-18 15:43:03
【问题描述】:
我有这个代码:
using System;
class Program
{
unsafe static void f(void* v)
{}
static void Main()
{
int a;
unsafe
{
f(&a);
}
Console.Write(a);
Console.ReadKey();
}
}
代码没有错误或异常。代码的输出是0,而a 没有明确给出任何初始值。代码是否正确,编译器是否将 a 隐式初始化为默认值?
【问题讨论】:
标签:
c#
pointers
variables
initialization
language-lawyer
【解决方案1】:
在不安全的代码中,事情可能会变得奇怪。
首先,让我们看一下 C# 语言规范的Variables 部分。
一个变量必须明确赋值 <...>才能获得它的值。
<...> 变量是初始分配或初始未分配。 <...> 一个最初未赋值的变量没有初始值。对于一个最初未分配的变量被认为是在某个位置确定分配的,对该变量的分配必须发生在通向该位置的每个可能的执行路径中。
对于“安全”的 C# 代码也是如此。
在不安全的代码规范中,有一个address-of operator的描述,它告诉我们:
& 运算符不要求其参数被明确赋值,但在& 操作之后,该运算符所应用的变量被认为在操作发生的执行路径中已明确分配。程序员有责任确保在这种情况下确实正确地初始化了变量。
这正是你的情况。您将 address-of 运算符应用于 initially unassigned 变量,然后编译器将其视为 明确分配 变量 - 因此即使在“安全”上下文。
规范告诉我们,程序员负责变量的初始化。如果没有初始化,规范不保证实际变量中会存储什么值。
当前的 C# 编译器为此生成这样的 IL 代码:
.locals init (
[0] int32 a
)
IL_0000: ldloca.s 0
IL_0002: conv.u
IL_0003: call void Namespace.Program::f(void*)
你看,有一个值类型为int32 的局部变量。
当前的 CLR (.NET Framework) 使用零自动初始化所有局部变量 - 这就是为什么变量的初始值为 0,即使它没有在代码中初始化。
我不会依赖这个事实 - AFAIK 无法保证,并且可能会在其他实现中发生变化。