前几天我正在阅读Kernighan 和 Rob Pike的《编程实践》 。
在第二章的“搜索”部分,我读到了其中一些让我感到困惑的台词。
“没有什么能比数组更好地存储静态表格数据。编译时初始化使得构造这样的数组变得便宜且容易。(在 Java 中,初始化发生在运行时,但这是一个不重要的实现细节,除非数组很大。 )"
我的问题是,如果用户仅在运行时提供数组或变量的编译时初始化,并且变量的内存分配发生在运行时,那么在任何语言中如何进行数组或变量的编译时初始化。在不知道内存地址的情况下如何初始化数组?
我想你误解了作者的意思。笔记:
这是一个静态数组,在 Java 中:
String[] suit = {
"item 1",
"item 2",
"item 3",
"item 4"
};
现在,Java 不允许您像 Delphi 和其他语言那样声明真正的动态数组,对于动态数组,我们必须选择另一个数据结构,ArrayList如下例所示:
List<String> list = new ArrayList<String>();
如果用户想使用运行时定义长度的静态数组,他可以做的最灵活的方法如下:
int maxsize = Integer.ParseInt(JOP.ShowInputDialog("give me a number")...);
int[] myArray = new int[maxsize]();
这是 Delphi 中的静态数组:
const MyStaticArray : array [0..3] of Integer = (0, 1, 2, 3);
这是动态数组
var MyDinamicArray : array of Integer;
MaxSize: Integer;
begin
MaxSize := StrToInt(InputBox(..,'Give me a number', ..));
SetLength(MyDinamicArray, MaxSize); //Defines the array size, in runtime;
end;
我的问题是,如果用户仅在运行时提供数组或变量的编译时初始化,并且变量的内存分配发生在运行时,那么在任何语言中如何进行数组或变量的编译时初始化。在不知道内存地址的情况下如何初始化数组?
也就是说,我们可以很容易地看到,这是一个“编译时”初始化(与实现细节无关)
String[] suit = {
"item 1",
"item 2",
"item 3",
"item 4"
};
一旦数组被初始化,它就不能被调整大小,所以操作系统可以在任何它想要的地方分配内存。而且由于数组在内存中是连续的,通过使用索引,Java 知道您想要获取的地址。
考虑到上面的数组,这是内存草图:
//程序存储器
address 00A1
value | 00BA |
alias suit
//操作系统内存
address 00BA 00BB 00BC 00BD
value | "item 1" | "item 2" | "item 3" | "item 4" |
alias suit[0] suit[1] suit[2] suit[3]
字符串在这里是为了方便理解,其实String也是一个指向某个东西的指针。
别名是Java隐藏指针算法的方式,即允许我们访问索引而不是内存地址。
以下是有关数组的一些文档:
它并没有说数据是在运行时提供的。它只是说“静态”数据。如果它在编译时是已知的,那么编译器可以将其直接编译到代码中。API 密钥、带有“幻数”的表格或错误消息文本都适合该模式。
从JLS
数组类型的变量保存对对象的引用。声明数组类型的变量不会创建数组对象或为数组组件分配任何空间。它只创建变量本身,它可以包含对数组的引用。
但是,声明器的初始化部分(第 8.3 节、第 9.3 节、第 14.4.1 节)可能会创建一个数组,然后对它的引用成为变量的初始值。