首页 / C# 入门教程 / 数组(一维/多维/交错)

C# 入门教程

数组(一维/多维/交错)

本教程共 100 篇 · 第 20 篇 · 更新于 2026-07-31 · 约 12 分钟阅读

C#C# 入门教程数组Array多维数组交错数组一维数组

20. 数组(一维/多维/交错)

本节目标:学完能声明并初始化一维数组、多维数组和交错数组,用索引读写元素,并用循环把它们遍历出来。

上一章讲了循环,循环最常被用来”对每个元素做一遍”。可元素放哪?总不能起 100 个变量名。数组(array)就是干这个的:它把同一类型的一批数据装进一个变量,用编号去取。

数组有三个关键特征,先记牢:

  1. 所有元素类型相同,比如全都是 int 或全是 string
  2. 长度(length)在创建时就固定了,之后不能直接伸缩。
  3. 编号从 0 开始,最后一个元素的编号是「长度减一」。

C# 里数组是引用类型,所以创建它要用 new。下面分三种来看:一维、多维、交错。

一、一维数组:最常用

一维数组(single-dimensional array)就是一条直线排开的一串元素。int[] 表示一个”装整数的数组”。

声明与初始化

只声明还不够,得用 new 真正在内存里开辟空间:

int[] numbers = new int[5];   // 长度为 5,元素默认都是 0

更省事的是直接把值列出来,让编译器自己数长度。C# 12 起的集合表达式(collection expression)写法最干净:

int[] numbers = [10, 20, 30, 40];   // 长度 4,依次赋值

也可以写成传统的数组初始化器(initializer),效果一样:

int[] numbers = new int[] { 10, 20, 30, 40 };
Tip

现代写法优先用 [...] 集合表达式。它不光能用于数组,下一章的集合 List<T> 也能用同一套语法,学一次处处通用。

索引访问

方括号里的数字叫索引(index)。第一个是 [0],第二个是 [1]

int[] numbers = [10, 20, 30];
Console.WriteLine(numbers[0]);   // 输出 10
numbers[1] = 99;                 // 把第二个元素改成 99
Console.WriteLine(numbers[1]);   // 输出 99

访问时索引不能越界。若写了 numbers[3](这里只有 3 个元素,编号最大是 2),运行时会抛出 IndexOutOfRangeException。新手不少 bug 就出在这。

想知道数组多长,用 Length 属性:

int[] numbers = [10, 20, 30];
Console.WriteLine(numbers.Length);   // 输出 3

遍历

遍历(traverse)就是把每个元素都访问一次。最常见两种:

int[] numbers = [10, 20, 30];

// 方法一:for 循环,需要下标时用
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"第 {i} 个是 {numbers[i]}");
}

// 方法二:foreach,只关心值、不关心位置时用
foreach (int n in numbers)
{
    Console.WriteLine(n * 2);   // 输出 20、40、60
}
Note

foreach 的特点是”只读地挨个过一遍”,它不会越界,也拿不到下标。需要按位置改元素或倒序遍历,就用 for

二、多维数组:整齐的表格

多维数组(multidimensional array,也叫矩形数组 rectangular array)可以理解成一张表。int[,] 是二维,像一个行×列的矩阵。

声明与初始化

int[,] matrix = new int[2, 3];   // 2 行 3 列,共 6 个元素,默认 0

直接填值也更直观,每行用一层花括号:

int[,] matrix = 
{
    { 1, 2, 3 },
    { 4, 5, 6 }
};

C# 12 的集合表达式同样适用,内层用 [...] 表示每行:

int[,] matrix = [[1, 2, 3], [4, 5, 6]];

方括号里逗号的数量 = 维数减一[,] 是二维,[,,] 就是三维,依此类推。

索引与遍历

访问元素要写两个索引:行和列,中间用逗号:

int[,] matrix = [[1, 2, 3], [4, 5, 6]];
Console.WriteLine(matrix[0, 1]);   // 输出 2(第 0 行第 1 列)
matrix[1, 2] = 99;                 // 把第 1 行第 2 列改成 99

多维数组遍历要用嵌套循环,每个维度一层。取某一维的长度得用 GetLength(维度编号),而不是 Length

int[,] matrix = [[1, 2, 3], [4, 5, 6]];

for (int i = 0; i < matrix.GetLength(0); i++)   // 行
{
    for (int j = 0; j < matrix.GetLength(1); j++)   // 列
    {
        Console.Write($"{matrix[i, j]} ");
    }
    Console.WriteLine();   // 一行结束换行
}
// 输出:
// 1 2 3
// 4 5 6
Note

对多维数组用 Length 拿到的是”所有元素总数”(这里是 6),不是行数。要分行取长度,必须用 GetLength(0) 取行、GetLength(1) 取列。

三、交错数组:每行长度可以不同

交错数组(jagged array)是”数组的数组”——外层数组的每一项,本身又是一个一维数组,而且每一行长度可以不一样int[][] 就是代表。

声明与初始化

先定好有几行,每行再单独创建:

int[][] jagged = new int[3][];      // 3 行,每行还是 null
jagged[0] = [1, 2, 3];              // 第 0 行 3 个元素
jagged[1] = [4, 5];                 // 第 1 行 2 个元素
jagged[2] = [6, 7, 8, 9];           // 第 2 行 4 个元素

也可以一步到位,用集合表达式把每行直接列出来:

int[][] jagged = [[1, 2, 3], [4, 5], [6, 7, 8, 9]];

注意每一行用两个方括号取值:第一个取”第几行”,第二个取”该行里的第几个”。

索引与遍历

int[][] jagged = [[1, 2, 3], [4, 5], [6, 7, 8, 9]];
Console.WriteLine(jagged[0][1]);   // 输出 2(第 0 行第 1 个)
jagged[1][0] = 40;                 // 把第 1 行第 0 个改成 40

// 外层循环走行,内层用 foreach 走该行元素
foreach (int[] row in jagged)
{
    foreach (int value in row)
    {
        Console.Write($"{value} ");
    }
    Console.WriteLine();
}
// 输出:
// 1 2 3
// 40 5
// 6 7 8 9

取行数用 jagged.Length(外层的行数),取某一行的元素数用 jagged[i].Length

四、多维 vs 交错,怎么挑

对比点多维数组 int[,]交错数组 int[][]
形状矩形,每行等长锯齿,每行可不等长
内存一块连续空间每行各是独立数组
索引matrix[i, j] 一个逗号jagged[i][j] 两层括号
适合场景规整的表格数据(如棋盘)不规则数据(如每个学生的选课数不同)

简单记:数据是整齐表格 → 用多维数组;每行长短不一 → 用交错数组。两者都能用嵌套循环遍历。

Tip

性能上,多维数组在连续内存里,缓存更友好;交错数组每行独立,灵活但稍多开销。初学阶段按”数据形状”选即可,别为微小性能提前优化。

五、新手常见坑

  1. 索引从 0 开始:长度为 n 的数组,最大合法索引是 n - 1。写 numbers[numbers.Length] 必然越界。
  2. 长度固定:数组创建后不能加长。想动态增删,学完本章后请看下一个章节的集合 List<T>
  3. 交错数组每行要先 newint[][] j 只建了外层,里层每行默认是 null,没初始化就访问会报空引用。
  4. 多维数组用 GetLength 而非 Length:前面强调过,遍历维度长度务必用对方法。

六、为什么数组长度固定(设计动机)

你也许会嘀咕:长度不能变的数组,用着多憋屈?这其实是用「灵活」换「速度」的取舍。

把数组想成银行墙里一排编号储物柜:柜子数量在装修时就钉死了,但好处是——你报一个号,管理员一步就能走到对应柜子,不用挨个找。数组在内存里就是「一块连续、等距排列」的空间,numbers[3] 的速度来自简单加法:起始地址 + 3 × 单个元素大小,一步直达(这种访问叫 O(1) 随机访问)。

一旦允许「中途加长」,这整齐的连续布局就被打破,要么整体搬家、要么碎片化。所以语言让数组保持固定,把「能伸缩」的任务交给下一章的 List<T>(它底层用数组,满了就自动扩容搬家)。一句话:数组 = 固定、快、简单;List = 灵活、自动扩容。按需求选。

七、默认值与引用类型数组

new 出来的数组,元素会被自动赋「类型的默认值(default value)」:数值类型是 0boolfalse,而引用类型(如 string、自定义类)是 null。这点特别容易踩坑:

string[] names = new string[3];   // 三个元素目前都是 null
Console.WriteLine(names.Length);  // 输出 3(数组本身非空)
// Console.WriteLine(names[0].Length);  // 危险!names[0] 是 null,会抛 NullReferenceException
names[0] = "小明";                  // 用之前先赋值
Console.WriteLine(names[0].Length); // 输出 2

引用类型数组装的其实是「引用(地址)」,不是对象本身。Person[] people = new Person[2]; 只是建了一个能放两个引用的槽,槽里还是 null,得 people[0] = new Person(); 才算真正创建对象。

Warning

数组创建后,未赋值的引用类型元素全是 null。在访问它们的属性或方法前,务必先判断或先赋值,否则运行时会炸 NullReferenceException。这正是第 16 章 ?? 和第 90 章可空引用类型要帮你兜住的场景。

八、复制数组:别直接赋值

数组是引用类型,int[] b = a; 不是复制,而是让 ba 指向同一块内存。改了 b[0]a[0] 也跟着变:

int[] a = [1, 2, 3];
int[] b = a;          // b 和 a 指向同一个数组
b[0] = 99;
Console.WriteLine(a[0]);   // 输出 99,原数组也被改了

想要「一份独立副本」,用 C# 的范围运算符(range operator)[..] 最简洁,或用 Array.Copy

int[] a = [1, 2, 3];
int[] copy = a[..];   // 复制出一份独立的新数组
copy[0] = 99;
Console.WriteLine(a[0]);   // 仍是 1,原数组不受影响
Note

a[..] 表示「从开头到结尾的全部元素」,生成一个新数组。范围语法还能截取片段,如 a[1..3] 取第 1、2 个元素。需要部分复制时它比手写循环方便。

要点速记

  • 一维 int[]:直线排布,numbers[0] 取值,Length 取长度。
  • 多维 int[,]:矩形表格,matrix[i, j] 取值,遍历用 GetLength(维度)
  • 交错 int[][]:每行不等长,jagged[i][j] 取值,每行是独立一维数组。
  • 声明初始化优先用集合表达式 [...];遍历一维用 for/foreach,多维和交错用嵌套循环。
  • 数组长度固定、元素是默认值;复制要用 [..]Array.Copy,别直接赋值。

动手练一练

  1. 用集合表达式建一个 int[],存 5 个成绩,用 foreach 算出总分并打印。
  2. 建一个 3×3 的 int[,] 矩阵,用嵌套 for 把主对角线(行号等于列号)上的元素都改成 1,其余为 0,并打印整张表。
  3. 建一个 int[][] 交错数组,三行分别有 2、4、3 个元素,遍历打印出”第几行有几个元素、分别是什么”。

下一章看数组的常用操作:排序、查找、复制、求最值,以及 Array 类上那些现成好用的方法。