数组
数组的概念
我们前面学过使用变量进行存储一个数据,如果我要是想要存储多个相同类型的数据的话不太方便,因此就有了数组,数组是啥呢?其实数组就是相同类型元素的集合
int a = 10;
int b = 20;
int c = 30;
int num[] = { 10,20,30 };
值得注意的是
在数组中可以存放一个或多个数据,数组的元素个数不能为零
在数组中存放的数据是相同类型的。
int num1[] = { 10 };
int num2[] = { 10,20,30 };
int num3[] = { };//err
int num3[] = { 10,"a",20};//err
数组分为一维数组和多维数组,最常见的多维数组是二维数组
一维数组的创建
现在我们对数组有了一定的了解,如何去创建数组呢?
数组创建的格式如下:
type arr_name[常量值]
type: 数组存放数据的类型,
arr_name : 数组名,起的有意义就行
常量值: 代表数组存放数组元素的个数,根据实际需求去设置
存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的大小和数组的元素类型
假设我们要存储某个班20位同学的成绩,可以用到数组的创建;这个班排名前三同学的名字也可以使用数组的创建
int math[20];
char name[10];
数组的初始化
现在我们知道这个班级需要输入20位同学的成绩,但没有每位同学具体的分数,这时就需要数组的初始化来解决,数组的初始化是将数据用大括号括起来的
int math[20] = { 85,86,87,90,91,92,95,91,75,85,86,87,90,91,92,95,91,75,65,77 };//完全初始化
int math[20] = { 85,86,87,90,91 };//不完全初始化,剩下的元素默认初始化为0
char name[10] = {'w', 'q'};
char mame[10] = "abc";//字符数组在初始化时可以用字符串进行初始化,它将 \0 包含在内
数组在初始化时注意初始化不要超过 [ ] 里数字的个数,会造成初始值设定项值过多的错误
int mun[3] = { 1,2,3,4 };//err
数组的类型
数组有类型吗? 答案是有的,具体长啥样呢?其实去掉对应的数组名留下的就是数组的类型
int num[3];// 数组类型是int [3]
char arr[5];//数组类型是char [5]
float num1[4];//数组类型是float [4]
数组是一种自定义类型,它的每个类型都是不同的,同时需要注意的是数组元素的类型和数组的类型的区别
int num[3];// 数组元素的类型是int
char arr[5];//数组元素的类型是char
float num1[4];//数组元素的类型是float
一维数组的使用
了解了数组的基本语法,那我们怎么具体使用它呢?这就不得不唠唠数组的下标了
数组的下标
C语言规定数组其实是有下标的,从0开始,假如有n个元素,其下标有 n-1 个,下标相当于我们对数组元素进行编号
int num[5] = { 1,2,3,4,5 };
// 下标 0 1 2 3 4
知道了数组的下标,如何进行对数据的访问呢?在C语言中提供了 下标引用操作符 [ ],专门用来访问数组元素的,假如我要访问该数组中下标为3的元素,num[3]即可 ;访问下标为4的元素,num[4]即可
代码如下
#include<stdio.h>
int main()
{
int num[5] = { 1,2,3,4,5 };
// 这里的5是指定数组元素个数的
printf("%d\n", num[3]);
//这里的3是指数组的下标为3的元素
printf("%d\n", num[4]);
return 0;
}
数组的打印
如果我们想要访问整个数组怎么办呢?我们可以使用 for 循环产生0 - 4的下标 进行访问即可
代码如下:
#include<stdio.h>
int main()
{
int num[5] = { 1,2,3,4,5 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", num[i]);
}
return 0;
}
数组的输入
我们现在可以对数组进行打印,相对应的我们也可以对数组进行输入值
代码如下:
#include<stdio.h>
int main()
{
int num[5] = { 1,2,3,4,5 };
int i = 0;
//输入
for (i = 0; i < 5; i++)
{
scanf("%d", &num[i]);//注意这里不能有空格
}
//打印
for (i = 0; i < 5; i++)
{
printf("%d ", num[i]);
}
return 0;
}
⼀维数组在内存中的存储
我们现在学会了数组的基本使用方法,还得需要知道数组在内存中是如何存储的,先通过一段代码及输出结果进行观察:
#include<stdio.h>
int main()
{
//当对数组进行初始化的时候,数组的大小可以省略。编译器会根据数组的初始化内容,自动计算数组的元素个数
int num[] = { 1,2,3,4,5 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("&num[%d] = %p\n", i, &num[i]);//%p - 用来打印地址的 & - 取地址操作符 - 取出变量在内存中的地址
}
return 0;
}
通过运行结果来看,因为这个数组元素是整型类型, 所以每两个相邻的元素之间相差4个字节,由此可以得出结论:
1.数组在内存中是连续存放的
2.随着数组下标的增长, 地址是由小(低)到大(高)变化的
sizeof计算数组元素个数
在遍历数组时,我们想知道数组元素的个数,如何计算呢?贴心的C语言给出了答案:使用sizeof,单位是字节,它可以对类型和变量进行计算,当然它也可以对数组进行计算,因为数组是属于自定义类型的。例如:
#include<stdio.h>
int main()
{
int num[20] = { 0 };
printf("%d\n", sizeof(num));//80
return 0;
}
输出的结果是80,是数组在内存中的总大小,单位字节
那如何计算数组元素的个数呢?我们可以使用数组的总大小 / 单个数组元素的大小 = 数组元素个数,代码如下:
#include<stdio.h>
int main()
{
int num[20] = { 0 };
int ret = sizeof(num) / sizeof(num[0]);
printf("%d\n", ret);//20个元素
return 0;
}
通过计算数组元素的个数,我们以后在写数组元素个数的地方就不用固定写死了,不管数组如何变化,其计算的结果也随之变化
举个代码的实例加以说明:
#include<stdio.h>
int main()
{
//int num[5] = { 1,2,3,4,5 };
//换成 10 也正常运行
int num[10] = { 1,2,3,4,5 };
int i = 0;
int ret = sizeof(num) / sizeof(num[0]);
//输入
for (i = 0; i < ret; i++)
{
scanf("%d", &num[i]);//注意这里不能有空格
}
//打印
for (i = 0; i < ret; i++)//这里原本的5换成了ret,即使前面数组的个数发生改变,后面输入和打印也会随着改变,代码不会出错
{
printf("%d ", num[i]);
}
return 0;
}
二维数组的概念
我们学习了一维数组,一维数组的元素是内置类型的,如果我们将一维数组的作为数组的元素,这就是二维数组;倘若将二维数组作为数组的元素,这就是三维数组,二维数组的数组我们统称为多维数组
二维数组的创建
我们对二维数组概念有了一定的了解后,那它该如何创建呢?
语法如下:
type arr_name[常量值1][常量值2];
int num[3][5];
float arr[3][2];
type:代表是数组每个元素的类型
arr_name:数组名,起的要有意义
常量值1:代表行号
常量值2:代表列号
二维数组的初始化
在创建变量或数组是给定一些初始值,被称为初始化
那二维数组该如何初始化呢?和一维数组一样,也是用大括号起来
完全初始化
int num[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
不完全初始化
int num[3][5] = { 1,2,3,4,5 };
按行初始化
int num[3][5] = { {1,2},{3,4},{5} };//这里第一行的元素是1,2,第二行是3,4, 第三行是5
int num[3][5] = { 1,2,3,4,5 };//这里第一行的元素是1,2,3,4,5,第二行和第三行是 0
初始化时可以省略行但是不能省略列
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
int num[][5] = { 1,2,3 };
int num[][5] = { {1,2},{3,4} };
二维数组的使用
二维数组的下标
二维数组也是有下标的,我们知道二维数组是有行和列的,只要确定了行号和列号就可以确定二维数组的元素了,在C语言中规定行号和列号的下标都是从0开始的
比如:
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
我想要第2行第5列的元素并将它打印在屏幕上,代码如下:
#include<stdio.h>
int main()
{
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
printf("%d\n", num[2][4]);//7
return 0;
}
⼆维数组的输入和输出
我们知道了如何访问单个数组元素的方法,那如何访问整个数组呢?
可以使用输入和输出实现,比如:
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
这个数组的行号的范围0-2,;列号是0-4,通过创建行号和列号的循环进行输入和输出
代码如下:
#include<stdio.h>
int main()
{
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
int i = 0;//创建行号
//输入
for (i = 0; i < 3; i++)
{
int j = 0;//创建列号
for (j = 0; j < 5; j++)
{
scanf("%d", &num[i][j]);
}
}
//输出
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", num[i][j]);
}
printf("\n");//每打印一行后进行换行
}
return 0;
}
如果我想要按列来打印呢?
比如原本是:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
使用列来打印,也就是五行三列,效果如下:
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
我们可以将行当成列,列当成行来看待,代码如下:
#include<stdio.h>
int main()
{
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
int i = 0;
//输入
for (i = 0; i < 5; i++)
{
int j = 0;
for (j = 0; j < 3; j++)
{
scanf("%d", &num[j][i]);
}
}
//输出
for (i = 0; i < 5; i++)
{
int j = 0;
for (j = 0; j < 3; j++)
{
printf("%d ", num[j][i]);
}
printf("\n");//每打印一行后进行换行
}
return 0;
}
//或者
#include<stdio.h>
int main()
{
int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
int i = 0;
int j = 0;
//输入
for (j = 0; j < 5; j++)
{
for (i = 0; i < 3; i++)
{
scanf("%d", &num[i][j]);
}
}
//输出
for (j = 0; j < 5; j++)
{
for (i = 0; i < 3; i++)
{
printf("%d ", num[i][j]);
}
printf("\n");
}
return 0;
二维数组在内存中的存储
我们知道了一维数组的在内存中是连续存放的,那二维数组呢?让我们通过一段代码及结果进行观察,代码如下:
#include<stdio.h>
int main()
{
int num[3][5] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("num[%d][%d] = %p\n", i, j, &num[i][j]);
}
}
return 0;
}
通过输出的结果可以看出来:每一行内部的元素是相邻的,相邻的两个元素之间的地址相差四个字节(元素是整型类型的,它们的大小是四个字节),可以得出结论:二维数组在内存中也是连续存放的
C99中的变长数组
在C99标准之前,C语⾔在创建数组的时候,数组大小的指定只能使用常量、常量表达式,如果我们初始化数据的化可以忽略其大小
比如:
int num[5];
int num[2+3];
int num[] = { 1,2,3,4,5 };
在C99中,引入了变长数组(VLA)这一概念,允许我们使用变量指定数组大小比如:
int n = 10;
int num[n];
由于数组长度在编译时才知道它的长度是多少,因此变长数组不可以初始化,其次,变长数组意思是可以使用变量来指定数组的大小,并不是说数组的长度是可以变化的
但是在VS上不支持变长数组,可以在DevC++ 或者小熊猫C++上使用,因为他们的底层都是使用的gcc,gcc支持C99中的变长数组的
代码如下:
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int num[n];
int i = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &num[i]);//数组名是地址,但这里是数组元素,因此需要使用&
}
for (i = 0; i < n; i++)
{
printf("%d ", num[i]);
}
return 0;
}
关于数组的练习:
练习1:练习1:多个字符从两端移动,向中间汇聚
编写代码,演示多个字符从两端移动,向中间汇聚
具体代码如下:
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
char arr1[] = "one piece is true!!!";
char arr2[] = "********************";
int left = 0;
int right = strlen(arr1) - 1;
while (left <= right)//这里是为了确定左右两边是否还有元素
{
Sleep(1000);//让程序睡眠1秒,也让我们看清楚程序是如何进行的
system("cls");//清理控制台的数据
arr2[left] = arr1[left];//演示字符从两端进行移动的过程
arr2[right] = arr1[right];
printf("%s\n", arr2);
left++;//循环一次后将左边和右边的下标进行移动,向中间回拢
right--;
}
return 0;
}
练习2:二分查找
在⼀个升序的数组中查找指定的数字n,一般情况下我们会遍历数组,但是这种方法效率比较低。
我们会采取二分查找的方法进行查找,,那么二分查找概念是啥呢?先看个生活中的例子吧,男生小帅买了一件价值80元的衣服,他让小美猜猜他花了多少钱,机灵的小美先从中间开始猜150元,小帅说大了,小美又说75元·······,二分查找也叫折半查找,意思是在有序的数据中从中间开始查找,这样查找会为我们提高效率。
在一个有序的整型数组中,输入一个数据,找出并且打印它的下标。
具体代码如下:
#include<stdio.h>
int main()
{
int num[15] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
int A = 0;
scanf("%d", &A);//输入一个你想要查找的值
int left = 0;//左下标
int sz = sizeof(num) / sizeof(num[0]);//sizeof - 是用来计算字符串或者字符数组的个数的
int right = sz - 1;//右下标
int flag = 0;//假设没找到
while (left <= right)//确定左下标和右下标的中间是否还有元素
{
//int mid = (left + right) / 2;//找到中间值
int mid = left + (right - left);//假设left 和 right 的值都没有超过 int_max,如果它两相加可能会超过,
//因此使用 mid = a + (b-a)可以避免代码出现bug,即使b - a是负数
if (num[mid] < A)
{
left = mid + 1;//左边的值都比A小,让左边进行+1,再次进行比较
}
else if (num[mid] > A)
{
right = mid - 1;//右边的值都比A大,让左边进行-1,再次进行比较
}
else
{
flag = 1;
printf("恭喜你找到了,下标是 %d \n", mid);
break;
}
}
//if (left > right)
if (flag == 0)
if (!flag)
{
printf("抱歉,没找到\n");//左右下标没有元素,说明元素不在这一组有序数据里,没有找到
}
return 0;
}