前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言之数组的基本知识

C语言之数组的基本知识

作者头像
全栈程序员站长
发布2022-09-08 10:20:31
5640
发布2022-09-08 10:20:31
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

在没接触数组之前,同学们用的都是定义一个一个变量来存放数据,但是这样就有一个缺陷,如果数据量很大的时候,比如有50个学生的成绩需要录入进去,那么定义50一个变量将会非常耗费时间,而且用scanf()函数输入数据的时候也很麻烦。

代码语言:javascript
复制
int stu1, stu2, stu3, ..., stu50;
scanf("%d %d %d %d ...",&stu1, &stu2, &stu3, ..., &stu50);

那么在C语言中有没有一种东西可以处理上面的数据呢? 当然有啦,数组这时候就出现了。 数组

数组是数据结构(我们大一下学期会专门学习这一章节),它可以存储一个固定大小的相同类型元素的顺序集合。<摘自百度> 有几个关键字要注意一下: 1:固定大小, 2:相同类型, 3:顺序集合。 要理解数组就得理解这三个关键字,我接下来一个一个对这个关键字进行讲解。

一:数组.固定大小

我们定义一个数组的时候,都必须事先告诉编译器这个数组的长度是多少,好让编译器给我们分配长度大小的内存空间,用来存放数据。 比如第一个例子,我想存放 50 个学生的成绩,或者存放一年每个月的销售额. 那么数组的定义是这样的:

代码语言:javascript
复制
double ArrStu[50];   //Array:数组
double Sales[12];    //一年十二个月,所以长度是12

观察下上面的两个数组,可以注意到数组(在这里先是一维数组)定义的基本格式是:

代码语言:javascript
复制
DataType ArrName[ size ];
//datatype 数据类型,如 int, long, float, double...
//ArrName 数组的名字,这里起名的方式跟变量名字的起法是一样的。
//size 数组的大小,这里的大小是固定的。
//[] 下标运算符,如我们要索引第2个元素,那么就是 Arr[1];

二:数组.数据类型

这里的数据类型就是上面提到的 datatype 。 一旦你确定了数组是何种类型的,那么你存放的数据就应该是这种类型的。 你不可以定义了 int 类型的数组,却用来存放浮点数,虽然可以编译通过,但是会得不到我们想要的结果。 如:

代码语言:javascript
复制
int arr[2];     //定义一个长度为2的int类型的数组
arr[0] = 12.5;  //赋值
arr[1] = 14.8;

运行结果如图:

int型,以%d的格式控制符输出,就会只保留整数部分,小数点后面的全部截断,所以输出的结果是12 14.

三:数组.顺序集合

假如我们定义了一个长度为 10 的数据,操作系统就会为其分配连续的十个内存地址。 这些地址用来存放地址,每一个地址所占的字节是数组的数据类型所决定的。 如int类型的每一个地址占据着4个字节,double类型的8个。

这里我用了取地址符将数组每一个元素的地址给显现出来,可以注意到各个元素之间的地址相差了4,为啥是4而不是别的呢?这是因为一个我一开始定义的数据类型是int类型的。 这里补充下内存地址的理解:

1:内存地址只是一个编号,代表一个内存空间。 2:内存地址也是内存当中存储数据的一个标识,并不是数据本身,通过内存地址可以找到内存当中存储的数据。<相当于通过你身份证上的地址信息,可以找到你的家乡一样.> 3:你也可以把计算机内存想象成一条长街上一间间房子,每间房子上面都有且只有一个唯一的编号,房子可以存放数据。

如这里的首元素的内存编号是 5240768,第二个元素的内存编号是 5240772, 这里也需要知道一点,这里的编号,只是该数据存放的首地址,只需要知道首地址就可以获取整个地址的值。 其他:

一 : 数组定义时候的方括号 [] 和 花括号里面的常量 上面我介绍了数组的定义方式和例子,如: int arr[10]这里的10表示整个数组长度为常量10[ ]也叫做下标运算符,如上面介绍的那样,你要索引哪个元素,就直接写该元素所在的位置即可。 这里要强调一点,数组的下标(index) 的范围是 0 ~ size – 1 下标下界是0,上界是 size – 1 如果应用不当,就会出现越界的错误。 在现在的学习阶段,方括号里面的内容必须是一个常量,而不能出现像

代码语言:javascript
复制
int n;
int arr[n];

二:数组的初始化 数组的初始化是在其定义的时候就应该执行的,如,为5个已经知道的整形数据进行排序,那么:

代码语言:javascript
复制
//正确
int ArrNum[5] = { 
    43, 65, 32, 774, 899 };
//而不能用下面这种方式
int ArrNum[5];
ArrNum[5] = { 
    43, 65, 32, 774, 899 };

因为对于 ArrNum[5] = 来说,这是一个赋值操作,将右值赋值给左值,一切常数、字符和字符串都是右值。在这里 { 43, 65, 32, 774, 899 }; 并不是右值的一种,所以这是错误的。 另一个错误是,ArrNum[5] 下标为 5 这个元素实际上并不存在的。原因上面 “其他,第一点”有讲述,这也属于数组的越界

有数字类型的数组初始化,也有字符类型数组的初始化。 例如

代码语言:javascript
复制
//方式一,单个单个元素的赋值,用单引号引起来每一个字符
char Name1[9] = { 
   'H','y','d','r','o','g','e','n'};  //H2

//方式二,直接用双引号,将字符串赋给变量Name2
char Name2[9] = "Hydrogen";
char Name2[9] =  { 
   "Hydrogen"}; 						//

三:数组的越界 这里讲的数组长度存在一个上界,一旦超过了这个界限会如何? 前面讲述到了,一旦数组定义完毕,系统就会为其分配它长度大小的空间地址。 而一旦超过了这个大小,就会发生一些未知的错误,也就是所谓的越界 这里用一个例子来说明下越界后数组内部的值的情况:

由运行结果可以知道,当数组的下标超过了上界后,其后面的值都是不确定的。


以上是数组的三个要素和一些补充,既然有数组了,我们如何为其赋值呢?总不可能采取:

代码语言:javascript
复制
scanf("%d %d %d...", &arr[0], &arr[1], &arr[2]...);

这样冗长的表达式吧。 考虑到数组当中,如果要对数组其中的某一个元素赋值的话,我们可以利用对应的下标索引出。即:

代码语言:javascript
复制
arr[0] = 23;
arr[1] = 44;
arr[2+3] = 412;  //方括号里面可以有加减操作
arr[2*3] = 32;   //也可以有乘除
arr[n--] = i;    //也可以是变量(变量的值对于数组来说有意义。)
...
arr[size-1] = 90;

在结合前面学习到的循环结构,是否可以将两者结合起来呢? 答案当时是可以的。 C语言中,循环有三种:

代码语言:javascript
复制
for( 表达式1; 表达式2; 表达式3) { 
    语句块; }
while(表达式){ 
     语句块; }
do { 
    语句块; }while(表达式);

每一个循环结构都需要一个循环变量来对其进行控制,如 i, k, j 每一个循环体, 对于循环变量来说: 1:其值都需要提前指定其大小(循环从哪里开始) 2:循环变量的上限(也就是循环到什么时候结束) 3:循环变量是如何改变的(如每次执行完循环体后,循环变量自增1,或是自增2…) 对于循环结构的 for 和 while 来说,执行第三步,都是在执行循环体后在执行的。 对于do-while()结构来说,无条件的执行一次。

讲到这里,很自然的就可以将循环结构和数组联系起来了。 对于数组的赋值,由于其下标可以用任意小于其上界的数字进行索引,那么我就可以借助一个循环变量 i , 来对其进行元素的索引。 可以这样理解:一个数组定义好了,在内存中已经分配了连续的空间地址,这个相当于一条街上连续的几户人家定了同一个公司的牛奶,然后每次配送员,只需要携带定的数量牛奶,一个接着一个送过去就可以了。 用代码写出来如下:

这里的循环变量 i 从 0 开始,也就是索引数组的第一个元素,即其下标为0的元素。 循环体的内容是将数据写入对应下标,每次执行完循环体后,循环变量自增1,即转到数组的下一个下标。这样循环执行,直到循环结束位置。

那对于字符数组呢? 字符数组有三种输入方式 一:用循环结构一个字符一个字符输入

二:调用gets()函数

三:调用scanf()函数

这里注释掉的两种输出方式都没啥问题,但是有个前提是有结束符号。 细心的人可能注意到了我这里第一种方式多了一行

代码语言:javascript
复制
arr[i] = '\0';

‘\0’是啥?有啥作用? 这个就是我上面提到的结束符号,输出的时候告诉编译器我这里结束啦,不可以再往后结束啦。 对于gets(), scanf(); 两个函数,在你输入字符串结束后,会自动在字符串结尾加上’\0’,这个是编译器帮你做到的,无需担心。<缓冲区和scanf的缺陷参见上一篇内容> 但是对于getchar();函数来说,却没有这个功能,它仅仅只是从缓冲区读取字符给你,也就是说,在最后你需要自己加上一个结束标记。


以上是一维数组的一些基本知识,以及一些补充。 对于二维数组来说,它的定义比一维的多了一个方括号:

代码语言:javascript
复制
int Arr[4][4]; 

一维数组像一条线一样,只有长度;二维数组有行,有列,可以看成有长和宽的矩形一样。 数据大小就是LH,如上面的二维数组,长度就是44=16。 在内存分配上面,是否也是按照二维的样式来分配呢?答案是否定的,它分配内存也是开辟了连续字节的。

这里首内存地址编号是:9828620 尾内存地址编号是: 9828680 < 9828620 + 15 * 4 = 9828680)(减去首地址这个元素) 可以看到这也是连续分配的。 值得注意的是,在输入,输出二维数组的时候,需要用到双重循环。 一维数组需要一层循环,二维两层,三维三层。 对于二维数组的理解,可以结合一维来。(二维比一维多了“行” 这个元素)。 在后面的学习中,可以将数组和指针联系起来,在更后续的学习中,可以联系到数据结构里面,这里以后学习到了自然会明白。

(如有错误,欢迎指出)

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/157321.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档