什么是数组呢?从名字上来看,数组、数组是不是就是数据的组合呢?让我们来看看数组的概念,数组是⼀组相同类型元素的集合,我们需要注意的是数组中存放的是1个或者多个数据,但是数组元素个数不能为0。同时 数组中存放的多个数据,是相同类型的。
一维数组的定义一般来讲需要包括数据类型,数组元素个数,数组名,它的一般形式为:
type arr_name[ 常量值 ];
type 指定的是数组中存放数据的类型,可以是: char 、 short 、 int 、 float 等,当然也可以⾃
定义的类型。
arr_name 指的是数组的名字,同时数组名也代表着 数组的首元素地址 。
[ ] 中的常量值是⽤来指定数组的⼤⼩的,即数组元素个数, 不仅仅是常量值,也可以是常量表达式的形式,比如[3+5]
在 C99标准之前 ,C语⾔在创建数组的时候,数组⼤⼩的指定只能使⽤ 常量、常量表达式 ,或者如果我 们初始化数据的话,可以省略数组⼤⼩。也就是说,C语言 不 可以对数组的大小 作动态的定义 ,比如下面在两个定义方式是错误的
int n;
scanf("%d",&n);
int arr[n];//想要通过在程序中输入数组的大小
int n = 10;
int a[n];//使用变量来定义数组的大小
因为这样的语法限制,让我们创建数组 就不够灵活,有时候数组⼤了浪费空间,有时候数组⼜⼩了不够⽤,于是 C99中给⼀个 变⻓数组(variable-length array,简称 VLA) 的新特性,允许我们可以使⽤变量指定 数组⼤⼩。
上⾯⽰例中,数组 arr1、arr2、arr 都是变⻓数组,因为它的⻓度取决于变量 的值,编译器没法事先确定。
变⻓数组的根本特征,就是 数组⻓度 只有 运⾏ 时才能确定,所以变⻓数组不能初始化。它的好处是程 序员不必在开发时,随意为数组指定⼀个估计的⻓度,程序可以在运⾏时为数组分配精确的⻓度。 变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的,在程序运⾏的时候,根 据变量的⼤⼩来指定数组的元素个数,⽽不是说数组的⼤⼩是可变的。 数组的⼤⼩⼀旦确定就不能再变化 。 目前在VS2022上,没有⽀持C99中的变⻓数组,在搭配有gcc编译器的集成开发环境是支持的,比如DevC++,上面的代码就是通过DevC++实现的。
在数组创建的时候,我们需要给定⼀些初始值,称为初始化的,它有多种赋值方式,
我们通过数组的概念可以知道,数组元素的类型,那么数组有没有类型呢?事实上,也是有的,像下面这两个一维数组,我们可以通过监视的方式来知道它的类型
所以arr1的类型是int[5],arr2的类型是int[6],我们可以知道,当数组元素个数不一样时,它们的数组类型也是不一样的。
C语⾔规定数组是有下标的,并且下标是从0开始的,假设数组有n个元素,最后⼀个元素的下标是n-1,下标就相当于数组元素的编号。同时在C语⾔中数组的访问提供了⼀个操作符 [] ,这个操作符叫:下标引⽤操作符,有了下标我们就可以对相应的数组元素进行打印,比如下面的arr1数组的第一个元素1下标是0,arr2中6的下标是5,就进行了相应的打印。
我们可以根据需求,给数组输⼊想要的数据,并且进行打印
那么数组中的元素在计算机中是怎么样存储的呢?
我们通过打印地址来知道它的存储方式:
在VS中使用x64表示是64位的环境,这个地址是64个bite位的,地址比较长;而x86是32位的环境,显示的地址是16进制的。
16进制:它的数据包括0~15在计算机中表示为 0,1,2,3,4,5,6,7,8,9,A(10),B(11),C(12),D(13),E(14),F(15),后面的字母也可以是小写字母。它的特点是逢16进1(比如输出结果中7C--->80,就是C(12)+4=16进1.
所以为了更好地观察地址的变化,我们使用x86的环境来打印地址
从输出的结果我们分析, 数组随着下标的增⻓,地址是由⼩(低)到⼤(高)变化的 ,并且我们发现每两个相邻的元素之间相差4(因为⼀个整型是4个字节)。所以我们得出结论: 数组在内存中是连续存放的
我们可以使⽤sizeof这个关键字来计算数组的元素个数,
一般来讲,sizeof是计算类型或者变量⼤⼩的,因为单位是字节,同时数组中所有元素的类型都是相同的, 所以输出结果是 单个元素的字节长度*数组元素个数 ,如果我们需要得到真正的数组元素个数,就需要 除以单个元素的字节长度 ,这⾥我们可以选择除以第⼀个元素算⼤⼩就可以了。
输出的结果是24,计算的是数组所占内存空间的总⼤⼩,单位是字节。
输出的结果是6(数组的元素个数)
当数组初始化有逗号表达式的时候,会取逗号表达式的最后一个内容作为初始化的值。比如下面的数组元素个数为4,输出1 4 5 6.
如果我们 把⼀维数组作为数组的元 素 ,这时候就是 ⼆维数组 ,⼆维数组作为数组元素的数组被为三维数组,⼆维数组以上的数组统称为多维数组。就像数学中的矩阵一样,有行(row)和列(column) 的排列形式, 我们把⼀维数组作为数组的行。
它定义的一般形式为:
type arr_name[常量值1][常量值2];
这里呢,我们先讲一下二维数组的访问:
C语⾔规定,⼆维数组的下标⾏是从0开始的,列也是从0开始的,⼆维数组访问也是使⽤下标的形式的,⼆维数组是有⾏和列的,只要锁定了⾏和列就能唯⼀锁定数组中的⼀个元素。比如下面的2的就是在第0行第一列,可以表示为arr[0][1]
初始化有着多种方式,我们可以通过两层for循环来进行打印二维数组
我们需要特别注意的是 初始化时 可以省略⾏,但是不能省略列 ,系统可以根据总共数和列数来计算出行数
通过前面我们知道数组在内存中是连续存放的,二维数组也是一样的,我们依然通过打印地址的方式来进行验证:
我们可以看到arr[0][3]和arr[1][0]的地址也是连续的,所以二维数组在内存中也是连续存放的,是线性的,不是二维的。
我们依然可以使用sizeof来计算
有了前面的基础之后,我们来介绍一种比较特别的数组,也就是字符数组。
我们一般这样定义字符数组:
一维字符数组:char 数组名[常量值]
二维字符数组:char 数组名[常量值][常量值]
因为字符型数据是以整型形式(ASCII码)进行存放的,我们也可以用整型数组来存放字符数据,但是一般不建议这样使用,因为字符只需要1个字节长度进行存放,而整型为4个字节长度进行存放,这样存储会浪费存储空间
字符数组如果不进行初始化的话,系统就会发出警告,数组中各个元素的值也是不可预料的。
如果{}中字符个数大于数组长度,就会出现语法错误;如果{}中字符个数小于数组长度,会将字符赋给前面的元素,其余的元素自动定为空字符(‘\0’)
例子如下:
在{}内,我们除了使用‘ ’(单引号)来进行一个个字符的初始化外,我们还可以使用“ ”(双引号)直接输入一个字符串更加方便,当然也就可以用%s来进行打印,使用字符串常量初始化字符数组的时候可以省略{}。
前面我们知道字符串的结束标志是‘\0’,所以下面sz也就等于4
我们一般使用scanf函数来进行输入,因为数组名代表着数组的首元素地址,所以scanf函数的输入项如果是字符数组名,就不需要再加取地址操作符&。
除了scanf函数,我们还可以使用gets函数来输入一个已经定义的字符数组.
除了printf函数,我们还可以使用puts函数来输出一个字符数组,输出的字符串可以包括转义字符,
用puts输出的时候会将‘\0’转换为‘\n’,即输出字符串后换行。
如果字符数组没有被初始化就使用puts进行输出的话,系统就会发出警告。
注意:gets和puts只能输入和输出一个字符串
字符串的比较不可以使用==这个操作符,而是使用strcmp函数(头文件string.h)来进行比较。比较规则是:将两个字符串从左向右逐个字符进行比较(按ASCII码值的大小比较),直到出现不同的字符或者遇到‘\0’为止。
它的一般使用形式为:strcmp(str1,str2)
有以下两种情况:
1.如果对应字符不一样,以第一对不相同的字符比较结果为准
2.如果前面的字符都一样,看哪一个字符串长。
比较结果由函数值带回:
1.str1与str2相同,返回函数值为0
2.str1>str2,返回函数为一个正整数
3.str1<str2,返回函数为一个负整数
//二分查找
#include<stdio.h>
int main()
{
int i = 0;
int a[] = { 1,2,3,4,5,6,7,8,12,15 };
int sz = sizeof(a) / sizeof(a[0]);
int left = 0;
int right = sz - 1;//使用下标进行查找
printf("你想找的值是:");
scanf("%d", &i);
while(left<=right)
{
int mid = left + (right - left) / 2;//mid只可以放在里面,后面left和right的值会变化
if (a[mid] > a[i])//i在left~mid范围内
{
right = mid - 1;
}
else if (a[mid] < a[i])//i在mid~right范围内
{
left = mid + 1;
}
else if(a[mid]=a[i])
{
printf("找到了,下标是:%d",mid);
break;
}
}
if (left > right)
printf("没有找到!");
return 0;
}