C语言之数组的基本知识

C语言之数组的基本知识在没接触数组之前,同学们用的都是定义一个一个变量来存放数据,但是这样就有一个缺陷,如果数据量很大的时候,比如有50个学生的成绩需要录入进去,那么定义50一个变量将会非常耗费时间,而且用scanf()函数输入数据的时候也很麻烦。intstu1,stu2,stu3,…,stu50;scanf("%d%d%d%d…",&stu1,&stu2,&s…

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

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

int stu1, stu2, stu3, ..., stu50;
scanf("%d %d %d %d ...",&stu1, &stu2, &stu3, ..., &stu50);

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

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

一:数组.固定大小

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

double ArrStu[50];   //Array:数组
double Sales[12];    //一年十二个月,所以长度是12

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

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

二:数组.数据类型

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

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 如果应用不当,就会出现越界的错误。
在现在的学习阶段,方括号里面的内容必须是一个常量,而不能出现像

int n;
int arr[n];

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

//正确
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 这个元素实际上并不存在的。原因上面 “其他,第一点”有讲述,这也属于数组的越界

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

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

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

在这里插入图片描述

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


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

scanf("%d %d %d...", &arr[0], &arr[1], &arr[2]...);

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

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

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

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()函数
C语言之数组的基本知识
三:调用scanf()函数
在这里插入图片描述
这里注释掉的两种输出方式都没啥问题,但是有个前提是有结束符号。
细心的人可能注意到了我这里第一种方式多了一行

arr[i] = '
arr[i] = '\0';
'
;

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


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

int Arr[4][4]; 

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

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

(如有错误,欢迎指出)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Windows进程间通信—命名管道

    命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。与Socket网络通信相比,命名管道不再需要编写身份验证的代码。将

    2021年12月27日
  • dropna无效_drop from

    dropna无效_drop from需要加等号如df22=df22.dropna(how=”any”)

  • centos mysql 1045 错误

    centos mysql 1045 错误1、停用MySQL服务:#/etc/rc.d/init.d/mysqldstop2、输入命令:#mysqld_safe–user=mysql–skip-grant-tables–skip-networking&3、登入数据库:#mysql-urootmysql4、mysql>usemysql; 结果如下:  Databasecha

  • conda换成中科大的源,2.更换Conda源「建议收藏」

    conda换成中科大的源,2.更换Conda源「建议收藏」#换源###1.Conda切换为清华源>condaconfig–addchannelshttps://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/>condaconfig–addchannelshttps://mirrors.tuna.tsinghua.edu.cn/anaconda/pkg…

  • Zabbix 监控Redis

    Zabbix 监控Redis网上有大量zabbix监控redis的文章,但大多数不详细,而我按一下方法成功了,所以转载了此博主的文章此按照以下配置好后,会遇到一个问题:后查明是由于监控shell脚本格式问题请按:http://www.2cto.com/os/201305/215945.html 处理shell脚本和模版看文章的最下面一、配置zabbix插件

  • Hadoop集群搭建教程(详细)「建议收藏」

    Hadoop集群搭建教程(详细)「建议收藏」需要的安装包:  1.jdk压缩包  2.hadoop压缩包请前往我的github上下载相关安装包开始搭建hadoop集群一.使用VMvare创建两个虚拟机,我使用的是ubuntu16.04版本的因为默认的虚拟机主机名都是ubuntu,所以为了便于虚拟机的识别,创建完成虚拟机后我们对虚拟机名进行修改,我们把用于主节点的虚拟机名称设为master(按自己的喜好创建),把用于从节点的虚拟机名称…

    2022年10月24日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号