- 浏览: 3053119 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
前天跟axx大聊起那个do..while(0)的宏的时候顺带聊到了别的一些语法结构的诡异地方。
觉得在C或者C-like语言里很麻烦的一个语法结构是for语句。比较常见的定义方式会是:
也就是,一般来说for语句头部的括号里,第二部分是一个表达式,第三部分是一个表达式列表,而第一部分可能是一个变量声明列表或者一个表达式列表。按照局部作用域的规则,一般来说在这第一部分里声明的变量都是局部与for语句内的;如果与外部作用域已定义的变量重名,则可能:
1、不允许这样重定义(Java、C#、D等);
2、在for语句的局部作用域内创建一个新的局部变量,遮盖外部作用域原本的同名变量(C99/C++98);
3、不允许在for语句的头部定义新变量——所有局部变量都必须在局部作用域的一开头定义。(C99以前的C);
4、由于同一个局部作用域允许同一个名字的变量多次声明,所以实际上声明与不声明都没啥区别;for的头部里声明的变量与外部作用域的同名变量可以看成是“同一个”(ECMAScript 3)。
让我们看看C-like语言里具体是怎么定义的。关键要留意一下for头部的第一部分的规定。
------------------------------------------------------------------------------
C99:ISO/IEC 9899:1999, 6.8.5.3
C99里的for语句与前面说的“一般情况”吻合。第一部分的子句可以是变量声明或者表达式,但不能是语句。
演示代码:
testCScope.c:
用GCC 3.4.5编译出来的结果。跟预期一样,for里创建了一个新的局部变量c,遮蔽了main()里的c。
------------------------------------------------------------------------------
C++98:ISO/IEC 14882:1998, 6.5.3
(这PDF复制不了……懒得打字,截图代替)
可以看到,C++98里对for语句头部第一部分的定义与C99的写法不一样——第一部分是一个语句,而那个分号是语句的一部分。
不过还得结合另外一部分的规定来看:
结合这个来看,其实它与C99的规定并没有多少区别。只是写法上的差异而已。
演示代码:
testCppScope.cpp:
用GCC 3.4.5和VC++2008编译都一样。运行结果是0,没问题,跟预期一样,与C99也吻合。
------------------------------------------------------------------------------
Java:Java Language Specification, 3rd Edition
Java的语法也与“一般情况”吻合。但是它不允许在for的头部对方法的局部变量进行再次声明,所以下面的代码在编译时会出现错误。
演示代码:
testJavaScope.java:
------------------------------------------------------------------------------
C#:ECMA-334 4th Edition, A.2.5
于是C#的for语句在语法上也跟C99、C++98、Java等相似,属于“一般情况”。
演示代码:
testCSharpScope.cs:
这段代码编译出错了。但是出错的原因与Java的版本并不完全相同,因为Java与C#的作用域规则并不完全一样。这里我们暂时不关心那个问题,至少在for语句头部的第一部分表现相似就是了。
------------------------------------------------------------------------------
吉里吉里2的TJS2
语法上也属于“一般情况。看看运行时如何?
演示代码:
startup.tjs:
运行结果是c == 0。去掉中间的注释的话,可以看到for循环中c是1,没问题。
于是TJS2在这个地方的行为与C99/C++98更相似。
------------------------------------------------------------------------------
D语言在这里比较诡异。
D 1.0
D 2.0:
演示代码1:
testDScope.d:
OK,编译时出现错误。跟前面Java和C#的行为差不多。但是……
演示代码2:
testDScope.d:
这段代码可以顺利通过编译(DMD 2.012),而且运行的结果与C/C++不一样……
诡异吧?
------------------------------------------------------------------------------
ECMAScript:ECMA-262 3rd Edition, 12.6
看上去语法与“一般情况”吻合。但这ECMAScript实际上也不乖……
让我们用Rhino 1.7R1来测试一下:
看到了吧,c的值变为1了。这跟ECMAScript对作用域的规定相关:同一个作用域内同一个名字的变量可以多次声明;多次声明的同名变量还是“同一个”;var关键字声明的变量拥有的是function scoping。所以……要是按照Java或者C#的习惯来写JavaScript代码,这里就危险了……
从JavaScript 1.7开始增加了let关键字,相应增加了let语句、let表达式和let声明。以let关键字而不是var关键字声明的变量的作用域就是局部于最小的语句块的,而不是函数的。但是for循环的初始化部分却无法用let关键字声明循环变量……
===========================================================================
真的是自己不写语言的语法都不觉得,真到要自己写语法的时候就会注意到很多这种诡异的地方 T T
觉得在C或者C-like语言里很麻烦的一个语法结构是for语句。比较常见的定义方式会是:
ForStatement -> "for" "(" ForInitialize ";" ForCondition ";" ForIncrement ")" ForBody ; ForInitialize -> VariableDeclarationList | ExpressionList ; ForCondition -> Expression ; ForIncrement -> ExpressionList ; ForBody -> Statement ;
也就是,一般来说for语句头部的括号里,第二部分是一个表达式,第三部分是一个表达式列表,而第一部分可能是一个变量声明列表或者一个表达式列表。按照局部作用域的规则,一般来说在这第一部分里声明的变量都是局部与for语句内的;如果与外部作用域已定义的变量重名,则可能:
1、不允许这样重定义(Java、C#、D等);
2、在for语句的局部作用域内创建一个新的局部变量,遮盖外部作用域原本的同名变量(C99/C++98);
3、不允许在for语句的头部定义新变量——所有局部变量都必须在局部作用域的一开头定义。(C99以前的C);
4、由于同一个局部作用域允许同一个名字的变量多次声明,所以实际上声明与不声明都没啥区别;for的头部里声明的变量与外部作用域的同名变量可以看成是“同一个”(ECMAScript 3)。
让我们看看C-like语言里具体是怎么定义的。关键要留意一下for头部的第一部分的规定。
------------------------------------------------------------------------------
C99:ISO/IEC 9899:1999, 6.8.5.3
引用
1 The statement
behaves as follows: The expression expression-2 is the controlling expression that is evaluated before each execution of the loop body. The expression expression-3 is evaluated as a void expression after each execution of the loop body. If clause-1 is a declaration, the scope of any variables it declares is the remainder of the declaration and the entire loop, including the other two expressions; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.134)
2 Both clause-1 and expression-3 can be omitted. An omitted expression-2 is replaced by a nonzero constant.
for ( clause-1 ; expression-2 ; expression-3 ) statement
behaves as follows: The expression expression-2 is the controlling expression that is evaluated before each execution of the loop body. The expression expression-3 is evaluated as a void expression after each execution of the loop body. If clause-1 is a declaration, the scope of any variables it declares is the remainder of the declaration and the entire loop, including the other two expressions; it is reached in the order of execution before the first evaluation of the controlling expression. If clause-1 is an expression, it is evaluated as a void expression before the first evaluation of the controlling expression.134)
2 Both clause-1 and expression-3 can be omitted. An omitted expression-2 is replaced by a nonzero constant.
C99里的for语句与前面说的“一般情况”吻合。第一部分的子句可以是变量声明或者表达式,但不能是语句。
演示代码:
testCScope.c:
#include <stdio.h> int main( ) { int c = 0; for ( int i = 0; i < 2; ++i ) { // ... do something } printf( "%d\n", c ); // 0 } /* rednaxela@META-FX /d/experiment $ gcc -std=c99 testCScope.c -o testCScope.exe */
用GCC 3.4.5编译出来的结果。跟预期一样,for里创建了一个新的局部变量c,遮蔽了main()里的c。
------------------------------------------------------------------------------
C++98:ISO/IEC 14882:1998, 6.5.3
(这PDF复制不了……懒得打字,截图代替)
可以看到,C++98里对for语句头部第一部分的定义与C99的写法不一样——第一部分是一个语句,而那个分号是语句的一部分。
不过还得结合另外一部分的规定来看:
引用
for-init-statement: expression-statement simple-declaration
结合这个来看,其实它与C99的规定并没有多少区别。只是写法上的差异而已。
演示代码:
testCppScope.cpp:
#include <iostream> int main( ) { int c = 0; for ( int i = 0, c = 1; i < 2; ++i ) { // ... do something } std::cout << c << std::endl; // 0 } /* D:\experiment>cl testCppScope.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. testCppScope.cpp C:\Program Files\Microsoft Visual Studio 9.0\VC\INCLUDE\xlocale(342) : warning C 4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc Microsoft (R) Incremental Linker Version 9.00.21022.08 Copyright (C) Microsoft Corporation. All rights reserved. /out:testCppScope.exe testCppScope.obj */
用GCC 3.4.5和VC++2008编译都一样。运行结果是0,没问题,跟预期一样,与C99也吻合。
------------------------------------------------------------------------------
Java:Java Language Specification, 3rd Edition
引用
BasicForStatement: for ( ForInitopt ; Expressionopt ; ForUpdateopt ) Statement ForStatementNoShortIf: for ( ForInitopt ; Expressionopt ; ForUpdateopt ) StatementNoShortIf ForInit: StatementExpressionList LocalVariableDeclaration ForUpdate: StatementExpressionList StatementExpressionList: StatementExpression StatementExpressionList , StatementExpression
Java的语法也与“一般情况”吻合。但是它不允许在for的头部对方法的局部变量进行再次声明,所以下面的代码在编译时会出现错误。
演示代码:
testJavaScope.java:
public class testJavaScope { public static void main( String[ ] args ) { int c = 0; for ( int i = 0, c = 1; i < 2; ++i ) { // error // ... do something } System.out.println( c ); } } /* D:\experiment>javac testJavaScope.java testJavaScope.java:4: 已在 main(java.lang.String[]) 中定义 c for ( int i = 0, c = 1; i < 2; ++i ) { ^ 1 错误 */
------------------------------------------------------------------------------
C#:ECMA-334 4th Edition, A.2.5
引用
for-statement: for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement for-initializer: local-variable-declaration statement-expression-list for-condition: boolean-expression for-iterator: statement-expression-list statement-expression-list: statement-expression statement-expression-list , statement-expression
于是C#的for语句在语法上也跟C99、C++98、Java等相似,属于“一般情况”。
演示代码:
testCSharpScope.cs:
sealed class Test { public static void Main( string[ ] args ) { int c = 0; for ( int i = 0, c = 1; i < 2; ++i ) { // error // ... do something } System.Console.WriteLine( c ); } } /* D:\experiment>csc testCSharpScope.cs 适用于 Microsoft(R) .NET Framework 3.5 版的 Microsoft(R) Visual C# 2008 编译器 3.5.21022.8 版 版权所有 (C) Microsoft Corporation。保留所有权利。 testCSharpScope.cs(4,26): error CS0136: 不能在此范围内声明名为“c”的局部变量,因为这样会使“c”具有不同的含义, 而它已在“父级或当前”范围中表示其他内容了 */
这段代码编译出错了。但是出错的原因与Java的版本并不完全相同,因为Java与C#的作用域规则并不完全一样。这里我们暂时不关心那个问题,至少在for语句头部的第一部分表现相似就是了。
------------------------------------------------------------------------------
吉里吉里2的TJS2
引用
2.28,\kirikiri2\src\core\tjs2\syntax\tjs.y,第298行开始
/* a for loop */ for : "for" "(" for_first_clause ";" for_second_clause ";" for_third_clause ")" block_or_statement { cc->ExitForCode(); } ; /* the first clause of a for statement */ for_first_clause : /* empty */ { cc->EnterForCode(false); } | { cc->EnterForCode(true); } variable_def_inner | expr { cc->EnterForCode(false); cc->CreateExprCode($1); } ; /* the second clause of a for statement */ for_second_clause : /* empty */ { cc->CreateForExprCode(NULL); } | expr { cc->CreateForExprCode($1); } ; /* the third clause of a for statement */ for_third_clause : /* empty */ { cc->SetForThirdExprCode(NULL); } | expr { cc->SetForThirdExprCode($1); } ;
语法上也属于“一般情况。看看运行时如何?
演示代码:
startup.tjs:
function foo() { var c = 0; for ( var i = 0, c = 1; i < 2; ++i ) { // ... do something // System.inform( c ); } System.inform( c ); } foo();
运行结果是c == 0。去掉中间的注释的话,可以看到for循环中c是1,没问题。
于是TJS2在这个地方的行为与C99/C++98更相似。
------------------------------------------------------------------------------
D语言在这里比较诡异。
D 1.0
D 2.0:
引用
ForStatement: for (Initialize Test; Increment) ScopeStatement Initialize: ; NoScopeNonEmptyStatement Test: empty Expression Increment: empty Expression
演示代码1:
testDScope.d:
void main(char[][] args) { int c = 0; for (int i = 0, c = 1; i < 2; ++i) { // error // ...do something } printf("%d", c); } /* D:\experiment>dmd testDScope.d testDScope.d(3): Error: shadowing declaration testDScope.main.c is deprecated */
OK,编译时出现错误。跟前面Java和C#的行为差不多。但是……
演示代码2:
testDScope.d:
void main(char[][] args) { int c = 0; for ({int i = 0; c = 1;} i < 2; ++i) { // ...do something } printf("%d", c); // 1 }
这段代码可以顺利通过编译(DMD 2.012),而且运行的结果与C/C++不一样……
诡异吧?
------------------------------------------------------------------------------
ECMAScript:ECMA-262 3rd Edition, 12.6
引用
for (ExpressionNoInopt; Expressionopt ; Expressionopt ) Statement for ( var VariableDeclarationListNoIn; Expressionopt ; Expressionopt ) Statement
看上去语法与“一般情况”吻合。但这ECMAScript实际上也不乖……
让我们用Rhino 1.7R1来测试一下:
Rhino 1.7 release 1 2008 03 06 js> var c = 0 js> for ( var i = 0, c = 1; i < 2; ++i ) { /* ... */ } js> c 1 js> i 2
看到了吧,c的值变为1了。这跟ECMAScript对作用域的规定相关:同一个作用域内同一个名字的变量可以多次声明;多次声明的同名变量还是“同一个”;var关键字声明的变量拥有的是function scoping。所以……要是按照Java或者C#的习惯来写JavaScript代码,这里就危险了……
从JavaScript 1.7开始增加了let关键字,相应增加了let语句、let表达式和let声明。以let关键字而不是var关键字声明的变量的作用域就是局部于最小的语句块的,而不是函数的。但是for循环的初始化部分却无法用let关键字声明循环变量……
===========================================================================
真的是自己不写语言的语法都不觉得,真到要自己写语法的时候就会注意到很多这种诡异的地方 T T
发表评论
-
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
基于LLVM实现VM的JIT的一些痛点
2014-01-07 17:25 0同事Philip Reames Sanjoy Das http ... -
tailcall notes
2013-12-27 07:42 0http://blogs.msdn.com/b/clrcode ... -
《自制编程语言》的一些笔记
2013-11-24 00:20 0http://kmaebashi.com/programmer ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22410(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局 (0): 拿在手上的是什么
2013-11-04 18:22 21510(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
Function.prototype.bind
2013-09-24 18:07 0polyfill http://stackoverflow. ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
struct做参数不能从寄存器传?
2013-08-28 23:33 0test test test struct Foo { i ... -
也谈类型: 数据, 类型, 标签
2013-08-18 01:59 0numeric tower http://en.wikiped ... -
SDCC 2012上做的JVM分享
2012-10-17 16:35 32662刚把在SDCC 2012做的JVM分享的演示稿上传了。 演示 ... -
运行时支持系统的“重量”
2012-10-12 16:08 0运行时支持系统的“重量” 好久没写博客了,可写的话题已经堆积 ... -
class?metaclass?meta-what?
2011-04-05 19:43 0http://en.wikipedia.org/wiki/Me ... -
“代码模式”与抽象
2010-10-28 15:21 0嗯,我是说“代码模式”,不是“设计模式”;这里指的是在给定的场 ... -
lvalue与rvalue
2010-09-03 00:40 0http://en.wikipedia.org/wiki/Va ... -
动态链接的“依据”
2010-02-09 09:54 0动态链接,意味着在生成的“东西”里留有符号信息,等到运行时再r ...
相关推荐
函数的变量和作用域 /全局变量和全局作用域指的是变量或者函数的位置 ... // 局部作用域:任何一个函数的内部都有一个局部作用域,在局部作用域中定义的变量 局部变量。局部变量只有在定义该变量的函数中可以访问
2. **局部作用域(Local Scope)**:局部变量在函数内部定义,仅在该函数内部可见。函数调用结束后,局部变量的存储空间会被释放,它们的值也会丢失。 3. **块级作用域(Block Scope)**:在C99引入了复合语句...
JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...
接着讨论了 JavaScript 中的全局作用域、局部作用域、块级作用域和函数作用域,特别是闭包的概念。随后,文章探讨了函数的高级用法,如递归函数、高阶函数和立即执行函数表达式(IIFE)。最后,通过实际应用示例,如...
2. 局部作用域(Local Scope):在函数内部定义的变量,只在该函数内部可见。 3. 块作用域(Block Scope):如`for`和`while`循环,`if`语句等,它们有自己的作用域。 4. 闭包作用域(Closure Scope):当一个内嵌...
- **局部作用域(Local, L)**:这是最内层的作用域,通常在函数或方法内部,只包含在此区域内定义的变量。这些变量在函数执行完毕后会被销毁。 - **包围作用域(Enclosing, E)**:当一个函数或类内部包含另一个...
2. **局部作用域**:只在特定代码块(如函数内部)中可访问的变量。 3. **块级作用域**:在特定的代码块(如循环体、条件语句等)中可访问的变量。 #### 三、函数原型作用域 函数原型中的参数具有特定的作用域。...
2. **局部作用域**:在函数内部定义的变量仅在该函数内部可见,无法在函数外部访问。此外,每个函数都拥有自己的作用域链,用于在执行过程中查找变量。 **作用域链**:当尝试访问一个变量时,JavaScript会沿着作用...
局部作用域是指当前函数或代码块中的变量作用域。局部作用域中的变量优先级最高,只有在局部作用域中找不到变量时,Python 才会去外部作用域和全局作用域中查找。 外部作用域是指当前函数或代码块的外部函数或代码...
- 定义:当一个变量在函数内部声明时,该变量仅在该函数内有效,这就是局部作用域。 - 特性: - 函数内部声明的变量不会污染全局命名空间。 - 如果函数内部声明的变量与全局变量同名,局部变量会覆盖全局变量。 ...
函数作用域通常与局部作用域相重叠,但指的是通过函数定义的方式(使用function关键字或函数表达式)来创建的函数,它们作用域的范围被限制在函数体内。函数作用域内的变量可以是局部的,也可以是从外部作用域中引入...
局部作用域则限定在特定代码块,如函数内部。在函数内部定义的变量只有在该函数内部可以访问,外部无法直接访问。这样的设计有助于防止变量污染全局空间,提高代码的可维护性和性能。 作用域链是JavaScript中另一个...
- **定义**:在函数内部声明的变量拥有局部作用域。 - **访问**:这些变量仅在该函数内部可访问。 - **示例**: ```javascript function checkLocal() { var localVar = "我是局部变量"; console.log(localVar)...
一、局部变量 局部变量是在函数内部定义的变量,其作用域仅限于函数内部,离开该函数后再使用此变量是非法的。另外,函数定义中的普通形参由于只能在本函数内部使用,因此也是局部变量。 二、全局变量 全局变量是指...
2. **局部作用域**:在函数内部声明的变量是局部的,它们只在该函数内可见。当函数执行结束时,这些变量会被销毁。 3. **类成员作用域**:在类的定义中,但不在任何方法内的变量称为类成员变量,它们对类的所有实例...
JavaScript 还没有块级作用域,这意味着在 if 语句、for 循环、while 循环等语句块中定义的变量,在整个函数体内都是可见的,而不是只在语句块中可见。 在 JavaScript 中,函数中声明的变量在整个函数中都有定义。...
2. **局部作用域**:在函数内部定义的变量具有局部作用域,只在函数内部有效。一旦函数执行完毕,这些变量就会被销毁。例如: ```python def local_example(): y = 5 print(y) # 输出5 local_example() print(y)...
关于变量的作用域,C++中有全局作用域和局部作用域。全局变量在整个程序中都是可见的,而局部变量只在定义它的函数或代码块内部可见。例如: ```cpp int global_var = 10; // 全局变量 void func() { int local_...
### 什么是作用域? 作用域是编程语言中的一个重要概念,它定义...通过掌握不同的作用域类型,如函数原型作用域、局部变量和全局变量,开发者能够更好地控制变量的生命周期和可见性,从而编写出更加清晰和高效的程序。
- **局部作用域**:在函数内部声明的变量,只在函数内部可见,函数执行完毕后,局部变量会被销毁。 在ES6中,引入了`let`和`const`两个新的关键字,用于声明块级作用域的变量,它们的使用更加严格,不允许变量提升...