C语言学习笔记——数组
创始人
2024-06-02 03:08:42
0

前言

     数组是C语言中的一种自定义数据类型,它的使用非常广泛。但是很多新手在使用数组时,经常在一些细节上出问题,导致程序崩溃或者无法编译。今天,我就来详细聊聊数组的使用和我注意到的一些细节。


一、常见的数组类型与数组的创建

1. 常见数组类型 

       与常见指针类型类似,常见的数组类型也是与常见的数据类型一一对应的,这里简单列举几个最常用的。

int arr1[10]; //整型数组
char arr2[20];  //字符数组
double arr3[5];  //双精度浮点型数组
struct stru arr[6];  //结构体数组

2. 数组的创建 

int arr1[10];  //创建数组
int arr2[5] = {0,0,0,0,0};  //创建数组并完全初始化
int arr3[10] = {0};    //创建数组并不完全初始化
int arr4[] = {1,2,3,4,5};   //创建未给定大小的数组并初始化

       我们以整型数组为例,上方为整形数组的四种创建与初始化方式。

       首先来介绍第一行,也是最基础的数组创建方式,“数据类型 + 数组名 + [数组大小]”。其中,数组名和[ ]之间不能出现空格,数组大小只能为常量(可以为标识符常量)且只能为正整数。那么我们来解读第一行,就是一个名为“arr1”,可以存放10个整型数据的数组(数组大小为10个整型,即40个字节),该数组的数据类型为"int[10]"。

       我们可以用“sizeof()”来计算数组在内存中占用的空间大小,如下图。

       接下来看第二行,数组arr2为一个大小为5的整型数组,它的五个数据均被初始化为0。其中花括号中的第一个0即对应数组的第一个数据,称为数组的第一个元素,以此类推。

       同理,第三行的arr3数组为一个大小为10的整型数组,它的第一个元素被初始化为0,剩余的9个元素未初始化。

       第四行的arr4数组在创建时未给定大小,那么它的大小就由初始化的元素个数决定。由此得出arr4的大小为5。  

补充:变长数组 (VS编译器不支持)

       变长数组与常规数组的区别在于,常规数组在定义时,其数组大小只能为常量,而变长数组的大小可以为变量。因此,变长数组可以精准地开辟内存空间,从而避免空间浪费。

3. 字符数组的初始化

char ch1[] = {'a','b','c'};
char ch2[] = "abc";

       字符数组可以按照常规数组初始化方式进行初始化,也可以按照上图第二行的方式,直接用字符串进行初始化。那么上图中的ch1,ch2的大小分别是多少呢?显然,ch1的大小为3,那么ch2的大小也为3吗?我们来看下图。

       我们发现,数组ch2的大小为4,这又是为什么呢?我们不妨直接将这两个数组以字符串形式打印出来看看。

       可以看见,第一行出现了许多乱码,而第二行则是正常的"abc"。这是为什么呢?关于这个问题,等到介绍数组在内存中的存储方式时,我再来详细解释。

二、数组元素的访问 

       当我们创建了一个数组后,我们应该如何调用数组中的元素呢?换句话说,如何访问数组的元素呢?

1. 下标引用操作符 

//  [] -  下标引用操作符 int arr[10];
arr[0] = 1;

       数组的每个元素都有自己对应的下标,通过其下标即可访问该元素。我们可以通过下标引用操作符“[ ]”。如图,arr为一个大小为10的整型数组,当我们创建了数组之后,我们通过“数组名 + [元素下标]”,访问了arr数组中下标为0的元素,并将其赋值为1。

       注意,上方代码中创建数组和访问数组元素均使用了“[ ]”,但这两个“[ ]”的含义不同,第一行的“[ ]”代表arr为一个"int[10]"类型的数组,而第二行的“[ ]”则是下标引用操作符,用来访问数组元素。

       数组中第n个元素的下标为n-1,例如,第一个元素的下标为0。

2. 指针访问

int arr[10];
*(arr+1) = 2;  //等价于: arr[1] = 2;

       本质上,数组名就是指向数组起始地址的指针。由于数组arr为整型数组,故访问数组元素时,其数组名本质上为" int* "类型的指针,可通过指针的解引用操作符访问其指向的元素。图中“arr+1”即指针向后偏移一位,指向了数组中的第二个元素,因此等价于访问数组第二个元素。

int arr[10];
int* p = arr;
*(p+1) = 2;

       同样地,我们也可以用其它指针储存arr所指向的地址,并通过该指针访问数组元素,如图中的指针p。 

三、数组在内存中的存储 

       相信通过上面的访问方式,聪明的你一定已经猜到数组在内存中的存储形式了。数组在内存上占用一块连续的空间。我们通过VS的调试器来观察数组在内存中的存储,如下图。每个整型数据占用四个字节的空间,故地址每隔四存放一个数据。

        而数组名arr即是首元素的地址,我们可以通过打印地址来查看。这也就解释了为什么数组元素可以通过指针的方式来访问。

 

        接下来我们来解决之前字符数组留下来的问题。

       通过监视窗口,我们可以看到,ch1中只有三个元素,相比之下,ch2多出了第四个元素'\0'。'\0'是一个字符,用来终止字符串。

       由于ch2中存在'\0',打印ch2时才不会出现后面的随机值。而ch1中没有'\0',因此当ch1的元素全部打印完后,编译器会继续打印数组外的内容,由于未被赋值,这些内容通常为随机值,因此一直打印到遇到随机的'\0',打印才结束。

       使用双引号" "引用的字符串的末尾都会自带一个'\0',这就是为什么用"abc"初始化ch2后会多出一个'\0'。 

四、二维数组

1. 二维数组的创建

int arr1[3][3] = {0};
int arr2[][3] = {0};

       二维数组有以上两种创建方式,其中前后两个“[ ]”中的数字我们分别称为行数和列数。二维数组的大小等于行数乘以列数。

       在第一行中,我们给定了数组arr1的行数和列数,均为3,因此arr1的大小为9,即可容纳九个元素。同样,如果行数和列数都给定了,就可以选择不初始化。

       在第二行中,我们给定了数组arr2的列数,但没有给定行数,这样一来数组的大小就根据初始化的值来确定了,如图中将第一个元素初始化为0,那么为了存储元素0,数组必须开辟一行的空间,由于一行有三列,因此数组arr2的大小为3。从中我们可以看出,为给定行数的二维数组的大小是由列数和初始化元素个数决定的。其大小必须为列数的整数倍,并且必须大于元素个数。

2. 二维数组元素的访问 

int arr[3][3];
arr[0][0]=1;

       二维数组的元素也是通过下标来访问的,只不过由于二维数组有行和列,因此下标也分为行标和列标。同样的,行标和列标也都是从0开始,如图中arr[0][0]即是访问第一行第一列的元素。

       关于二维数组的指针访问,本文就不过多解释了,因为相对来说比较复杂,涉及到二维数组的原理。有兴趣的小伙伴可以自行推导。(提示:要用到二级指针)

3. 二维数组在内存中的存储 

       在我们理解二维数组时,我们把二维数组分成行和列,如上图。我们知道,一维数组在内存中是占用一块连续的空间。那么二维数组在内存中又是如何存储的呢?我们再次通过VS的内存窗口观察二维数组arr在内存中的存储情况。我们发现,二维数组在内存中也是连续存储的,并不是像我们理解的那样分为行和列来存储。

五、数组问题中一些常见的错误和技巧

1. 越界访问

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int a = arr[10];

       我们来看上面这段代码。首先创建一个大小为10的整型数组并完全初始化,然后创建一个整型变量a,并初始化为arr数组中下标为10的元素。但是数组arr的大小为10,那么它最大的下标应该为9,也就是说不存在下标为10的元素。这就是所谓的越界访问。

       由于数组创建时只开辟了10个整型大小的合法空间,而上方代码访问了第11个整型的空间,因此形成非法访问。

2. 初始化与修改数组元素

//错误示范
int arr[] = {1,2,3,4,5};
arr = {1,2,3,0,0};
char ch[] = "hello";
ch = "world";//正确做法
int arr[] = {1,2,3,4,5};
arr[3] = 0;
arr[4] = 0; 
char ch[] = "hello";
ch = strcpy(ch,"world");

       在讲数组的初始化时,我提到可以使用花括号和字符串进行初始化。但是要注意,当数组创建完毕并初始化后就不能再使用花括号或字符串来修改数组了。如果数组创建完成后需要修改数组元素,则需通过下标访问需要修改的元素进行修改,若需要整体修改则可借助循环遍历数组。

3. 数组传参 

void test(int* arr)
{arr[0] = arr[1];
}int main()
{int arr[3] = {0,1,2};test(arr);return 0;
}

       之前介绍访问数组的方法时就提到,数组名其实就是指向数组首地址的指针,也就是说,数组传参其实也就等价于指针传参。在声明函数的参数时,若需接受数组,只需要声明一个同类型的指针变量即可。


结束语 

       以上就是我对数组的一些基础知识的总结啦。关于数组,我个人还是更喜欢将其理解为指针的一种变体,因为C语言中,指针可以说与内存息息相关,而数组则是通过一个指针(数组名)来管理该数组空间内的所有数据。不知道大家都是如何理解的呢?欢迎在评论区留言。

相关内容

热门资讯

恋夜视频安卓系统Uc,恋夜视频... 亲爱的读者,你是否曾在深夜时分,被手机屏幕上跳动的视频吸引?今天,就让我带你一探究竟,揭开恋夜视频安...
鸿蒙套娃安卓系统视频,融合与创... 你知道吗?最近科技圈可是炸开了锅,因为华为的新操作系统鸿蒙OS又有了新动作。这不,他们竟然把鸿蒙套娃...
xp系统连接安卓手机问题,实用... 你有没有遇到过这样的情况:你的电脑上还运行着那个经典的XP系统,而你的安卓手机却时不时地想要和你亲密...
压缩安卓系统储存空间,高效管理... 手机里的照片越来越多,游戏也越玩越上瘾,可这安卓系统的储存空间却越来越紧张,是不是感觉像是在挤牙膏?...
安卓手游转苹果系统教程,轻松实... 你是不是也和我一样,手头有一堆安卓手游,突然之间想换换口味,体验一下苹果系统的魅力呢?别急,今天就来...
安卓原生系统锁屏暗,安卓系统锁... 亲爱的手机控们,你是否曾为安卓手机锁屏时的暗模式而感到好奇?那种在夜晚或光线不足的环境中,屏幕自动调...
安卓系统表情包下载地址,安卓系... 你是不是也和我一样,对安卓系统的表情包爱不释手?那些搞笑的、可爱的、甚至是有点小调皮的表情,总能让我...
原生安卓系统声音bug,揭秘那... 你有没有遇到过这种情况?手机里突然传来一阵奇怪的声音,让你瞬间从美梦中惊醒,或者正在专心工作时被打扰...
水果收银机安卓系统,便捷高效的... 你有没有想过,在繁忙的超市里,那些摆满新鲜水果的摊位,背后竟然隐藏着一个小小的科技秘密?没错,就是那...
安卓系统变苹果界面了吗,苹果界... 最近手机界可是炸开了锅,不少安卓用户都在议论纷纷:“安卓系统变苹果界面了吗?”这事儿可真不简单,得好...
miui操作系统与安卓系统吗,... 亲爱的读者,你是否曾在手机上看到过MIUI操作系统和安卓系统这两个名字,好奇它们之间有什么区别?今天...
安卓系统怎么卡道具界面,探究原... 手机用久了,是不是感觉安卓系统越来越卡?尤其是那个道具界面,点开就慢吞吞的,真是让人头疼。别急,今天...
安卓系统红包加速器,畅享无阻新... 你有没有发现,现在用手机抢红包简直是一场速度与激情的较量?别急,别急,让我来给你揭秘一款神器——安卓...
安卓经典版系统更新时间,从首次... 你有没有发现,最近你的安卓手机又悄悄地变了个样?没错,就是那个陪伴我们多年的经典版系统,它又来更新啦...
安卓系统开发要多久,约需1-2... 你有没有想过,自己动手开发一个安卓应用,究竟需要多长时间呢?这可是个让人好奇的问题,毕竟安卓系统开发...
原生安卓系统手机壁纸图片,探索... 亲爱的手机控们,你是否曾为寻找一款独特的壁纸而烦恼?今天,就让我带你走进原生安卓系统手机壁纸的奇幻世...
bmw安卓互联系统,智能驾驶新... 你有没有发现,现在开车已经不仅仅是驾驶那么简单了?一辆好车,还得有个好“大脑”,这样才能让你的驾驶体...
安卓手机升级系统卡吗,安卓手机... 你有没有遇到过这种情况:安卓手机升级系统后,突然感觉手机像蜗牛一样慢吞吞的,心里那个急啊!今天,就让...
无线麦克风安卓系统,轻松实现无... 你有没有想过,在一场热闹的K歌派对或者重要的演讲场合,无线麦克风简直就是救星啊!想象你手握麦克风,自...
怎么重新定制安卓系统,打造专属... 你有没有想过,你的安卓手机其实可以变得独一无二,就像是你自己的小宇宙一样?没错,就是重新定制安卓系统...