文章目录
数组
数组的介绍
数组是数据类型相同的一系列元素,在使用数组时,需要先声明数组,根据这些信息编译器就可以成功创建数组
数组声明例子
float arr[30];//包含30个float类型的元素的集合
char arr1[20];//包含20个char类型元素的集合
int arr2[10];//包含10个int类型元素的集合
但是不能声明成这样
int arr[];//错误
数组声明的格式
type name[ ] ;
先是数组的类型,然后是数组的命名,“ [ ] ”用来表明name是一个数组,“ [ ] ”里面的是数组的元素个数
初始化数组
数组创建的同时给一些初始值叫做初始化
一般在程序一开始时就初始化数组,
int arr[3] = {1,2,3};
这里创建了一个包含3个元素的数组arr,{1,2,3}在赋值的时候把1赋值给了数组的第一个元素,把2赋值给了数组的第二个元素,把3赋给了数组的第三个元素。"1,2,3"之间的逗号用来分隔数值,确保赋值的时候不会出错。
还有其他的初始化方式
char ch[] = "abc";
char ch1 = {'a','b','c'};
int arr[] = {1,2,3};//这个数组只有三个元素
完全初始化:
有几个元素位置就给几个元素的值
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
当赋值的数量多余元素的数量时,会报错:
初始值设定项太多
int arr[3] = {1,2,3,4,}//初识值多了,这样的写法是错误的
不完全初始化:
int arr[10] = {0};//全部都初始化为0
int arr[10] = {1,2,3};//后面的几个元素都被默认初始化为0
字符串与字符的初始化
字符串初始化的时候有:’\0’
int arr1[] = "abc";//a,b,c,\0————有四个元素,数组大小是四个字节
printf("%d",sizeeof(arr));//4
printf("%d",strlen(arr));//3__遇到\0就停止了,计算\0前面的
字符初始化的时候没有:’\0’
int arr[] = {'a','b','c'};//a,b,c————有三个元素,数组大小是三个字节
printf("%d",sizeeof(arr));//3
printf("%d",strlen(arr));//随机值,遇不到\0
数组的储存与下标
创建数组时,会在内存中申请一块连续的空间,所以数组的n个元素的地址是连续的:
对于这样一个数组arr[3] = {1,2,3};
#include<stdio.h>
int main()
{
int arr[3] = { 1,2,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d", sz);
return 0;
}
那我们怎么才能看到用到数组里面不同的元呢,这里就需要用到下标引用操作符" [ ]",
我们创建一个新的数组,分别打印出它的不同元素
#include<stdio.h>
int main()
{
int arr[3] = { 100,200,300 };
printf("arr[0] =%d\n", arr[0]);
printf("arr[1] =%d\n", arr[1]);
printf("arr[2] =%d\n", arr[2]);
return 0;
}
我们知道100这个数据被储存在了数组的第一个元素中,但是在数组的第一个元素的下标是0,所以在打印时我们打印arr[0]的值就会出现100。以此类推,数组的第二个元素下标是1,第三个元素的下标是2……
清楚哪一个数组元素对应的是哪一个下标是十分重要的
数组元素的输入与输出
当我们处理的数据较多时,就会利用数组,而数据多的时候采取下面这种方式去输入与输出就会很耗时间
比如我们需要输入一百个值出巡在数组中,那么我们就需要输入100个scanf函数嘛,输出时就需要打出100个printf函数嘛?
#include<stdio.h>
int main()
{
int arr[100] = {0};
//输入
scanf("%d",&arr[0]);
scanf("%d",&arr[1]);
scanf("%d",&arr[2]);
...
scanf("%d",&arr[99]);
//输出
printf("%d",arr[0]);
printf("%d",arr[1]);
printf("%d",arr[2]);
...
printf("%d",arr[99]);
return 0;
}
很明显这样是十分麻烦的,而当遇到要输入很多类型相同的数据,以及打印一串类型相同的数据,我们会想到循环
没错,循环可以很简单的解决上面这个问题
#include<stdio.h>
int main()
{
int arr[100] = {0};
int i = 0;
for(i = 0;i <100;i++)
{
scanf("%d",&arr[i]);
}
for(i = 0;i<100;i++)
{
printf("%d\n",arr[i]);
}
return 0;
}
上面这个循环可以很快的解决输入输出的问题但是有时我们遇见的数组是这样的
int arr[] = {1,2,3,4,5,6,7,8,9,1,2,3};
这样的数组初始化是合法的,但是在循环的时候我们需要知道在什么时候终止循环,执行上面打印100个元素的数组时,我们明确知道有100个元素,所以在i<100的条件内循环,但是目前这个数组,我们第一使劲按不能确定有多少个元素,这里为嘛呢可以用这样一个表达式来经计算出数组元素的多少
int sz = sizeof(array)/sizeof(array[0]);
//这里相当于创建了一个整形变量,来储存表达式sizeof(array)/sizeof(array[0])的结果
/*对于等号后面的表达式:sizeof(),
如果括号里面的是单个数组名,那么sizeof(array)计算的就是整个array数组的大小(单位字节)
如果括号里面的是单个数组元素,那么计算的四则偶分(array[0])计算的就是这个元素的大小(单位字节)
*/
那么下面就是打印int arr[] = {1,2,3,4,5,6,7,8,9,1,2,3}这个数组的代码
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,1,2,3 };
int sz = sizeof(arr) / sizeof(arr[0]);//这里计算出了数组元素的个数
int i = 0;
for (i = 0; i < sz; i++)
{
printf("arr[%d] = %d\n", i,arr[i]);
}
return 0;
}
数组越界
数组是有边界的
数组在使用时需要注意不要超过了数组的下标,即如果你创建的数组是arr[20],那么在下标调用时,就使用0-19的下标,否则就造成了数组越界
我们知道数组在创建按的时候使用的是一块连续的空间,所以数组的几个元素的地址是连续的,当出现数组越界时,
还是举这个例子:
int arr[20]= {0};
//当我们在打印的时后错误使用了:
printf("%d",arr[-1]);
printf("%d",arr[20]);
//这两种下标都超出了数组的边界
//这时arr[-1]的地址在数组地址前面
//arr[20]的地址在数组地址后面
//如果arr[-1]的地址上没有赋值,那么打印出来的就是一堆乱码,arr[20]也一样
编译器一般不会检查数组越界这样的问题
二维数组的创建与初始化
二维数组的创建
二维数组的创建与一维数组有很多相似的地方:比如数组类型、数组名、[ ]操作符。
也有不同的地方:
int arr[2] = {1,2};
int arr1[2][2] = {1,2,3,4};
二维数组在创建时多了一个[ ]符号,相当于多创建了一个维度,
二维数组里面的总元素个数扽等于两个括号里面数的乘积
int arr[3][4];//有 12 个元素
int arr[5][5];//有 25 个元素
这样的一个 数组也可以理解为:arr是一个大集合,下面包含了三个小集合arr[0]、arr[1]、arr[2],每个集合小面有包括了几个更小的集合
二维数组的初始化
二维数组初始化的方式有很多种,不同的方式有不同的初始化效果
int arr[3][5] = {{1,2},{1,2},{3,2}};
可以看到,这样的初始化是让arr[0]的前几个元素赋值,arr[1]的前几个元素赋值,arr[3]的前几个元素赋值,其余的元素就被赋值为0;
二维数组的储存
二维数组在储存上和一维数组很相似,都是取用连续的空间,但是二维数组储存的“连续”是值上一个小集合的元素地址后面紧跟下一个小集合的第一个元素地址
这张图片展示了
arr[0] [4]的地址是:0x00AFF834
arr[1] [0]的地址是:0x00AFF838
两个地址是连续的,证实了上面的结论。
二维数组行可以省略,但是列不能省略
int arr[2][3] ={1,2,3,4,5,6}//一个含有六个元素的数组
int arr[][3] = {1,2,3,4,5,6}//与上面的结果相同
int arr[2][] = {1,2,3,4,5,6}
/*第三个是错误的,会造成歧义,系统只知道要创建两个六个空间分两部分使用,
但是不知道每一个空间分配多少个元素,造成报错*/
数组与函数
数组元素作为函数的参数
数组元素可以做函数的实参,但是不能做形参(形参是临时分配空间按单元的,不会单独为一个数组元素分配一个单元)。传递时,只是把数组元素的值传给形参,这里我们用一个例子来说明数组元素作为函数实参的“值传递”
例题:在一个数组中输入十个数,并且打印出十个数中最大的一个数
#include<stdio.h>
int MAX(int x,int y)
{
return x > y ? x : y;
}//比较函数
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", &arr[i]);
}//读取十个数
int max = 0;
for (i = 0; i < 10; i++)
{
max = MAX(arr[i], max);
//arr[i]作为函数实参,将arr[i]的值传给了形参x
}//作比较
printf("max = %d\n", max);
for (i = 0; i < 10; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
//输入1 2 10 9 8 7 6 5 4 3
//打印的是:max = 10;
可以看出每个数组元素在作为函数实参之后数值并没有改变,可以证实,在数组元素作为实参时,只是将自己的值传递给了形参。
数组名作为函数的参数
与刷组元素不同的是,数组名不仅可以做函数的实参,也可以做函数的形参
用数组名做函数的实参时,传递的是首元素的地址,很多时候数组名都代表首元素的地址
同样,这里举一个例子来说明数组名做参数的情况
#include<stdio.h>
int average(int arr[])
//实参对应的形参指定和实参一样的类型
//形参的数组可以不指定大小
//
{
int i = 0;
int sum = arr[0];
for (i = 1; i < 10; i++)
{
sum += arr[i];
}
return sum / 10;
}
int main()
{
int score[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
scanf("%d", &score[i]);
}
int aver = average(score);//函数名做实参
printf(" %d\n", aver);
return 0;
}
可以认为,形参数组首元素的地址和实参数组的首元素地址相同,他们沟通占有同一单元,实参数组和对应的形参数组具有相同的值。