- 浏览: 3052783 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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分享的概要
来来回回已经碰到这问题不知道多少次了,但每过一段时间总得把它弄混。郁闷啊。还是得记下来才行,不然每次都翻规范太痛苦了。
C/C++里的数组是“矩形”(rectangle)的,也就是说数组每个维度中的元素的长度都一样。
下标的顺序是:下标从左向右指定的是数组从外向内的维度的长度。跟有初始化器时,声明的最外层的维度可以留空,也就是说最左边的下标可以留空。没有初始化器时,声明的所有维度都必须指定。其中C89是只允许以常量来指定数组维度的长度,C99和GCC扩展则允许使用变量来指定。至于为什么VC从6到9都无法编译这段代码:
大概只是因为对C99支持得不好吧。用GCC开-std=c99也照样能编译的。
对多维数组的指针运算跟下标是对应的。
C99对数组声明的规定真是繁琐得不行……在什么范围允许留空,或者允许*,或者允许可变长度,Geez。还是要用到那么麻烦的东西的时候才去查C99规范的6.7.5.2好了。
C/C++的初始化器里元素的个数可以比对应维度的长度要少,此时剩余的元素与静态存储级的变量一样会被默认初始化(例如说算术类型的话会初始化为0)。
C#和Java的初始化器就不能省略任何元素,必须与new表达式里指定的维度/长度匹配。
C#同样支持矩形数组。
下标的顺序与C/C++里的一样。new的时候要么所有维度都不指定长度,要么所有维度都要指定长度。指定长度的版本应用的是这条语法规则:
new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
不指定长度的应用的是则是另一条:
new array-type array-initializer
C#也支持所谓锯齿形数组(jagged array),它只要求总的维度数量与声明吻合,不要求每个维度的元素长度相等。
C#规定锯齿形数组在new的时候,如果没有跟初始化器,则必须指定最外层的维度,其余的维度必须留空。如果跟有初始化器,则最外层的维度可以留空。
注意到锯齿形数组的初始化器无法用矩形数组的简写语法,内部的数组也必须写成new表达式。
顺带一记:.NET的数组可以分为SZArray和普通Array两种,前者是single-dimensional zero-based array,在CLI术语中也叫vector,只有这种数组有直接操作的MSIL/CIL指令;后者是多维数组或者下标不从0开始的数组,其相关操作都通过库方法来完成。
Java只支持锯齿形数组,多维数组实际上是数组的数组。
在new表达式里,如果没有跟初始化器,则最外层的维度必须指定,其余的维度可以指定也可以留空。如果跟有初始化器,则所有维度都必须留空。
除了通过ArrayCreationExpression之外,Java还允许直接用ArrayInitializer来创建数组实例:
但ArrayInitializer必须在数组变量的声明中、或者作为ArrayCreationExpression的一部分来使用,而不能作为任意表达式使用。也就是说不能这样写:
Java的数组最多只能有255维。在创建多维数组时如果只指定了最外层维度的长度,会使用newarray/anewarray指令;如果指定了多于一维的长度,则使用multianewarray指令。
Java的数组变量声明时可以将表示数组的方括号跟在元素类型后作为类型的一部分,也可以跟在变量名后作为变量声明的修饰;Java中惯用的写法是前一种。
主要就是这几种看起来很像的语言的数组微妙的不同让我总是弄混 T T
到底哪里必须指定,哪里必须留空,哪里是可指定可留空……|||
其实最关键的还是“什么是可以单独存在的对象”的问题吧。这里的对象指的是广义的对象。
C和C++里的多维数组是一个整体,代表一块连续的存储空间。
声明数组的时候,C/C++关心的是“要分配多少空间”。在没有初始化器时,当然只能通过指定所有维度的长度才能计算出要分配的空间大小。有初始化器时,可以通过初始化器中元素的个数来得到最外层维度的长度,所以可以给最外层维度的长度声明留空。内层维度必须指定长度这点恐怕是为了编译方便?
C#的矩形数组也是单一的对象,指向一块连续的存储空间。
C#和Java的锯齿形数组中每个维度都是连续的存储空间,但除了最内层的一维之外,其它维度的数组保存的是指向数组的引用。这些引用确实存在,而不像C/C++中取中间维度的地址时是算出来的。
由于数组长度不影响类型匹配(数组维度和元素类型才影响),如果数组的元素是指向数组的引用,那么这些元素指向的数组的长度是多少都可以。
所以C#不允许在锯齿形多维数组的new表达式中指定除最外层维度以外的维度长度。
Java……理由是一样的但为什么语法规则就是不同呢……
说来,最近才注意到LINQv1和LINQv2都不支持矩形多维数组的初始化……NewArrayInit只能用来初始化一维数组,嵌套使用可以初始化锯齿形多维数组,但矩形多维数组就只能用NewArrayBounds来创建新实例,却不支持对应的初始化。太郁闷了。
====================================================================
F#/OCaml的多维数组也是锯齿形数组,每个维度的元素长度可以不同。
F#在访问数组元素的时候跟OCaml的不一样 OTL
Standard ML没有数组的字面量。要用数组的话可以使用标准库里的array,也可以用许多SML实现所支持的vector。不过用下标访问数组元素的时候还是得用库函数:
不过ML家族里的语言许多情况下用list和tuple就行了,毕竟不可变的数据类型在许多情况下就够用了。
Scala的数组也是锯齿形的。
Haskell的数组跟SML一样是标准库里的,没有字面量。要使用数组的话需要import Array。要初始化一个无规则的数组看起来挺麻烦的:
内容有规则的数组的话,用lambda或者用list comprehension来初始化都挺方便的:
至于像是Ruby、JavaScript这些动态语言的数组,本来也就没有多不多维的问题,反正数组里什么都能装,自然也能容纳数组作为元素。
说来Python里[...]语法所指的序列是叫list而不是array啊,不过支持的操作都一样,没差。而且有切片用真方便。
然后还有FORTRAN的数组?老爸以前倒是经常用FORTRAN,但我就从来没学过这历史悠久的语言。只是在用Python的NumPy库时留意到一段说明,说C的二维数组是行优先存储,而FORTRAN的是列优先存储的,多维同理。另外FORTRAN的数组的下标默认是从1开始的。
C/C++里的数组是“矩形”(rectangle)的,也就是说数组每个维度中的元素的长度都一样。
#include <stdio.h> void main() { int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; // int arr[][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; printf("%d", arr[0][1]); // 2 // printf("%d", *(*(arr + 0) + 1)); // 2 }
下标的顺序是:下标从左向右指定的是数组从外向内的维度的长度。跟有初始化器时,声明的最外层的维度可以留空,也就是说最左边的下标可以留空。没有初始化器时,声明的所有维度都必须指定。其中C89是只允许以常量来指定数组维度的长度,C99和GCC扩展则允许使用变量来指定。至于为什么VC从6到9都无法编译这段代码:
void main() { int n = 2; int arr[n][3]; }
大概只是因为对C99支持得不好吧。用GCC开-std=c99也照样能编译的。
对多维数组的指针运算跟下标是对应的。
C99对数组声明的规定真是繁琐得不行……在什么范围允许留空,或者允许*,或者允许可变长度,Geez。还是要用到那么麻烦的东西的时候才去查C99规范的6.7.5.2好了。
C/C++的初始化器里元素的个数可以比对应维度的长度要少,此时剩余的元素与静态存储级的变量一样会被默认初始化(例如说算术类型的话会初始化为0)。
#include <stdio.h> void main() { int i, j; int arr[2][3] = { { 1, 2 }, { 4 } }; for (i = 0; i < 2; ++i) { for (j = 0; j < 3; ++j) { printf("%d\n", arr[i][j]); } } } // 1 // 2 // 0 // 4 // 0 // 0
C#和Java的初始化器就不能省略任何元素,必须与new表达式里指定的维度/长度匹配。
C#同样支持矩形数组。
using System; static class Program { static void Main( string[ ] args ) { var arr = new int[ 2, 3 ] { { 1, 2, 3 }, { 4, 5, 6 } }; // var arr = new int[ , ] { { 1, 2, 3 }, { 4, 5, 6 } }; Console.WriteLine( arr[ 0, 1 ] ); // 2 } }
下标的顺序与C/C++里的一样。new的时候要么所有维度都不指定长度,要么所有维度都要指定长度。指定长度的版本应用的是这条语法规则:
new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
不指定长度的应用的是则是另一条:
new array-type array-initializer
C#也支持所谓锯齿形数组(jagged array),它只要求总的维度数量与声明吻合,不要求每个维度的元素长度相等。
using System; static class Program { static void Main( string[ ] args ) { var arr = new int[ 2 ][ ] { new [ ] { 1, 2, 3 }, new [ ] { 4, 5, 6 } }; // var arr = new int[ ][ ] { new [ ] { 1, 2, 3 }, new [ ] { 4, 5, 6 } }; Console.WriteLine( arr[ 0 ][ 1 ] ); // 2 } }
C#规定锯齿形数组在new的时候,如果没有跟初始化器,则必须指定最外层的维度,其余的维度必须留空。如果跟有初始化器,则最外层的维度可以留空。
注意到锯齿形数组的初始化器无法用矩形数组的简写语法,内部的数组也必须写成new表达式。
顺带一记:.NET的数组可以分为SZArray和普通Array两种,前者是single-dimensional zero-based array,在CLI术语中也叫vector,只有这种数组有直接操作的MSIL/CIL指令;后者是多维数组或者下标不从0开始的数组,其相关操作都通过库方法来完成。
Java只支持锯齿形数组,多维数组实际上是数组的数组。
public class Program { public static void main(String[] args) { int[][] arr = new int[][] { { 1, 2, 3 }, { 4, 5, 6 } }; System.out.println(arr[0][1]); // 2 } }
在new表达式里,如果没有跟初始化器,则最外层的维度必须指定,其余的维度可以指定也可以留空。如果跟有初始化器,则所有维度都必须留空。
除了通过ArrayCreationExpression之外,Java还允许直接用ArrayInitializer来创建数组实例:
public class Program { public static void main(String[] args) { int[][] arr = { { 1, 2, 3 }, { 4, 5, 6 } }; System.out.println(arr[0][1]); // 2 } }
但ArrayInitializer必须在数组变量的声明中、或者作为ArrayCreationExpression的一部分来使用,而不能作为任意表达式使用。也就是说不能这样写:
int[] array; array = { 0 };
Java的数组最多只能有255维。在创建多维数组时如果只指定了最外层维度的长度,会使用newarray/anewarray指令;如果指定了多于一维的长度,则使用multianewarray指令。
Java的数组变量声明时可以将表示数组的方括号跟在元素类型后作为类型的一部分,也可以跟在变量名后作为变量声明的修饰;Java中惯用的写法是前一种。
主要就是这几种看起来很像的语言的数组微妙的不同让我总是弄混 T T
到底哪里必须指定,哪里必须留空,哪里是可指定可留空……|||
其实最关键的还是“什么是可以单独存在的对象”的问题吧。这里的对象指的是广义的对象。
C和C++里的多维数组是一个整体,代表一块连续的存储空间。
声明数组的时候,C/C++关心的是“要分配多少空间”。在没有初始化器时,当然只能通过指定所有维度的长度才能计算出要分配的空间大小。有初始化器时,可以通过初始化器中元素的个数来得到最外层维度的长度,所以可以给最外层维度的长度声明留空。内层维度必须指定长度这点恐怕是为了编译方便?
C#的矩形数组也是单一的对象,指向一块连续的存储空间。
C#和Java的锯齿形数组中每个维度都是连续的存储空间,但除了最内层的一维之外,其它维度的数组保存的是指向数组的引用。这些引用确实存在,而不像C/C++中取中间维度的地址时是算出来的。
由于数组长度不影响类型匹配(数组维度和元素类型才影响),如果数组的元素是指向数组的引用,那么这些元素指向的数组的长度是多少都可以。
所以C#不允许在锯齿形多维数组的new表达式中指定除最外层维度以外的维度长度。
Java……理由是一样的但为什么语法规则就是不同呢……
说来,最近才注意到LINQv1和LINQv2都不支持矩形多维数组的初始化……NewArrayInit只能用来初始化一维数组,嵌套使用可以初始化锯齿形多维数组,但矩形多维数组就只能用NewArrayBounds来创建新实例,却不支持对应的初始化。太郁闷了。
====================================================================
F#/OCaml的多维数组也是锯齿形数组,每个维度的元素长度可以不同。
let arr = [| [| 1 |]; [| 2; 3 |] |];; // val arr : int array array arr.[0];; // val it : int array
F#在访问数组元素的时候跟OCaml的不一样 OTL
let arr = [| [| 1 |]; [| 2; 3 |] |];; (* arr : int array array *) arr.(0);; (* - : int array *)
Standard ML没有数组的字面量。要用数组的话可以使用标准库里的array,也可以用许多SML实现所支持的vector。不过用下标访问数组元素的时候还是得用库函数:
#[#[1], #[2,3]]; (* val it = #[#[1],#[2,3]] : int vector vector *) Vector.sub (it, 0); (* val it = #[] : int vector *)
不过ML家族里的语言许多情况下用list和tuple就行了,毕竟不可变的数据类型在许多情况下就够用了。
Scala的数组也是锯齿形的。
def arr = Array(Array(1), Array(2, 3)) // Array[Array[Int]] arr(0) // Array[Int]
Haskell的数组跟SML一样是标准库里的,没有字面量。要使用数组的话需要import Array。要初始化一个无规则的数组看起来挺麻烦的:
arr = array (0,1) [(0,array (0,0) [(0,1)]),(1,array (0,1) [(0,2),(1,3)])] -- Array Integer (Array Integer Integer) arr!1!0 -- 2 :: Integer
内容有规则的数组的话,用lambda或者用list comprehension来初始化都挺方便的:
squares = array (1,100) [(i, i*i) | i <- [1..100]] squares = mkArray (\i -> i * i) (1,100)
至于像是Ruby、JavaScript这些动态语言的数组,本来也就没有多不多维的问题,反正数组里什么都能装,自然也能容纳数组作为元素。
说来Python里[...]语法所指的序列是叫list而不是array啊,不过支持的操作都一样,没差。而且有切片用真方便。
然后还有FORTRAN的数组?老爸以前倒是经常用FORTRAN,但我就从来没学过这历史悠久的语言。只是在用Python的NumPy库时留意到一段说明,说C的二维数组是行优先存储,而FORTRAN的是列优先存储的,多维同理。另外FORTRAN的数组的下标默认是从1开始的。
发表评论
-
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 22408(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局 (0): 拿在手上的是什么
2013-11-04 18:22 21508(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 ...
相关推荐
标题中的“@Power geez_omnet_”很可能是指一个与OMNeT++相关的项目或者工具,其中"Power geez"可能是项目名称或者是特定模块的标识。OMNeT++是一个开源的C++仿真框架,主要用于分布式系统、网络、并行和嵌入式系统...
:octopus: geez :computer_disk: 安装 npm i -g geez
我的网页:D笏 $ git submodule update --init$ gem install jekyll # jekyll? still, fil? geez$ jekyll build建筑学利用所有 AWS。域名系统AWS Route53CDN AWS 云前沿静态站点托管AWS S3 有一个为静态托管配置的 ...
I always thought if I worked hard, I’d be employable.... Geez, it is embarrassing to acknowledge, but after my “distinguished” job, (translation: twenty-five years as a in the wage-slave wheel) the
安德娜·卡伦德(Andegna Calender) 如果您想将埃塞俄比亚日历转换为任何其他日历系统(如公历),这是适合您的(经过良好测试,精心设计的高质量)软件包。 顺便说一下,它还支持Amharic日期格式等等。...