c#笔记-数组

数组

声明数组

数组是把多个同类型变量打包的类型。所以声明一个数组,相当于让c#替你同时声明多个变量。

var arr1 = new int[10];//构造一个int数组,它里面有10个int

如果你一开始就能决定好所有元素的数量和值,那么可以使用字面量进行声明。
字面量必须指定变量类型,不能使用var进行判断。

int[] arr2 = [ 4, 5, 6, 7, 8, 9 ];

尽管这称作字面量,但他依然是通过构造产生的。因此不能作为常量值。

多维数组

一个方括号放在类型后面表示这种类型的数组。
并且,由于数组也是有效的类型,所以有数组的数组。

int[][] arr3 =new int [10][];//这是数组的数组。数量写在最后一个方括号里。

除此之外,还有多维数组,他在一个方括号内添加逗号表示维度的数量。

int[,] arr4 =new int [4 ,5];//这是二维的多维数组

上述例子中,多维数组会创建20个int
而数组的数组会创建10个int []。并且这10个数组还需要你之后再自己进行构造和赋值。
所以这10个数组不一定是一样多的。

访问元素

数组的元素通过索引器访问。使用中括号表示。

int[] arr11 = new int[4];
arr11[0] = 12;
arr11[1] = 16;
arr11[2] = 64;
arr11[3] = arr11[0] + arr11[1];
Console.WriteLine(arr11[3] - arr11[2]);

索引器中要求的参数是Index类型。
不过所有的int类型都可以直接转为Index类型。

Index index11 = 1;//表示正数第2个。索引从0开始计数,所以1是第二个。
Index index12 = ^1;//在前面加^表示总数量-1,也就是倒数第1个。

int i11 = 1;
Index index13 = i11;//Index可以直接接受int作为值
Index index14 = ^i11;//也可以在int变量前面加^

遍历

索引器接受的参数也可以使用变量。
这样就可以配合循环来遍历数组。
对于普通数组,使用Length获取元素数量作为条件。

int[] arr12 = new int[10];
for (int i = 0; i < arr12.Length; i++)
{
    arr12[i] = i * i;
}
for (int i = 0; i < arr12.Length; i++)
{
    Console.WriteLine(arr12[i]);
}

对于多维数组,使用GetLength()方法获取指定维度的元素数量作为条件。

int[,] arr13 = new int[4, 5];
for (int i = 0; i < arr13.GetLength(0); i++)//获取维度也是从0开始计数
{
    for (int j = 0; j < arr13.GetLength(1); j++)
    {
        arr13[i, j] = i * j;
    }
}
  • ^1不是运算符,而是语法糖。它要求编译器构造一个Index类型的值。
  • ^1不是字面量,而是指令。所以无法声明Index的常量

数组长度

索引越界

对数组元素的访问,只能访问有效索引。
有效索引从0开始数,总数量等于数组长度。所以最大的有效索引为数组长度-1

而对于Index类型来说,^1表示倒数第一个元素。相当于arr[arr.Length - 1]
Index的倒数最大有效值为数组的Length

无论使用int还是Index,如果访问了非法的索引,那么或报错索引越界
数组长度可以为0,此时任何索引都是非法的。

长度固定

之前的类型都是简单类型。他们没有内容。
任何对值的修改都是通过修改变量本身来完成的。

而数组的元素,是数组的内容
你可以在不改变数组变量的情况下改变他的内容。
但如果你要改变数组元素的数量,你就必须把这个数组变量给重新赋值。

也因为数组的元素是数组的内容,所以你在使用他们前,不需要像变量一样先赋值初始值。

指针

引用类型

如果你直接用一个数组变量来给另一个数组变量赋值,你会发现他们会窜号。

int[] arr21 = new int[1];
int[] arr22 = arr21;
Console.WriteLine(arr21[0]);//0
Console.WriteLine(arr22[0]);//0
arr21[0] = 12;
Console.WriteLine(arr21[0]);//12
Console.WriteLine(arr22[0]);//12

这是因为数组是引用类型
他的值表明内容储存在哪,而自己不直接存储内容。

内容是指通过点出来,或通过索引器访问的东西。

解指针

表明内容储存在哪这个东西可以称作一个指针。
类似于你桌面上的快捷方式,或者你看视频时分享给好友的视频链接。

就像你发网址而不是下载视频发视频。引用类型通常是内容非常大的东西。
比如数组能轻易声明几百万个int。完全复制会占用非常多的资源。

引用类型和值类型的区别在于,他的值是否连他的内容一起包含。
赋值的操作本质是一个复制。值类型因为储存内容,所以他的内容会一起复制。

引用类型因为自己不储存内容,所以如果想要访问他的内容
就要顺着内容储存在哪这个信息去找到实际的内容。

那么如果引用类型直接复制,他们找到的内容就是同一份。
所以一方修改值,另一方就也会看到值被修改了。

托管类型

托管是指.Net可以控制的东西,比如分配,监视,回收,释放。
因此引用类型的指针值的更新(清理垃圾的时候会移动内容),
创建指针和解指针的操作,完全不需要你操心。

c#真正的指针需要通过修改配置来启用。
这些不正规的指针,都是托管指针。都是安全的。

引用变量

引用变量可以创建对变量的指针。对引用变量的任何操作都会解指针后对原变量进行操作。

int i31 = 6;
ref int ri31 = ref i31;//必须声明时赋值
Console.WriteLine(i31);//6
Console.WriteLine(ri31);//6,相当于访问i1
ri31 = 10;//直接赋值会作用到原变量上
Console.WriteLine(i31);//10,会被修改
Console.WriteLine(ri31);//10

int[] arr31 = new int[6];
ri31 = ref arr31[0];//可以重新赋值
Console.WriteLine(arr31[0]);//0
ri31 = 50;
Console.WriteLine(arr31[0]);//50

默认值

数组创建后,它里面的元素是默认值。
对于指定的类型可以使用字面量default获得默认值。
对于没有指定的类型,可以在default后加个括号指定类型。

bool b = default;
Console.WriteLine(default(char));

默认值的逻辑是,对这个类型所占据的内存都设置为0

  • 数字类型会被解读0
  • char会被解读为一个字符编码为0的字符。
  • bool会被解读为false
  • 引用会被解读为null

null也是一个字面量,表示不引用任何数据。所有的引用类型都可以使用null值。

int[][] arr13 = new int[5][];//数组是一个有效的类型,所以数组也可以有数组
for (int i = 0; i < arr13.Length; i++)
{
	arr13[i] = new int[6];//数组的默认值是null,无法使用
}
arr13[2][3] = 8;

null值不能执行解指针操作。null值变量本身可以使用,访问,比较。但不能访问内容。

string s1 = null;//字符串也是引用类型
Console.WriteLine(s1 == null);//仅访问变量
Console.WriteLine(s1 == "hello");//仅访问变量
Console.WriteLine("hello".Length);//获取内容,字符串的长度
Console.WriteLine("hello"[2]);//获取内容,第3个字符
Console.WriteLine(s1.Length);//尝试获取null的内容,报错
Console.WriteLine(s1[2]);//尝试获取null的内容,报错

字符串是不可改变的值。尽管可以通过索引访问他的字符,
但不能对他的字符进行赋值。任何对字符串的修改都要通过修改他的变量来完成。
字符串是引用类型,和数组有着相同的判断逻辑,即判断指针。
但是.Net会控制字符串的生成,并回收他们。每当你创建字符串时,
都会从记录的字符串里看看有没有一样的,如果有就给你现成的。所以字符串可以直接进行比较。

截取数组

复制

如果想复制数组的一小段内容,可以直接使用Range作为索引。
Index类似,他可以使用特殊语法构造,但不是常量。

int[] arr41 = [ 4, 5, 6, 7, 8, 9 ];
var arr42 = arr41[0..^3];
for (int i = 0; i < arr42.Length; i++)
{
    Console.WriteLine(arr42[i]);//4,5,6
}

Range包含起始索引,不包含结束索引,即arr41[^3]并不在arr42中。
Range可以省略开头或尾的索引,这样的话就表示从开头一直取,或者一直取到结尾。
也可以同时省略开头和结尾的索引,表示复制整个数组。

int[] arr43 = { 4, 5, 6, 7, 8, 9 };
var arr44 = arr43[..];
arr43[0] = 14;
Console.WriteLine(arr44[0]);//4

这个语法也可以作用于字符串。
另外,Range的构造语法有个缺点,他的优先级太高了。
如果要对首尾的索引进行修正,需要打括号。

string s41 = "hello2.png";
string s42 = s41[..(s41.Length - 3)];

引用

引用类型的数据由.Net负责分配,监视,回收,释放。这个流程消耗的资源是非常高的。
在一些高性能代码中,只需要读取数组的一部分,或者是排序算法中只需要聚焦一部分。
可以使用Span

int[] arr45 = [ 4, 5, 6, 7, 8, 9 ];
var span41 = arr45.AsSpan();
Console.WriteLine(arr45[0]);//4
span41[0] = 99;
Console.WriteLine(arr45[0]);//99

Span类型就像引用变量的数组。引用变量是特殊类型,你没有办法直接声明他的数组。
Span的值进行修改,会作用到原本的数组上。Span也可以使用Range进行截取。
这样会构造一个新的Span,但Span是值类型,构造Span的消耗比构造一个新的数组小得多。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值