目录
数组
1.数组是什么?
数组是一组相同类型元素的集合。
比方说将int类型的1、2、3、4、5放在一个数:int arr[5]={1,2,3,4,5}
2.一维数组的创建和初始化
2.1一维数组的创建
type_t arr_name [const_n]
//type_t 元素类型 int、char等
//arr_name 数组名
//[const_n] 数组大小 :是一个常量表达式,用于指定数组的大小
[]叫做下标引用操作符
int main()
{
int arr[10]; //整形数组的创建
char arr[5]; //字符数组的创建
char arr[3+2] //用表达式指定数组大小
//特殊情况: //用变量表示数组大小
int n;
scanf("%d",&n);
int arr[n]; //设置一个变量,通过手动输入控制数组大小
}
注意:
//①在C89、C90的标准中,不允许使用变量表示数组的大小。
int arr[] ; []中只能给一个常量:4、10、100等(可以是计算式:3+2等)
//②C99后,添加了“变长数组”这一概念,此后可以使用变量指定数组大。
例如:int n ; scanf("%d",&n) ; int arr[n];
//③目前仍有一些开发环境不支持C99的“变长数组”,例如:VS2019、VS2022
2.2一维数组的初始化
什么是初始化?
在一维数组被定义的时候,就给数组赋一些初始元素。
例如:int arr[5] = {1,2,3,4,5}; 定义了一个整形数组 arr,大小为5,初始元素有1,2,3,4,5
int main()
{
int arr1[10] = { 1,2,3 }; //不完全初始化,剩余的元素默认为0;
int arr2[5] = { 1,2,3,4,5 }; //完全初始化
char arr3[3] = { 'a',98,'c' }; //98是字符b对应的ASCII码值
char arr4[3] = { 'a','b','c' }; //这两个数组元素相同
int arr5[] = { 1,2,3 }; //完全初始化时,可以省略数组大小,自动根据初始化元素配置大小
例如:int arr6[] = { 0 }; //数组大小为1,元素为0
int arr6[]={1,3,5}; //数组大小为3,元素为1、3、5
char arr7[] = { 'a','b','c' }; //数组内没有'\0'
char arr8[] = "abc"; //字符串自带结束标记"\0"
//实际上arr8[]={'a','b','c','\0'};
return 0;
}
//对于一个已经被初始化了数组,但是你不知道这个数组的大小,这时一定有人会一个元素一个元素的去数,其实有更简单也更符合标准的办法,那就是使用sizeof
原理:用整个一维数组的在内存中占用的字节数,除以一个元素占用的字节数,就得到了元素个数
int main()
{
int arr[]={1,2,4,9,10,15,34,20,18,37};
int len=sizeof(arr)/sizeof(arr[0]); //sizeof(arr)计算原理,本文下面有论述
printf("%d",len);
//打印结果实际为10,上述数组的元素个数的确为10
}
3.访问一维数组中的元素
想必你也发现了:对于数组 arr[10],[] 其实是一个操作符,叫做下标引用操作符
[] 的使用方式为:一个数组名 + 下标索引值
如此便可以实现对数组中元素的访问。
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //C语言规定,数组的下标从0开始
//0,1,2,3,4,5,6,7,8,9 此为下标值
//通过下标打印数组中的元素:
printf("%d\n", arr[6]); //打印结果为7
//通过下标修改对应元素的值:(也可以说是赋值)
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //获取元素个数
for (i = 0; i < sz; i++)
{
arr[i] = 10 - i; //例如将元素大小与i的值挂钩
}
for (i = 0; i < sz; i++) //打印下标对应的元素
printf("%d ", arr[i]);
//打印结果为10 9 8 7 6 5 4 3 2 1
}
4.一维数组在内存中的存放
数组在内存中是怎么存放的呢?
我们首先用以下代码打印数组中每个元素的地址。
//我们使用&获取数组在内存中的地址:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]); //获取元素个数
for (i = 0; i < sz; i++)
printf("&arr[%d]=%p\n", i, &arr[i]); //打印地址
}
打印结果如下:
注意:为了便于观察使用的是32位的机器打印、地址是以16进制的形式打印出来,单位是字节
上图打印结果显示:数组中每个元素见地址相差 4 ,而这正好是 int 类型在内存中占用的空间
// //说明1:数组在内存中是连续存放的
// // 2:随着下标的增长,地址是由低到高变化的
5.二维数组的创建和初始化
5.1二维数组的创建
类似于一维数组:
type_t arr_name [const_m][const_n]
//type_t 元素类型 int、char等
//arr_name 数组名
//[const_m] 数组行索引:是一个常量表达式,用于指定数组的行数
//[const_n] 数组列索引:是一个常量表达式,用于指定数组的列数
[]叫做下标引用操作符
//注意二维数组实际上是没有行列之分的,只是便于描述,便于理解才这样称呼的
int main()
{
int arr[3][4];
char arr1[2][5];
}
5.2二维数组的初始化
与一维数组有相同点:
int main()
{
int arr2[3][5] = { 0 }; //所有元素均为0;
int arr3[3][5] = { 1,2,3,4,5,11,12,13,14,15,21,22,23,24,25 };//完全初始化,先行后列
int arr4[3][5] = { {1,2,3},{ 2,3,4,5} ,{ 3,4,5,6,7 } };//指定行元素内容,多余位置为0
int arr5[][10] = { {1,2},{2,3,4},{5,5,5} };//有3行,每一行10个元素,未被初始化的元素默认为0
//对于二维数组,若进行初始化,则行标可以省略,列标不能省略
//行标和列标都是从0开始的
//知道行标和列标就可以访问指定元素
}
需要注意的点:
①二维数组标明行标和列标后,初始化的元素会自动按照行列分配
②可以在初始化时,指定某一行放哪些元素,若该行有剩余的位置,则默认为0
③二维数组的行标可以省略,列标不能省
6.访问二维数组中的元素
类似于一维数组:
若想访问二维数组中的元素,也需要使用下标引用操作符:[]
访问方式为:一个数组名 + 行标索引值 + 列标索引值
int main()
{
int i, j;
int arr[2][3];
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
scanf("%d", &arr[i][j]); //从每一行的每一列依次 录入元素
}
//假设我们输入的是1,2,3,4,5,6
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
printf("%d ", arr[i][j]); //从每一行的每一列依次 打印
printf("\n"); //体现每一行,实际存放时,并没有行列之分,而是连续存放
}
运行结果如下:
7.二维数组在内存中的存放
同样地,这里使用取地址运算符&,获得每个元素的地址:
int main()
{
int i, j;
int arr[2][3] = { 1,2,3,4,5,6 };
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
}
}
运行结果如下:
可以发现
元素的地址之间也是正好相差 4 个字节
说明在二维数组中,元素是依次在地址中排列的,没有所谓行列分布
期望排布: 实际排布:
因此,二维数组中,所有元素在内存中都是连续存储的,在内存中不存在行列之分
二维数组其实是 一维数组的数组
///即:二维数组内存放的是多个一维数组
这个一维数组元素个数就是列标
一维数组的数组名就是arr[0]、arr[1]....
8.数组越界
什么是数组越界呢?
我们都知道,内存是连续的线性空间,当我们创建数组时,会向内存申请存储空间用于存放数组
例:int arr[10] = {1,2,3,4,5,6,7,8,9,10};
这个数组的下标从0开始,范围【0,10)也就是0—9的下标。当使用这个范围以外的下标值访问时,便会产生数组越界。
由于我们不知道范围外存放的是什么、也不知该区域是否允许访问。当我们访问时便可能产生或大或小的问题。
当我们在写程序时,如果存在越界访问,编译器在编译时并不会报错,程序会照常运行,但是运行结果往往是错误的,例如:
int main()
{
int i=0;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //下标范围:0-9
for (i = 0; i <= 10; i++) //i=10时,发生越界访问
{
printf("%d ",arr[i]); //打印结果会发生错误
}
printf("\n\n");
//编译器一般不会发现数组越界访问的操作,所以程序能运行
程序运行结果如下:
//所以程序员写代码时,注意要防止越界访问的操作出现。