LIVE

导航

C# Language Specification 1.2 之五 变量

1. 变量

变量表示存储位置。每个变量都具有一个类型它确定哪些值可以存储在该变量中。C#  是一种类型安全的语言,C#  编译器保证存储在变量中的值总是具有合适的类型。通过赋值或使用 ++ -- 运算符可以更改变量的值。

在可以获取变量的值之前变量必须已明确赋值 (definitely assigned) 5.3

如下面的章节所述变量是初始已赋值 (initially assigned) 或初始未赋值 (initially unassigned)。初始已赋值的变量有一个正确定义了的初始值并且总是被视为已明确赋值。初始未赋值的变量没有初始值。为了使初始未赋值的变量在某个位置被视为已明确赋值,变量赋值必须发生在通向该位置的每个可能的执行路径中。

1.1 变量类别

C#  定义了 7 种变量类别静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量。后面的章节将介绍其中的每一种类别。

在下面的示例中

class A
{
public static int x;
int y;

void F(int[] v, int a, ref int b, out int c) {
     int i = 1;
     c = a + b++;
}
}

x 是静态变量y 是实例变量v[0] 是数组元素a 是值参数b 是引用参数c 是输出参数i 是局部变量。

1.1.1 静态变量

static 修饰符声明的字段称为静态变量 (static variable)。静态变量在包含了它的那个类型的静态构造函数(第 10.11 节)执行之前就存在了,在关联的应用程序域终止时终止。

静态变量的初始值是该变量的类型的默认值 5.2

出于明确赋值检查的目的静态变量被视为初始已赋值。

1.1.2 实例变量

未用 static 修饰符声明的字段称为实例变量 (instance variable)

1.1.2.1 类中的实例变量

类的实例变量在创建该类的新实例时开始存在在所有对该实例的引用都已终止并且已执行了该实例的析构函数若有时终止。

类实例变量的初始值是该变量的类型的默认值 5.2

出于明确赋值检查的目的类的实例变量被视为初始已赋值。

1.1.2.2 结构中的实例变量

结构的实例变量与它所属的结构变量具有完全相同的生存期。换言之,当结构类型的变量开始存在或停止存在时,该结构的实例变量也随之存在或消失。

结构的实例变量与包含它的结构变量具有相同的初始赋值状态。换言之,当结构变量本身被视为初始已赋值时,它的实例变量也被视为初始已赋值。而当结构变量被视为初始未赋值时,它的实例变量同样被视为未赋值。

1.1.3 数组元素

数组的元素在创建数组实例时开始存在在没有对该数组实例的引用时停止存在。

每个数组元素的初始值都是其数组元素类型的默认值 5.2

出于明确赋值检查的目的数组元素被视为初始已赋值。

1.1.4 值参数

未用 ref out 修饰符声明的参数为值参数 (value parameter)

值形参在调用该参数所属的函数成员方法、实例构造函数、访问器或运算符时开始存在并用调用中给定的实参的值初始化。当返回该函数成员时值参数停止存在。

出于明确赋值检查的目的值参数被视为初始已赋值。

1.1.5 引用参数

ref 修饰符声明的参数是引用参数 (reference parameter)

引用参数不创建新的存储位置。相反引用参数表示的是那个在对该函数成员调用中被当作自变量的变量所表示的同一个存储位置。因此,引用参数的值总是与基础变量相同。

下面的明确赋值规则适用于引用参数。注意第 5.1.6 节中描述的输出参数的不同规则。

·         变量在可以作为引用参数在函数成员调用中传递之前必须已明确赋值 5.3

·         在函数成员内部引用参数被视为初始已赋值。

在结构类型的实例方法或实例访问器内部this 关键字的行为与该结构类型的引用参数完全相同 7.5.7

1.1.6 输出参数

out 修饰符声明的参数是输出参数 (output parameter)

输出参数不创建新的存储位置。相反输出参数表示的是那个在对该函数成员调用中被当作自变量的变量所表示的同一个存储位置。因此,输出参数的值总是与基础变量相同。

下面的明确赋值规则应用于输出参数。注意第 5.1.5 节中描述的引用参数的不同规则。

·         变量在可以作为输出参数在函数成员调用中传递之前不一定要明确赋值。

·         在正常完成函数成员调用之后每个作为输出参数传递的变量都被认为在该执行路径中已赋值。

·         在函数成员内部输出参数被视为初始未赋值。

·         函数成员的每个输出参数在该函数成员正常返回前都必须已明确赋值 5.3

在结构类型的实例构造函数内部this 关键字的行为与结构类型的输出参数完全相同 7.5.7

1.1.7 局部变量

局部变量 (local variable) 是通过 local-variable-declaration 来声明的此声明可以出现在 blockfor-statementswitch-statement using-statement 中。

局部变量的生存期是程序执行过程中的某一”,在此期间一定会为该局部变量保留存储。此生存期从进入与它关联的 blockfor-statementswitch-statementusing-statement 开始,一直延续到对应的 blockfor-statementswitch-statementusing-statement 的执行以任何方式结束为止。(进入封闭 block 或调用方法会挂起(但不会结束)当前的 blockfor-statementswitch-statementusing-statement 的执行。)如果以递归方式进入父 blockfor-statementswitch-statementusing-statement,则每次都创建局部变量的新实例,并且重新计算它的 local-variable-initializer(如果有)。

局部变量不自动初始化因此没有默认值。出于明确赋值检查的目的,局部变量被视为初始未赋值。local-variable-declaration 可包括 local-variable-initializer,在此情况下变量被视为在它的整个范围内(local-variable-initializer 中提供的表达式内除外)已明确赋值。

在局部变量的范围内 local-variable-declarator 之前的文本位置引用该局部变量是编译时错误。

局部变量的实际生存期依赖于具体实现。例如,编译器可能静态地确定块中的某个局部变量只用于该块的一小部分。使用这种分析,编译器生成的代码可能会使该变量存储的生存期短于包含该变量的块的生存期。

局部引用变量所引用的存储的回收与该局部引用变量 3.9 的生存期无关。

foreach-statement try-statement specific-catch-clause 也声明局部变量。对于 foreach-statement局部变量是一个迭代变量 8.8.4 。对于 specific-catch-clause局部变量是一个异常变量 8.10 foreach-statementspecific-catch-clause 所声明的局部变量被视为在它的整个范围内已明确赋值。

1.2 默认值

以下类别的变量自动初始化为它们的默认值

·         静态变量。

·         类实例的实例变量。

·         数组元素。

变量的默认值取决于该变量的类型并按下面这样确定

·         对于 value-type 的变量默认值与该 value-type 的默认构造函数 0 所计算的值相同。

·         对于 reference-type 的变量默认值为 null

初始化为默认值的实现方法一般是让内存管理器或垃圾回收器在分配内存以供使用之前将内存初始化为所有位归零(all-bits-zero)。由于这个原因,使用所有位归零来表示空 (null) 引用很方便。

1.3 明确赋值

在函数成员可执行代码中的给定位置如果编译器可通过特定的静态流程分析 5.3.3 证明变量已自动初始化或已成为至少一个赋值的目标则称该变量已明确赋值 (definitely assigned)。非正式地讲明确赋值的规则为

·         初始已赋值的变量 5.3.1 总是被视为已明确赋值。

·         如果所有可能通向给定位置的执行路径都至少包含以下内容之一则初始未赋值的变量
5.3.2 被视为在该位置已明确赋值

o        将变量作为左操作数的简单赋值(第 7.13.1 节)。

o        将变量作为输出参数传递的调用表达式(第 7.5.5 节)或对象创建表达式(第 7.5.10.1 节)。

o        对于局部变量,包含变量初始值设定项的局部变量声明(第 8.5 节)。

以上非正式规则所基于的正式规范在第 5.3.1 节、第 5.3.2 节和第 5.3.3 节中说明。

关于对一个 struct-type 变量的实例变量是否明确赋值既可个别地也可作为整体进行跟踪。除了上述规则,下面的规则也应用于 struct-type 变量及其实例变量:

·         如果一个实例变量的包含它的那个 struct-type 变量被视为已明确赋值则该实例变量被视为已明确赋值。

·         如果一个 struct-type 变量的每个实例变量都被视为已明确赋值则该结构类型变量被视为已明确赋值。

在下列上下文中要求实施明确赋值

·         变量必须在获取其值的每个位置都已明确赋值。这确保了从来不会出现未定义的值。变量在表达式中出现被视为要获取该变量的值,除非当

o        该变量为简单赋值的左操作数,

o        该变量作为输出参数传递,或者

o        该变量为 struct-type 变量并作为成员访问的左操作数出现。

·         变量必须在它作为引用参数传递的每个位置都已明确赋值。这确保了被调用的函数成员可以将引用参数视为初始已赋值。

·         函数成员的所有输出参数必须在函数成员返回的每个位置都已明确赋值返回位置包括通过 return 语句实现的返回或者通过执行语句到达函数成员体结尾的返回。这确保了函数成员不在输出参数中返回未定义的值,从而使编译器能够把一个对函数成员的调用当作对某些变量的赋值,这些变量在该调用中被当作输出参数传递。

·         struct-type 实例构造函数的 this 变量必须在该实例构造函数返回的每个位置明确赋值。

1.3.1 初始已赋值变量

以下类别的变量属于初始已赋值变量

·         静态变量。

·         类实例的实例变量。

·         初始已赋值结构变量的实例变量。

·         数组元素。

·         值参数。

·         引用参数。

·         catch 子句或 foreach 语句中声明的变量。

1.3.2 初始未赋值变量

以下类别的变量属于初始未赋值变量

·         初始未赋值结构变量的实例变量。

·         输出参数包括结构实例构造函数的 this 变量。

·         局部变量 catch 子句或 foreach 语句中声明的那些除外。

1.3.3 确定明确赋值的细则

为了确定每个已使用变量都已明确赋值编译器必须使用与本节中描述的进程等效的进程。

编译器处理每个具有一个或多个初始未赋值变量的函数成员的体。对于每个初始未赋值的变量 v,编译器在函数成员中的下列每个点上确定 v 的明确赋值状态 (definite assignment state)

·         在每个语句的开头处

·         在每个语句的结束点 8.1

·         在每个将控制转移到另一个语句或语句结束点的 arc

·         在每个表达式的开头处

·         在每个表达式的结尾处

v 的明确赋值状态可以是

·         明确赋值。这表明在能达到该点的所有可能的控制流上v 都已赋值。

·         未明确赋值。当在 bool 类型表达式结尾处确定变量的状态时未明确赋值的变量的状态可能但不一定属于下列子状态

o        true 表达式后明确赋值。此状态表明如果该布尔表达式计算为 true v 是明确赋值的但如果布尔表达式计算为 false则不一定要赋值。

o        false 表达式后明确赋值。此状态表明如果该布尔表达式计算为 false v 是明确赋值的但如果布尔表达式计算为 true则不一定要赋值。

下列规则控制变量 v 的状态在每个位置是如何确定的。

1.3.3.1 一般语句规则

·         v 在函数成员体的开头处不是明确赋值的。

·         v 在任何无法访问的语句的开头处都是明确赋值的。

·         在任何其他语句开头处为了确定 v 的明确赋值状态请检查以该语句开头处为目标的所有控制流转移上的 v 的明确赋值状态。当且仅当 v 在所有此类控制流转移上是明确赋值的时,v才在该语句的开始处明确赋值。确定可能的控制流转移集的方法与检查语句可访问性的方法(第 8.1 节)相同。

·         blockcheckeduncheckedifwhiledoforforeachlockusing switch 等语句的结束点处为了确定 v 的明确赋值状态需检查以该语句结束点为目标的所有控制流转移上的 v 的明确赋值状态。如果 v 在所有此类控制流转移上是明确赋值的 v 在该语句结束点明确赋值。否则,v 在语句结束点处不是明确赋值的。确定可能的控制流转移集的方法与检查语句可访问性的方法(第 8.1 节)相同。

1.3.3.2 块语句、checked unchecked 语句

在指向位于某块中语句列表的第一个语句如果语句列表为空则指向该块的结束点的控制转移上v 的明确赋值状态与块语句、checked unchecked 语句之前的 v 的明确赋值状态相同。

1.3.3.3 表达式语句

对于由表达式 expr 组成的表达式语句 stmt

·         v expr 的开头处与在 stmt 的开头处具有相同的明确赋值状态。

·         如果 v expr 的结尾处明确赋值则它在 stmt 的结束点也明确赋值否则它在 stmt 的结束点也不明确赋值。

1.3.3.4 声明语句

·         如果 stmt 是不带有初始值设定项的声明语句 v stmt 的结束点与在 stmt 的开头处具有相同的明确赋值状态。

·         如果 stmt 是带有初始值设定项的声明语句则确定 v 的明确赋值状态时可把 stmt 当作一个语句列表其中每个带有初始值设定项的声明对应一个赋值语句按声明的顺序

1.3.3.5 if 语句

对于具有以下形式的 if 语句 stmt

if ( expr ) then-stmt else else-stmt

·         v expr 的开头处与在 stmt 的开头处具有相同的明确赋值状态。

·         如果 v expr 的结尾处明确赋值则它在指向 then-stmt else-stmt 或指向 stmt 的结束点如果没有 else 子句的控制流转移上是明确赋值的。

·         如果 v expr 的结尾处具有 true 表达式后明确赋值状态则它在指向 then-stmt 的控制流转移上是明确赋值的在指向 else-stmt 或指向 stmt 的结束点如果没有 else 子句的控制流转移上不是明确赋值的。

·         如果 v expr 的结尾处具有 false 表达式后明确赋值状态则它在指向 else-stmt 的控制流转移上是明确赋值的在指向 then-stmt 的控制流转移上不是明确赋值的。此后当且仅当它在 then-stmt 的结束点是明确赋值的时它在 stmt 的结束点才是明确赋值的。

·         否则认为 v 在指向 then-stmt else-stmt或指向 stmt 的结束点如果没有 else 子句的控制流转移上都不是明确赋值的。

1.3.3.6 switch 语句

在带有控制表达式 expr switch 语句 stmt

·         位于 expr 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         在指向可访问的 switch 语句列表的控制流转移上v 的明确赋值状态就是它在 expr 结尾处的明确赋值状态。

1.3.3.7 while 语句

对于具有以下形式的 while 语句 stmt

while ( expr ) while-body

·         v expr 的开头处与在 stmt 的开头处具有相同的明确赋值状态。

·         如果 v expr 的结尾处明确赋值则它在指向 while-body 和指向 stmt 结束点的控制流转移上是明确赋值的。

·         如果 v expr 的结尾处具有 true 表达式后明确赋值状态则它在指向 while-body 的控制流转移上是明确赋值的但在 stmt 的结束点处不是明确赋值的。

·         如果 v expr 的结尾处具有 false 表达式后明确赋值状态则它在指向 stmt 的结束点的控制流转移上是明确赋值的但在指向 while-body 的控制流转移上不是明确赋值的。

1.3.3.8 do 语句

对于具有以下形式的 do 语句 stmt

do do-body while ( expr ) ;

·         v 在从 stmt 的开头处到 do-body 的控制流转移上的明确赋值状态与在 stmt 的开头处的状态相同。

·         v expr 的开头处与在 do-body 的结束点具有相同的明确赋值状态。

·         如果 v expr 的结尾处是明确赋值的则它在指向 stmt 的结束点的控制流转移上是明确赋值的。

·         如果 v expr 的结尾处的状态为 false 表达式后明确赋值”,则它在指向 stmt 的结束点的控制流转移上是明确赋值的。

1.3.3.9 for 语句

对具有以下形式的 for 语句进行的明确赋值检查

for ( for-initializer ; for-condition ; for-iterator ) embedded-statement

就如执行下列语句一样

{
  
for-initializer ;
while (
for-condition ) {
    
embedded-statement ;
    
for-iterator ;
}
}

如果 for 语句中省略了 for-condition则在确定关于明确赋值的状态时可把上述展开语句列表中的 for-condition 当作 true

1.3.3.10 breakcontinue goto 语句

breakcontinue goto 语句引起的控制流转移上的 v 的明确赋值状态与它在该语句开头处的明确赋值状态是一样的。

1.3.3.11 throw 语句

对于具有以下形式的语句 stmt

throw expr ;

位于 expr 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

1.3.3.12 return 语句

对于具有以下形式的语句 stmt

return expr ;

·         位于 expr 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         如果 v 是输出参数则它必须在下列两个位置之一被明确赋值

o        expr 之后

o        在包含 return 语句的 try-finally try-catch-finally finally 块的结尾处。

对于具有以下形式的语句 stmt

return ;

·         如果 v 是输出参数则它必须在下列两个位置之一被明确赋值

o        stmt 之前

o        在包含 return 语句的 try-finally try-catch-finally finally 块的结尾处。

1.3.3.13 try-catch 语句

对于具有以下形式的语句 stmt

try try-block
catch(...)
catch-block-1
...
catch(...)
catch-block-n

·         位于 try-block 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         位于 catch-block-i对于所有的 i开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         当且仅当 v try-block 和每个 catch-block-i每个 i 1 n的结束点明确赋值时stmt 结束点处的 v 的明确赋值状态才是明确赋值的。

1.3.3.14 try-finally 语句

对于具有以下形式的 try 语句 stmt

try try-block finally finally-block

·         位于 try-block 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         位于 finally-block 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         当且仅当下列条件中至少有一个为真时位于 stmt 结束点处的 v 的明确赋值状态才是明确赋值的

o        v try-block 的结束点明确赋值

o        v finally-block 的结束点明确赋值

如果控制流转移例如goto 语句 try-block 内开始 try-block 外结束那么如果 v finally-block 的结束点明确赋值v 也被认为在该控制流转移上明确赋值。这不是必要条件如果 v 由于其他原因在该控制流转移上明确赋值则它仍被视为明确赋值。

1.3.3.15 try-catch-finally 语句

对具有以下形式的 try-catch-finally 语句进行的明确赋值分析

try try-block
catch(...)
catch-block-1
...
catch(...)
catch-block-n
finally
finally-block

在进行明确赋值分析时可把该语句当作包含了 try-catch 语句的 try-finally 语句如下所示

try {
try
try-block
catch(...)
catch-block-1
  
...
catch(...)
catch-block-n
}
finally
finally-block

下面的示例演示 try 语句 8.10 的不同块如何影响明确赋值状态。

class A
{
static void F() {
     int i, j;
     try {
        goto LABEL;
        // neither i nor j definitely assigned
        i = 1;
        // i definitely assigned
     }

     catch {
        // neither i nor j definitely assigned
        i = 3;
        // i definitely assigned
     }

     finally {
        // neither i nor j definitely assigned
        j = 5;
        // j definitely assigned
     }
     // i and j definitely assigned
  LABEL:;
     // j definitely assigned

}
}

1.3.3.16 foreach 语句

对于具有以下形式的 foreach 语句 stmt

foreach ( type identifier in expr ) embedded-statement

·         位于 expr 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         在指向 embedded-statement 或指向 stmt 结束点处的控制流转移上v 的明确赋值状态与位于 expr 结尾处的 v 的状态相同。

1.3.3.17 using 语句

对于具有以下形式的 using 语句 stmt

using ( resource-acquisition ) embedded-statement

·         位于 resource-acquisition 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         在指向 embedded-statement 的控制流转移上v 的明确赋值状态与位于 resource-acquisition 结尾处的 v 的状态相同。

1.3.3.18 lock 语句

对于具有以下形式的 lock 语句 stmt

lock ( expr ) embedded-statement

·         位于 expr 开头处的 v 的明确赋值状态与位于 stmt 开头处的 v 的明确赋值状态相同。

·         在指向 embedded-statement 的控制流转移上v 的明确赋值状态与位于 expr 结尾处的 v 的状态相同。

1.3.3.19 简单表达式的一般规则

下列规则应用于这些类型的表达式文本 7.5.1 、简单名称 7.5.2 、成员访问表达式 7.5.4 、非索引 base 访问表达式 7.5.8 typeof 表达式 7.5.11

·         位于此类表达式结尾处的 v的明确赋值状态与位于表达式开头处的 v 的明确赋值状态相同。

1.3.3.20 带有嵌入表达式的表达式的一般规则

下列规则应用于这些类型的表达式带括号的表达式 7.5.3 );元素访问表达式 7.5.6 );带索引的 base 访问表达式 7.5.8 );增量和减量表达式 7.5.9 节、第 7.6.5 );强制转换表达式 7.6.6 );一元 +-~* 表达式二元 +-*/%<<>><<=>>===!=isas&|^ 表达式 7.7 节、第 7.8 节、第 7.9 节、第 7.10 );复合赋值表达式 7.13.2 );checked unchecked 表达式 7.5.12 );数组和委托创建表达式 7.5.10

这些表达式的每一个都有一个和多个按固定顺序无条件计算的子表达式。例如,二元运算符 % 先计算运算符左边的值,然后计算右边的值。索引操作先计算索引表达式,然后按从左到右的顺序计算每个索引表达式。对于具有子表达式 expr1expr2...exprn 的表达式 expr,按下列顺序计算:

·         位于 expr1 开头处的 v 的明确赋值状态与位于 expr 开头处的 v 的明确赋值状态相同。

·         位于 exprii 大于 1开头处的 v 的明确赋值状态与位于 expri-1 结尾处的 v 的明确赋值状态相同。

·         位于 expr 结尾处的 v 的明确赋值状态与位于 exprn 结尾处的 v 的明确赋值状态相同。

1.3.3.21 调用表达式和对象创建表达式

对于具有以下形式的调用表达式 expr

primary-expression ( arg1 , arg2 , … , argn )

或具有以下形式的对象创建表达式

new type ( arg1 , arg2 , … , argn )

·         对于调用表达式位于 primary-expression 之前的 v 的明确赋值状态与位于 expr 之前的 v 的状态相同。

·         对于调用表达式位于 arg1 之前的 v 的明确赋值状态与位于 primary-expression 之后的 v 的明确赋值状态相同。

·         对于对象创建表达式位于 arg1 之前的 v 的明确赋值状态与位于 expr 之前的 v 的状态相同。

·         对于每一个参数 argi位于 argi 之后的 v 的明确赋值状态由标准表达式规则决定其中忽略所有的 ref out 修饰符。

·         对于每一个 i 大于 1 的参数 argi位于 argi 之前的 v 的明确赋值状态与位于 argi-1 之后的 v 的状态相同。

·         如果变量 v 是被作为 out 参数传送形式为out v的参数),则无论将它用作哪一个 argi expr 之后v 的状态是明确赋值的。否则,位于 expr 之后的 v 的状态与位于 argn 之后的 v 的状态相同。

1.3.3.22 简单赋值表达式

对于具有形式 w = expr-rhs 的表达式 expr

·         位于 expr-rhs 之前的 v 的明确赋值状态与位于 expr 之前的 v 的明确赋值状态相同。

·         如果 w v 是同一变量则位于 expr 之后的 v 的明确赋值状态是明确赋值的。否则,位于 expr 之后的 v 的明确赋值状态与位于 expr-rhs 之后的 v 的明确赋值状态相同。

1.3.3.23 && 表达式

对于形式为 expr-first && expr-second 的表达式 expr

·         位于 expr-first 之前的 v 的明确赋值状态与位于 expr 之前的 v 的明确赋值状态相同。

·         如果位于 expr-first 之后的 v 的状态是明确赋值的或为 true 表达式后明确赋值”,则位于 expr-second 之前的 v 的明确赋值状态是明确赋值的。否则它就不是明确赋值的。

·         位于 expr 之后的 v 的明确赋值状态取决于

o        如果在 expr-first 之后v 的状态是明确赋值的则在 expr 之后的 v 的状态也是明确赋值的。

o        否则如果位于 expr-second 之后的 v 的状态是明确赋值的而且位于 expr-first 之后的 v 的状态为 false 表达式后明确赋值”,则位于 expr 之后的 v 的状态是明确赋值的。

o        否则如果位于 expr-second 之后的 v 的状态是明确赋值的或为 true 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 true 表达式后明确赋值

o        否则如果位于 expr-first 之后的 v 的状态是 false 表达式后明确赋值”,而且位于 expr-second 之后的 v 的状态是 false 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 false 表达式后明确赋值

o        否则 expr 之后v 的状态就不是明确赋值的。

在下面的示例中

class A
{
static void F(int x, int y) {
     int i;
     if (x >= 0 && (i = y) >= 0) {
        // i definitely assigned
     }
     else {
        // i not definitely assigned
     }
     // i not definitely assigned
}
}

变量 i 被视为在 if 语句的一个嵌入语句中已明确赋值而在另一个嵌入语句中未明确赋值。在 F 方法中的 if 语句中,由于总是在第一个嵌入语句执行前执行表达式 (i = y),因此变量 i 在第一个嵌入语句中已明确赋值。相反,变量 i 在第二个嵌入语句中没有明确赋值,因为 x >= 0 可能已测试为 false,从而导致变量 i 未赋值。

1.3.3.24 || 表达式

对于形式为 expr-first || expr-second 的表达式 expr

·         位于 expr-first 之前的 v 的明确赋值状态与位于 expr 之前的 v 的明确赋值状态相同。

·         如果位于 expr-first 之后的 v 的状态是明确赋值的或 false 表达式后明确赋值”,则位于 expr-second 之前的 v 的明确赋值状态是明确赋值的。否则它就不是明确赋值的。

·         位于 expr 之后的 v 的明确赋值状态取决于

o        如果在 expr-first 之后v 的状态是明确赋值的则在 expr 之后的 v 的状态也是明确赋值的。

o        否则如果位于 expr-second 之后的 v 的状态是明确赋值的而且位于 expr-first 之后的 v 的状态为 true 表达式后明确赋值”,则位于 expr 之后的 v 的状态是明确赋值的。

o        否则如果位于 expr-second 之后的 v 的状态是明确赋值的或是 false 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 false 表达式后明确赋值

o        否则如果位于 expr-first 之后的 v 的状态是 true 表达式后明确赋值”,而且位于 expr-second 之后的 v 的状态是 true 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 true 表达式后明确赋值

o        否则 expr 之后v 的状态就不是明确赋值的。

在下面的示例中

class A
{
static void G(int x, int y) {
     int i;
     if (x >= 0 || (i = y) >= 0) {
        // i not definitely assigned
     }
     else {
        // i definitely assigned
     }
     // i not definitely assigned
}
}

变量 i 被视为在 if 语句的一个嵌入语句中已明确赋值而在另一个嵌入语句中未明确赋值。在 G 方法中的 if 语句中,由于总是在第二个嵌入语句执行前执行表达式 (i = y),因此变量 i 在第二个嵌入语句中已明确赋值。相反,在第一个嵌入语句中,变量 i 的状态不是明确赋值的,因为 x >= 0 可能已测试为 true,从而导致变量 i 未赋值。

1.3.3.25 ! 表达式

对于形式为 ! expr-operand 的表达式 expr

·         位于 expr-operand 之前的 v 的明确赋值状态与位于 expr 之前的 v 的明确赋值状态相同。

·         位于 expr 之后的 v 的明确赋值状态取决于

o        如果在 expr-operand 之后v 的状态是明确赋值的则在 expr 之后v 的状态也是明确赋值的。

o        如果在 expr-operand 之后v 的状态不是明确赋值的则在 expr 之后v 的状态也不是明确赋值的。

o        如果位于 expr-operand 之后的 v 的状态是 false 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 true 表达式后明确赋值

o        如果位于 expr-operand 之后的 v 的状态是 true 表达式后明确赋值”,则位于 expr 之后的 v 的状态是 false 表达式后明确赋值

1.3.3.26 ?: 表达式

对于形式为 expr-cond ? expr-true : expr-false 的表达式 expr

·         位于 expr-cond 之前的 v 的明确赋值状态与位于 expr 之前的 v 的状态相同。

·         当且仅当位于 expr-cond 之后的 v 的状态是明确赋值的或 true 表达式后明确赋值位于 expr-true 之前的 v 的明确赋值状态才是明确赋值的。

·         当且仅当位于 expr-cond 之后的 v 的状态是明确赋值的或 false 表达式后明确赋值位于 expr-false 之前的 v 的明确赋值状态才是明确赋值的。

·         位于 expr 之后的 v 的明确赋值状态取决于

o        如果 expr-cond 是值为 true 的常量表达式 7.15 ),则位于 expr 之后的 v 的状态与位于 expr-true 之后的 v 的状态相同。

o        否则如果 expr-cond 是值为 false 的常量表达式 7.15 ),则位于 expr 之后的 v 的状态与位于 expr-false 之后的 v 的状态相同。

o        否则如果位于 expr-true 之后的 v 的状态是明确赋值的而且位于 expr-false 之后的 v 的状态也是明确赋值的则位于 expr 之后的 v 的状态是明确赋值的。

o        否则 expr 之后v 的状态就不是明确赋值的。

1.4 变量引用

variable-reference 是一个 expression它被归类为一个变量。variable-reference 表示一个存储位置访问它可以获取当前值以及存储新值。

variable-reference:
expression

C C++ variable-reference 称为 lvalue

1.5 变量引用的原子性

下列数据类型的读写是原子的boolcharbytesbyteshortushortuintintfloat 和引用类型。除此之外当枚举类型的基础类型的属于上述类型之一时对它的读写也是原子的。其他类型的读写,包括 longulongdoubledecimal 以及用户定义类型,都不保证是原子的。除专为该目的设计的库函数以外对于增量或减量这类操作也不能保证进行原子的读、修改和写。

posted on 2006-08-03 16:02  2017_LIVE  阅读(293)  评论(0编辑  收藏  举报