1数组是什么
数组是一组相同类型的元素,个数固定,按顺序排好。不用声明 number0 到 number99 一百个变量,只要一个名字 numbers,再用编号区分每一个,这个编号叫索引,从 0 开始。
数组的元素在内存里挨着放、中间没有空隙:地址最低的是第一个元素,最高的是最后一个。视频里把它比成一条长托盘,一格放一个元素,格子一样大(类型相同),两头有挡板(个数固定)。
2声明数组
写法是:元素类型,加一对方括号 [],再加数组名。
| 8 | double[] balance; | // 声明一个装 double 的数组,名字叫 balance |
方括号说明这是个数组,它决定的是数组的秩,也就是维数:一对空方括号就是一维,大小要等创建时才定。
3用 new 创建数组
很多人以为声明完就能往里放东西,真跑一下会编译报错:
| 8 | double[] balance; | // 只声明了变量,数组本身还不存在 |
| 9 | balance[0] = 4500.0; | // 往 0 号元素里放值 |
Program.cs(9,10): error CS0165: 使用了未赋值的局部变量“balance”
数组是引用类型,要用 new 创建实例(造出来的具体对象):
| 8 | double[] balance = new double[10]; | // 在内存里造出 10 格的数组,balance 指向它 |
变量 balance 里放的是指向这个数组的引用(记的是地址)。
4给元素赋值
用索引给单个元素赋值:0 号格的旧值换成 4500,其余九格不受影响。
| 9 | balance[0] = 4500.0; | // 把 4500.0 放进 0 号元素 |
5声明的同时赋值
可以在声明时直接给出全部元素,有三种写法:
| 8 | double[] balance = { 2340.0, 4523.69, 3421.0}; | // 第一种:花括号里列 3 个值,数组就有 3 个元素 |
| 9 | int [] marks = new int[5] { 99, 98, 92, 97, 95}; | // 第二种:new int[5],后面正好 5 个值 |
| 10 | int [] marks2 = new int[] { 99, 98, 92, 97, 95}; | // 第三种:方括号里不写大小,按值的个数定为 5 |
打印三个数组的长度(.)得到 3、5、5,balance[1] 是 4523.69。
第二种写法个数必须对上,只写 3 个值就编译报错:
| 8 | int [] marks = new int[5] { 99, 98, 92}; | // 说好 5 个,只给了 3 个 |
Program.cs(8,37): error CS0847: 应为一个长度为“5”的数组初始值设定项
6把数组赋给另一个数组变量
score = marks 之后,两个变量指向同一个数组,没有复制。通过 score 改 0 号元素,marks 看到的也变了。
| 8 | int [] marks = new int[] { 99, 98, 92, 97, 95}; | // marks 指向一个 5 个元素的数组,0 号是 99 |
| 9 | int[] score = marks; | // score 拿到同一个引用,数组还是那一个 |
| 10 | score[0] = 60; | // 通过 score 把 0 号改成 60 |
| 11 | Console.WriteLine(marks[0]); | // 通过 marks 读 0 号:输出 60 |
7默认值
创建数组时,每个元素会自动设成类型的默认值,数字就是 0,所以 int 数组一开始也全是 0。默认值是程序运行时、创建数组的那一刻设好的。
| 11 | int[] marks = new int[5]; | // 5 个元素,还没赋值,全是 0 |
8访问数组元素
读取元素也是数组名加方括号写索引。
| 10 | double salary = balance[9]; | // 取出最后一格(9 号),还没赋过值 |
| 11 | int[] marks = new int[5]; | // 新建 int 数组,元素默认是 0 |
| 12 | Console.WriteLine(balance[0]); | // 0 号刚赋了 4500:输出 4500 |
| 13 | Console.WriteLine(salary); | // 9 号是默认值:输出 0 |
| 14 | Console.WriteLine(marks[0]); | // int 数组的 0 号:输出 0 |
| 15 | Console.WriteLine(balance.Length); | // Length 是元素个数:输出 10 |
查看完整代码(18 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | namespace ArrayApplication | // 定义命名空间 ArrayApplication |
| 3 | { | // ArrayApplication 命名空间开始 |
| 4 | class MyArray | // 定义 MyArray 类 |
| 5 | { | // MyArray 类开始 |
| 6 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 7 | { | // Main 开始 |
| 8 | double[] balance = new double[10]; | // 创建 10 个元素的 double 数组,全是 0 |
| 9 | balance[0] = 4500.0; | // 0 号元素设为 4500 |
| 10 | double salary = balance[9]; | // 读 9 号元素:还是 0 |
| 11 | int[] marks = new int[5]; | // 创建 5 个元素的 int 数组,全是 0 |
| 12 | Console.WriteLine(balance[0]); | // 输出 4500 |
| 13 | Console.WriteLine(salary); | // 输出 0 |
| 14 | Console.WriteLine(marks[0]); | // 输出 0 |
| 15 | Console.WriteLine(balance.Length); | // 输出长度 10 |
| 16 | } | // Main 结束 |
| 17 | } | // MyArray 类结束 |
| 18 | } | // ArrayApplication 命名空间结束 |
长度是 10,索引只能从 0 到 9。写 n[10] 呢?
| 8 | int [] n = new int[10]; | // 10 个元素,索引 0~9 |
| 9 | Console.WriteLine(n[9]); | // 9 号是最后一个,输出 0 |
| 10 | Console.WriteLine(n[10]); | // 没有 10 号,运行到这里出错 |
0 Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array. at ArrayApplication.MyArray.Main(String[] args) in 数组/out-of-range/Program.cs:line 10
编译能过,一运行就抛出异常:索引超出了数组的范围。
9完整例子:用 for 循环赋值和输出
先声明能放 10 个整数的数组 n,第一个 for 循环把每格设为 i + 100,第二个按索引逐个打印。
| 12 | for ( i = 0; i < 10; i++ ) | // i 从 0 走到 9 |
| 13 | { | // 赋值 for 循环开始 |
| 14 | n[ i ] = i + 100; | // 第 i 格设为 i + 100:0 号是 100……9 号是 109 |
| 15 | } | // 赋值 for 循环结束 |
| 16 | | |
| 17 | /* 输出每个数组元素的值 */ | // 教程自带的块注释:下面开始输出 |
| 18 | for (j = 0; j < 10; j++ ) | // j 从 0 走到 9 |
| 19 | { | // 输出 for 循环开始 |
| 20 | Console.WriteLine("Element[{0}] = {1}", j, n[j]); | // {0} 换成索引 j,{1} 换成元素 n[j] |
| 21 | } | // 输出 for 循环结束 |
查看完整代码(25 行)
| 1 | using System; | // 引入 System 命名空间 |
| 2 | namespace ArrayApplication | // 定义命名空间 ArrayApplication |
| 3 | { | // ArrayApplication 命名空间开始 |
| 4 | class MyArray | // 定义 MyArray 类 |
| 5 | { | // MyArray 类开始 |
| 6 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 7 | { | // Main 开始 |
| 8 | int [] n = new int[10]; /* n 是一个带有 10 个整数的数组 */ | // 创建 10 个元素的 int 数组 |
| 9 | int i,j; | // 声明两个循环变量 i、j |
| 10 | | |
| 11 | /* 初始化数组 n 中的元素 */ | // 教程自带的块注释:下面开始赋值 |
| 12 | for ( i = 0; i < 10; i++ ) | // i 从 0 走到 9 |
| 13 | { | // 赋值 for 循环开始 |
| 14 | n[ i ] = i + 100; | // 第 i 格设为 i + 100 |
| 15 | } | // 赋值 for 循环结束 |
| 16 | | |
| 17 | /* 输出每个数组元素的值 */ | // 教程自带的块注释:下面开始输出 |
| 18 | for (j = 0; j < 10; j++ ) | // j 从 0 走到 9 |
| 19 | { | // 输出 for 循环开始 |
| 20 | Console.WriteLine("Element[{0}] = {1}", j, n[j]); | // 打印索引和元素值 |
| 21 | } | // 输出 for 循环结束 |
| 22 | Console.ReadKey(); | // 等待按键(现在的 VS 不写也会停住) |
| 23 | } | // Main 结束 |
| 24 | } | // MyArray 类结束 |
| 25 | } | // ArrayApplication 命名空间结束 |
查看完整输出(10 行)
Element[0] = 100 Element[1] = 101 Element[2] = 102 Element[3] = 103 Element[4] = 104 Element[5] = 105 Element[6] = 106 Element[7] = 107 Element[8] = 108 Element[9] = 109
10用 foreach 遍历数组
foreach 每转一圈,j 就依次拿到数组里的一个元素。注意 j 是元素的值,不是索引,所以教程用 j - 100 把编号算回来,输出和上面 for 循环完全一样。
| 18 | foreach (int j in n ) | // 依次取出 n 里的每个元素放进 j:100、101……109 |
| 19 | { | // foreach 循环开始 |
| 20 | int i = j-100; | // j 是值不是索引,减 100 算回编号 |
| 21 | Console.WriteLine("Element[{0}] = {1}", i, j); | // 打印编号和元素值 |
| 22 | } | // foreach 循环结束 |
foreach 里不能给 j 赋值,要按位置改元素,还是用 for 加索引:
| 9 | foreach (int j in n ) | // j 依次拿到每个元素 |
| 10 | { | // foreach 循环开始 |
| 11 | j = 5; | // 想给 j 赋值 |
| 12 | } | // foreach 循环结束 |
Program.cs(11,13): error CS1656: 无法为“j”赋值,因为它是“foreach 迭代变量”
11数组的更多概念
教程最后列了五个和数组有关的概念,逐个点名。
多维数组
最简单的是二维,像一张表,按行和列取值。下面两行三列,第二行第三列是 6;它有两个维度,所以秩是 2。
| 8 | int[,] a = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } }; | // [,] 表示二维:2 行 3 列 |
| 9 | Console.WriteLine(a[1, 2]); | // 第 1 号行、第 2 号列(第二行第三列):6 |
| 10 | Console.WriteLine(a.Rank); | // Rank 是秩(维数):2 |
交错数组
交错数组是数组的数组:每一行都是一个独立的数组,长短可以不一样。
| 8 | int[][] scores = new int[2][]; | // [][]:外层 2 行,每行先空着 |
| 9 | scores[0] = new int[] { 92, 93, 94 }; | // 第一行是 3 个元素的数组 |
| 10 | scores[1] = new int[] { 85, 66 }; | // 第二行是 2 个元素的数组 |
| 11 | Console.WriteLine(scores[0].Length); | // 第一行长度 3 |
| 12 | Console.WriteLine(scores[1].Length); | // 第二行长度 2 |
| 13 | Console.WriteLine(scores[1][0]); | // 第二行的 0 号:85 |
传递数组给函数
调用时只写数组名,不带索引。C# 传的是数组的引用,所以方法里改 0 号元素,外面的 n 也跟着变。
| 6 | static void Change(int[] arr) | // 参数 arr 是 int 数组 |
| 7 | { | // Change 开始 |
| 8 | arr[0] = 99; | // 把 0 号元素改成 99 |
| 9 | } | // Change 结束 |
| 10 | static void Main(string[] args) | // 程序入口 Main 方法 |
| 11 | { | // Main 开始 |
| 12 | int[] n = { 10, 20, 30 }; | // n 的 0 号是 10 |
| 13 | Change(n); | // 只写数组名:arr 和 n 指向同一个数组 |
| 14 | Console.WriteLine(n[0]); | // 外面再看 0 号:输出 99 |
参数数组
参数前加 params,调用时想传几个值就传几个,方法里拿到的是一个数组。
| 6 | static int Sum(params int[] nums) | // params:传进来的几个数装进数组 nums,方法里用 foreach 加起来 |
| 17 | Console.WriteLine(Sum(1, 2, 3)); | // 传 3 个数:1 + 2 + 3,输出 6 |
| 18 | Console.WriteLine(Sum(10, 20)); | // 传 2 个数:10 + 20,输出 30 |
Array 类
Array 类在 System 命名空间里,是所有数组的基类(被继承的上一级类,后面会讲)。数组的 Length 属性(后面会讲),还有排序 Sort、反转 Reverse,都来自它。
| 8 | int[] list = { 34, 72, 13, 44, 25 }; | // 5 个没排序的数 |
| 9 | Array.Sort(list); | // 从小到大排序 |
| 10 | Console.WriteLine(string.Join(" ", list)); | // string.Join 用空格连成一行:13 25 34 44 72 |
| 11 | Array.Reverse(list); | // 倒过来 |
| 12 | Console.WriteLine(string.Join(" ", list)); | // 输出 72 44 34 25 13 |
| 13 | Console.WriteLine(list.Length); | // 长度不变:输出 5 |
12小结
- 数组是一排相同类型、个数固定、挨着放的格子。
- 先声明,再用
new创建;新元素是默认值,索引从 0 到长度减 1。 - 数组变量放的是引用:赋给别的变量或传给方法,指向的还是同一个数组。
下一节:一串文字,比如名字、一句话,在 C# 里又是怎么存、怎么拼接的?
