什么是数组?
想象一下有一排连号的、大小相同的储物柜,每个柜子都用来存放同一种类型的物品(比如都放书,或者都放鞋子)。
数组 (Array) 就是这样一个类似的概念:它是一个可以存储固定数量、相同数据类型元素的集合。这些元素在内存中通常是连续存放的(这有助于快速访问)。
数组具有许多特性:
- 固定大小: 在创建数组时,必须指定它能容纳多少个元素,并且这个大小之后不能改变。想增加或减少容量?你需要创建一个新的数组。
- 相同类型: 一个数组只能存储同一种数据类型的元素。你可以有一个 int 数组(存整数),一个 string 数组(存字符串),但不能在一个数组里既存 int 又存 string(除非它们都向上转型为 object,但我们暂时不考虑这个)。
- 索引访问: 数组中的每个元素都有一个唯一的索引,就像储物柜的编号一样。通过这个索引,我们可以精确地访问或修改对应的元素。
- 重要: C# (以及很多其他编程语言) 的数组索引是从 0 开始的!也就是说,第一个元素的索引是 0,第二个是 1,第三个是 2,以此类推。如果一个数组有 N 个元素,那么它的有效索引范围是 0 到 N-1。
声明数组
声明数组只是告诉编译器:“嘿,兄弟,我需要一个变量,它将来会引用一个某种类型的数组。”
基本语法:
dataType[] arrayName;
/*dataType: 数组中元素的数据类型 (如 int, string, double)。
[]: 方括号表示这是一个数组类型。
arrayName: 你给数组起的名字 (遵循变量命名规则)。*/
代码示例:
int[] scores; // 声明一个将引用 int 数组的变量
string[] studentNames; // 声明一个将引用 string 数组的变量
double[] temperatures; // 声明一个将引用 double 数组的变量
此时只是声明了变量,数组本身(那些“储物柜”)还没有在内存中被创建!scores, studentNames, temperatures 此时的值是 null(表示不引用任何对象)。
创建和初始化数组
声明之后,我们需要实际创建数组对象并分配内存空间。这通常使用 new 关键字。有几种方法:
方法一:指定大小创建 (元素获得默认值)
基本语法:
arrayName = new dataType[size]; //size: 你希望数组包含的元素个数 (一个正整数)。
代码示例:
scores = new int[5]; // 创建一个可以容纳 5 个 int 元素的数组
studentNames = new string[10]; // 创建一个可以容纳 10 个 string 元素的数组
默认值: 当你只指定大小时,数组中的每个元素会被自动初始化为其数据类型的默认值:
- 数值类型 (int, double, float, decimal 等): 0
- bool: false
- char: '\0' (空字符)
- 引用类型 (string, 其他类对象): null
所以,上面的 scores 数组创建后,里面包含 5 个 0。studentNames 数组创建后,里面包含 10 个 null。
方法二:声明的同时指定大小创建
这是方法一的简化写法:
dataType[] arrayName = new dataType[size];
代码示例:
double[] prices = new double[100]; // 声明并创建一个包含 100 个 double (默认值为 0.0) 的数组
方法三:使用数组初始化器 (直接提供初始值)
这是最常用、最方便的方法,尤其是在创建时就知道数组内容的情况下。编译器会自动计算数组的大小。
基本语法:
dataType[] arrayName = new dataType[size] { value1, value2, ..., valueN }; //完整
dataType[] arrayName = new dataType[] { value1, value2, ..., valueN }; //常用,省略大小
dataType[] arrayName = { value1, value2, ..., valueN }; //最简洁,省略new和类型
代码示例:
// 完整语法
int[] luckyNumbers1 = new int[4] { 7, 11, 13, 42 };
// 常用语法 (推荐)
string[] weekdays = new string[] { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" };
// 最简洁语法 (推荐,当类型已知时)
int[] luckyNumbers2 = { 7, 11, 13, 42 };
char[] vowels = { 'a', 'e', 'i', 'o', 'u' };
使用初始化器时,数组的大小由你提供的值的数量决定。
访问数组元素
使用数组名和方括号 [] 内的索引来访问或修改特定元素。
基本语法:
variable = arrayName[index]; //读取
arrayName[index] = newValue; //修改
代码示例:
string[] fruits = { "苹果", "香蕉", "橙子" };
// 读取元素 (索引从 0 开始!)
string firstFruit = fruits[0]; // firstFruit 的值是 "苹果"
string secondFruit = fruits[1]; // secondFruit 的值是 "香蕉"
Console.WriteLine($"第三种水果是:{fruits[2]}"); // 输出 "橙子"
// 修改元素
fruits[1] = "葡萄"; // 将索引为 1 的元素从 "香蕉" 改为 "葡萄"
Console.WriteLine($"现在第二种水果是:{fruits[1]}"); // 输出 "葡萄"
// 访问一个刚创建、只有默认值的数组
int[] numbers = new int[3]; // 包含 { 0, 0, 0 }
Console.WriteLine($"第一个数字是:{numbers[0]}"); // 输出 0
numbers[0] = 100; // 修改第一个元素
Console.WriteLine($"修改后第一个数字是:{numbers[0]}"); // 输出 100
索引越界
试图访问一个不存在的索引会导致运行时错误:
System.IndexOutOfRangeException
int[] myNums = { 10, 20, 30 }; // 有效索引是 0, 1, 2
Console.WriteLine(myNums[0]); // OK
Console.WriteLine(myNums[2]); // OK
// 下面这两行会抛出 IndexOutOfRangeException 错误!
// Console.WriteLine(myNums[3]); // 错误!索引 3 不存在
// Console.WriteLine(myNums[-1]); // 错误!索引不能是负数
记住:最大有效索引是 数组长度 - 1
获取数组长度
使用数组的 Length 属性(注意,不是方法,后面没有括号 ())可以获取数组中元素的总数。
基本语法:
arrayName.Length
代码示例:
string[] colors = { "红", "绿", "蓝" };
int count = colors.Length; // count 的值是 3
Console.WriteLine($"颜色数组中有 {count} 个元素。");
double[] data = new double[50];
Console.WriteLine($"数据数组的大小是:{data.Length}"); // 输出 50
Length 属性在循环遍历数组时非常有用。
遍历数组 (使用循环)
我们经常需要处理数组中的每一个元素。这时就需要结合使用循环和数组访问。
方法一:使用 for 循环 (最常用,需要索引时)
for 循环非常适合遍历数组,因为它允许我们通过索引访问每个元素,并且可以使用 Length 属性作为循环的结束条件。
int[] scores = { 85, 92, 78, 95, 88 };
Console.WriteLine("学生成绩列表:");
// 循环从索引 0 开始,到 Length - 1 结束
for (int i = 0; i < scores.Length; i++)
{
// 使用索引 i 访问每个元素
Console.WriteLine($"第 {i + 1} 位学生的成绩:{scores[i]}"); // i+1 是为了显示 1, 2, 3...
}
// 计算总分
int totalScore = 0;
for (int i = 0; i < scores.Length; i++)
{
totalScore += scores[i]; // 累加每个分数
}
double averageScore = (double)totalScore / scores.Length; // 注意转换为 double 进行除法
Console.WriteLine($"总分:{totalScore}");
Console.WriteLine($"平均分:{averageScore:F2}"); // :F2 格式化输出,保留两位小数
循环条件是 i < scores.Length,确保 i 的最大值是 Length - 1,不会越界。
方法二:使用 foreach 循环 (更简洁,只读访问时)
如果你只需要读取数组中的每个元素,而不需要知道它的索引,foreach 循环通常更简洁易读。
基本语法:
foreach (dataType elementVariable in arrayName)
{
// 在这里使用 elementVariable (它是当前迭代的元素的值)
}
代码示例:
string[] pets = { "猫", "狗", "兔子" };
Console.WriteLine("我的宠物们:");
foreach (string pet in pets) // 对于 pets 数组中的每一个 string 元素,将其赋值给 pet 变量
{
Console.WriteLine(pet);
// 注意:在 foreach 循环内部通常不应该 (或不能直接) 修改集合本身 (pets 数组)。
// pet = "仓鼠"; // 这只会改变 pet 变量的副本,不会改变原数组 pets
}
foreach 会自动处理遍历过程,你不需要关心索引或边界条件,更不容易出错,代码也更清晰。但是,如果你需要在循环中修改数组元素,或者需要知道当前元素的索引,那么还是需要用 for 循环。
学了那么久,该自己实践了!
一样的,可以选择创建新项目或者在已有项目中练习:
- 创建并打印数组:
- 创建一个 string 数组,包含你最喜欢的三种水果。
- 使用 Console.WriteLine 打印数组的长度。
- 使用 Console.WriteLine 打印第一个和最后一个水果(记得索引)。
- 使用 foreach 循环打印出所有水果的名称。
- 数字数组操作:
- 创建一个包含 5 个 int 类型成绩的数组 (可以自己随意指定成绩)。
- 使用 for 循环找到并打印出数组中的最高分。 (提示: 需要一个变量来记录当前找到的最高分)。
- 用户输入到数组:
- 创建一个大小为 3 的 string 数组 userInputs。
- 使用 for 循环,提示用户输入三次,每次输入的内容存入数组的对应位置 (userInputs[0], userInputs[1], userInputs[2])。
- 输入完成后,使用 foreach 循环将用户输入的内容全部打印出来。
Comments NOTHING