1. 数组——元素的容器
你大概率听说过,计算机能处理巨量的信息。条件语句(if-else)和循环(for,while)当然很有用。但光靠它们还不够,因为数据得有地方存。
这时候C#(其实几乎所有编程语言都有)就有个超好用的东西叫数组(Array)。有时候也叫表格。
数组其实就是个特殊的对象,可以存不止一个值,而是好几个。
以前我们把变量比作盒子(能存一个值),那数组就像是盒子里分了好几个格子。每个格子在“盒子-数组”里都有自己的编号。编号当然是从0开始的……
还可以再打个比方。普通住宅和高层公寓。普通住宅住一户,高层公寓分成很多房间。给普通住宅写信只要写唯一地址,给公寓的住户写信得写公寓地址还要加房间号。
所以,数组变量就像高层公寓变量。它能存不止一个值。这样的变量里有好几个房间(单元格),每个都能通过编号(索引)访问。
要访问某个单元格,只要在变量名后面加中括号写上索引就行。很简单:
array[索引] = 值;
这里array是数组变量名,索引是数组里的单元格编号,值就是你想放进去的内容。
不过我们先来看看,数组怎么创建。
2. 在C#里创建数组
假设你的程序需要存100个整数。数组就很合适。那怎么创建呢?
如果只想存一个整数,用int类型就行,但要存100个整数,肯定得用int类型的数组。代码长这样:
int[] array = new int[100];
来拆解一下这行代码。
你应该能看出来,等号左边是声明了个叫array的变量,类型是int[]。int后面加中括号,意思就是这种“盒子”能存好几个值。
等号右边是“创建对象”(new),有100个元素(单元格),类型是int。也不难懂。
如果想创建20个单元格的浮点数数组,代码大概这样:
double[] vals = new double[20];
数组的单元格数量叫数组大小或者数组长度。因为能存很多值,数组也叫容器。
重点:数组容器的大小创建后不能改。
你可以新建一个,但已经建好的数组长度是改不了的。
3. 操作数组单元格
好啦,数组会创建了,那怎么用呢?
其实和普通变量差不多,只是用数组变量名时一定要加上你要操作的单元格编号。
数组的单元格编号永远从0开始。如果数组有10个元素,编号(索引)是0..9;如果有200个元素,就是0..199。以此类推。
举几个例子:
int[] a = new int[10]; // 创建10个int类型的数组。
a[2] = 4; // 把4放进索引为2的单元格。
a[7] = 9; // 把9放进索引为7的单元格。
a[9] = a[2] + a[5]; // 把索引2(是4)和索引5(是0)的值相加,结果放进索引9。
执行完上面代码后,内存里会长这样:
左边灰色那列是单元格编号(索引)。单元格里存的就是你放进去的值:4、9和4。数组刚创建时,所有单元格都是0。
注意。数组所有单元格类型都一样。如果你建的是string数组,只能存字符串。数组的数据类型在创建时就定了,类型和长度都不能再改。
4. 数组在内存中的样子
前面例子的图其实有点不准确。
创建数组(和字符串一样)时,内存里会分配两个块:一个存数组(容器)本身,另一个存变量,变量里放地址。更准确的图在下面:
绿色的是10个int元素的数组,还有一个int[]类型的变量,变量里存着地址(引用),指向内存里的int数组。
蓝色的是普通int变量,存着199。
是不是有点像字符串在内存里的存法?
没错,字符串也是。像操作字符串那样,“数组类型变量”也可以互相赋值:
int[] a = new int[10]; // 创建10个int类型的数组。
a[2] = 4; // 把4放进索引2。
a[7] = 9; // 把9放进索引7。
int[] b = a; // 把a里的地址赋给b。
// 现在a和b都指向同一个数组对象。
a[9] = b[2] + a[7]; // 把b[2](4)和a[7](9)相加,结果放进a[9]。
数组对象还是在原地,a和b变量都存着同一个地址(引用)。看图:
5. 更细致地玩数组
数组可以用任何类型创建。只要在类型名后面加中括号。通用写法如下:
类型[] 名字 = new 类型[数量];
类型是你要存的元素(单元格)类型,名字是变量名,数量是数组单元格数。
上面例子是标准写法:声明数组变量+创建数组对象。其实这俩可以分开写:
类型[] 名字;
名字 = new 类型[数量];
还有个很重要的点
数组的索引和元素数量都可以用变量,甚至表达式。
举几个例子:
int n = 100;
int[] a = new int[n]; // 创建n个元素的数组
int n = 100;
int[] a = new int[n * 2 + 3]; // 创建203个元素的数组
int n = 100;
int[] a = new int[n];
a[n-1] = 2; // a[99] = 2;
a[n-2] = 3; // a[98] = 3;
a[n/5] = a[n-1] + a[n-2]; // a[20] = a[99] + a[98];
数组越界
注意,如果你试图访问数组里没有的索引(比如我们这个例子里除了0..99以外的所有整数),程序会直接报错IndexOutOfRangeException——索引超出数组范围。
6. 数组长度
刚才的例子里,我们可以先声明一个数组变量,然后在代码里再给它赋值(指向数组对象)。甚至可以这样写:
int[] array; // 声明int[]类型的数组变量
if (a < 10) // 如果a小于10
array = new int[10]; // 就创建10个元素的数组
else // 否则
array = new int[20]; // 创建20个元素的数组
那怎么知道这个数组有多少元素?
数组有个专门的属性(变量)——length。想知道数组长度,用这个表达式:
array.Length;
array是数组变量名,Length是数组对象的属性。Length的值不能改:你可以把它赋给别的变量,但不能给它赋值(否则编译不过)。
可以这样继续上面的例子:
int[] array; // 声明int[]类型的数组变量
if (a < 10) // 如果a小于10
array = new int[10]; // 就创建10个元素的数组
else // 否则
array = new int[20]; // 创建20个元素的数组
for (int i = 0; i < array.Length; i++) // 遍历数组所有元素:从0到array.Length-1
{
Console.WriteLine(array[i]);
}
7. C#数组小结
来总结下关于数组的知识点:
- 知识点1. 数组由很多单元格组成。
- 知识点2. 访问某个单元格要用它的编号。
- 知识点3. 所有单元格类型一样。
- 知识点4. 所有单元格初始值是0和null(如果存的是地址),false(bool类型)。
- 知识点5. string[] list只是声明变量:容器(数组对象)还没创建。要用它,得先创建数组(容器)并赋给这个变量,然后才能用。见下面例子。
- 知识点6. 创建数组对象(容器)时要指定长度——有多少单元格。写法:new TypeName[n];
- 知识点7. 数组长度可以通过.Length属性获取。
- 知识点8. 数组创建后,元素类型和数量都不能改。
string s; // s是null
string[] list; // list是null
list = new string[10]; // list变量存着一个10个元素的字符串数组对象的引用
int n = list.Length; // n是10
list = new string[0]; // 现在list是0元素的数组
// 数组有了,但不能存元素
list = null;
Console.WriteLine(list[1]); // 程序会报错——空引用
// list是空引用——null
list = new string[10];
Console.WriteLine(list[10]); // 程序会报错——数组越界
// 如果list有10个元素/单元格,合法索引是:0 1 2 3 4 5 6 7 8 9 ——一共10个。
GO TO FULL VERSION