序言
本片博客会从以下几个方面带大家了解一下数组
1.一维数组的创建和初始化;2.一维数组的使用;3.一维数组在内存中的存储
4.二维数组的创建和初始化;5.二维数组的使用;6.二维数组在内存中的存储
7.数组越界;8.数组作为函数参数;
接下来本博主带大家一一领略
一、一维数组的创建和初始化
数组是一组相同类型元素的集合。
数组的创建方式:
type_t arr_name [const_n];
// type_t 是指数组的元素类型
//arr_name指数组名
// const_n 是一个常量表达式,用来指定数组的大小
这里我们注意:
C99 标准之前 数组的大小只能是常量表达式
C99 标准中引入了:变长数组的概念,使得数组在创建的时候可以使用变量,但是这样的数组不能初始化
下面就是一个简单数组创建:
int arr1[10];
char arr1[];
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
下面是数组的简单初始化:
int arr1[10] = {1,2,3,4,5,6,7,8,9,10};//完全初始化
int arr2[10] = { 1,2,3 };//不完全初始化,剩余的元素默认都是0
int arr3[10] = { 0 };//不完全初始化,剩余的元素默认都是0
int arr4[] = { 0 };//省略数组的大小,数组必须初始化,数组的大小是根据初始化的内容来确定
int arr5[] = { 1,2,3 };
char arr1[] = "abc";//一个元素
char arr2[] = {'a', 'b', 'c'};//三个元素
char arr3[] = { 'a', 98, 'c' };
初始化时一定是需要给一些合理的初始值
int arr6[];//错误初始化
二、一维数组的使用
数组必须先定义,然后使用。C 语言规定,只能逐个引用数组元素,而不能一次引用整个数组。前面讲过,数组元素的表示形式为:
数组名[下标];
下标可以是整型常量或整型表达式,比如:
a[0] = a[5] + a[7] - a[2 * 3];
千万要注意,定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“数组名[下标]”是有区别的,定义数组时的常量表达式表示的是数组的长度,而引用数组元素时的下标表示的是元素的编号。比如:
arr[5]={1,2,3,4,5}
//表示有五个元素,分别存储1,2,3,4,5
//下标从0开始,到你所定义的数组长度-1结束,此处是5;
//a[0]=1,a[1]=2,a[2]=3,a[3]=4,a[4]=5;
关于数组在scanf()与printf()中的使用,一般借助for语句
int i = 0;
int arr[5] = {1,2,3,4,5};
for(i = 0;i<5;i++)
{
scanf("%d",&arr[i]);
}
for(i = 0;i<5;i++)
{
printf("%d",arr[i]);
}
这里注意:由于在使用时表示的时元素的编号,所在输入时要加上&
三、一维数组在内存中的存储
数组在内存中的存储是连续的,我们写一个代码给大家看一下就明白了
int main()
{
int arr[10] = { 1,2,3,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
这时候我们运行一下
打印坐标后我们发现每个坐标中间相差4,那是因为我们这里用的是int,所以占4个字节
仔细观察输出的结果,可知随着数组下标的增长,元素的地址,也在有规律的递增。 由此可以得
出结论:数组在内存中是连续存放的。
总结:
- 数组在内存中开辟是线性连续且递增的。
- 在c语言中,任何变量(基本变量,指针变量,结构体变量,数组变量)的空间都是整体开辟,但任何元素的起始地址一定是开辟字节当中最小的。
四、二维数组的创建和初始化
二维数组的创建
//数组创建
int arr[3][4];//[行数][列数]
char arr[][5];
double arr[2][4];
二位数组的初始化
//数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};
注意:
花括号中的一个花括号代表一个一维数组的初始化。当里面无花括号分组时,按照顺序从第一个开始逐个进行初始化。余下的未赋值的元素用0初始化
五、二维数组的使用
二维数组的使用也是通过下标的方式,用双重循环嵌套进行索引使用。 看代码:
#include <stdio.h>
int main()
{
int arr[3][4] = { 0 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
arr[i][j] = i * 4 + j;
}
}
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
}
return 0;
}
六、二维数组在内存中的存储
这里我们依旧打印地址看一下,代码如下
//int main()
//{
// int arr[4][5] = { 0 };
// int i = 0;
// //行号
// for (i = 0; i < 4; i++)
// {
// int j = 0;
// for (j = 0; j < 5; j++)
// {
// printf("&arr[%d][%d] = %p\n",i,j, &arr[i][j]);
// }
// }
// return 0;
我们来看一下运行结果
通过结果我们可以分析到,其实二维数组在内存中也是连续存储的。
注意:
- 二维数组在内存的空间布局上,也是线性连续且递增的!!!
- 二维数组本质上也是一维数组,只不过内部元素放的是一维数组。
七、数组越界
在C语言中,我们可以直接通过数组下标来访问数组中的元素;
如果一个数组定义为有n
个元素,那么,对这n
个元素(下标为0
到 n-1
的元素)的访问都合法,如果对这n
个元素之外的访问,就是非法的,称为越界,例如:
int a[5] = {0}; //等价 int a[5] = {0,0,0,0,0};
a[0] = 1; // ok
a[1] = 2; // ok
a[2] = 3; // ok
a[3] = 4; // ok
a[4] = 5; // ok
a[5] = 6; // 数组下标越界
在上面代码中,声明一个数组a[5]
,该数组中只能存放5
个元素,下标索引值取值范围0~4
,超过这个范围就属于下标越界
那么访问越界会出现什么结果?
首先,它并不会 造成编译错误!就是说,C
,C++
的编译器并不判断和指出你的代码访问越界了。一个明明是错误的东西,就这样“顺利”地通过了编译;
数组访问越界在运行时,它的表现是不定的,有时似乎什么事也没有,程序一直运行(当然,某些错误结果已造成);有时,则是程序一下子崩溃。因此在使用数组时,一定要在编程中判断是否越界以保证程序的正确性
那么我们应该如何避免数组越界呢?
如果数组的长度和下标访问值弄错,都会造成数组下标越界;数组的下标是从0开始的,最大的访问值是数组的长度-1;
//如果是整形数组
int len = sizeof(array)/sizeof(int);
//如果是字符数组
int len = sizeof(array)/sizeof(char);
//如果是浮点数数组
int len = sizeof(array)/sizeof(double);
//如果是浮点数数组
int len = sizeof(array)/sizeof(float);
for(int i = 0;i < len ; i++)
{
//.....
}
八、数组作为函数参数
- 调用函数传参数组时,减少函数传数组时的成本问题(时间和空间)。因为传参时,需要临时拷贝,如果数组过大,可能会浪费资源,严重的话可能栈溢出。
- 数组元素降维成指向数组内部元素类型的指针。
- 对指针加一,加上所指向的类型的大小。
数组可以由整个数组当作函数的参数,也可以由数组中的某个元素当作函数的参数:
整个数组当作函数参数,即把数组名称传入函数中,例如:
数组可以做为函数形参传入进行处理,此时的数组变量退化为数组首元素的地址。其他读写操作可以类推,但是长度不能导入。
int test(int array [],int len)
{
int sum = 0;
for (int i = 0; i < len; i++)
{
array[i]+=3;
}
return sum;
}
int main()
{
int array[] = { 1,2,3,4 };
int sum = test(array,sizeof(array)/sizeof(int));
//printf("%d", sum);
int len = sizeof(array)/sizeof(int);
for (int i = 0; i < len; i++)
{
printf("%d", array[i]);
printf("\r\n");
}
return 0;
}
数组作为函数参数时注意以下事项:
1、数组名作为函数实参传递时,函数定义处作为接收参数的数组类型形参既可以指定长度也可以不指定长度。
2、数组元素作为函数实参传递时,数组元素类型必须与形参数据类型一致。