C语言 数组入门级别讲解(另含指针与数组)
写在前面:
考虑到数组与指针的关系,我是先把指针第一部分讲完,让大家对指针有个基本的了解后再想大家介绍数组,我觉得这样更好的了解数组和指针的一些关系。本篇将会向大家介绍数组的基本知识以及数组和函数的一些关系。
附一下上篇的地址:C语言-入门级别指针详解(上)
1.一维数组
数组用来储存一类相同类型的数据的,数组与其中的数据呈现以下的关系:
1.1一维数组的声明和初始化
🔆 数组的声明
type 数组名[常量];
- type:决定了你在数组中想存储的数据类型
- [常量]:常量的大小决定了这个数组的容量大小
☕️ 举例:
char arr[4];这里是给名字为arr的数组申请了4个char类型大小的内存空间,可存放4个char型数据。
int arr2[10];这里是给名字为arr2的数组申请了10个int类型大小的内存空间,可存放10个int型数据。
float arr3[2];这里是给名字为arr3的数组申请了2个int类型大小的内存空间,可存放2个float型数据。
🔆 数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
数组初始化有两种形式,每一种都相差不大:
🍋在初始化时确定数组的长度和内容,如:
int a[10]={1,2,3,4,5,6,7,8,9,10};
char b[10]="hello"
⚠️这样写的话要注意后面初始化的内容不能超过数组的长度给个错误示例:int a[4]={1,2,3,4,5};
这里給数组a开辟了4个整形大小的空间但却使用了5个整形,造成了数组越界的问题。
⚠️对于fint a[10]={1};
这类未完全初始化的整形数组系统会自动将后面的值初始化为0,而对于字符数组b后面的值则会初始化为\0。
🍋 在初始化时只确定内容,如:
int a[]={10}
char b="ok"
这时系统会自动计算数组中的大小并将它作为数组的长度,于是数组a的大小为一个整形的大小,而数组b的大小为3个字符型的大小(输入字符串时会默认在结尾加上’\0’,上面等价于:“ok\0”)
1.2一维数组的使用
🔆 数组的下标
数组中的每一个元素都有对应的编号,用数组名+[下标]表示
对于int arr[10]={1,2,3,4,5,6,7,8,9,10,};
arr数组内部如下:
⚠️数组的下标是从0开始的
🔆计算数组的大小
🍑数组所占内存的大小
数组的大小即为数组中的元素个数乘于单个元素所占的内存大小,即n*size(type)
而在我们真正计算是要用到关键字sizeof,它的功能就是求数组的内存大小,以下为它的工作效果:
int main()
{
char a[10] = "hello";
int b[10] = { 1 };
printf("字符数组a[10]的大小:%zu\n", sizeof(a));
printf("整形数组b[10]的大小:%zu", sizeof(b));
//虽然打印sizeof用%d也行,但推荐用%zu,
return 0;
}
🍐 计算数组中的元素个数
数组的大小除于每一个元素的大小即为元素个数n=sizeof(arr)/sizeod(arr[0])
🔆 数组的输入
对数组的遍历需要使用循环,对数组中的每一个元素依次赋值。
现在对一个整形数组来输入:
int main()
{
int arr[10];
for(int i=0;i<10;i++)
{
scanf("%d",a[i])
}
return 0;
}
2.二维数组
二维数组可以理解为多个长度相同的一维数组的组合
形象的理解可以将二维数组理解为一张表格,下图为a[2][3]={{1,2,3},{4,5,6}}中的数据:
如果把表格拿开就成了这样:
这可以把a[2][3]看成是数组名叫a[0]和a[I]的两个数组,而且这两个数组的长度相同。
2.1二维数组的声明和初始化
🔆 二维数组的声明
type 数组名[行数][列数];
相对于一维数组,二维数组多了一个下标应用操作符"[]".
🔆 二维数组的初始化
二维数组初始化有以下三种方式:
🍑 初始化时确定大小和内容:
int a[2][3]={1,2,3,4,5,6};
int a[2][3]={{1,2,3},{4,5,6}};
注:一个大括号就是一行,如果没有大括号这会在数组列数到达上限时自动转换到下一行中,如上述两个例子的a[1][0]元素均为’4’.
🍐只确定列数和内容,由系统自动换行
如int a[][3]={1,2,3,,4,5,6}
;在第一行a[0][2]填入第三个元素’3’时,由于到达了列数的上限,则下一个元素’4’会填充到a[1][0]中。
2.2一维数组的使用
二维数组的两个下标都是从0开始计数的
以 int a[2][3]={1,2,3,4,5,6};
为例,其下标对应值如下:
🔆 二维数组的输入
二维数组的输入需要两个循环嵌套来实现对每一个值的输入
现在对一个整形数组来输入:
int main()
{
int arr[10][10];
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
scanf("%d",a[i][j]);
}
}
return 0;
}
3.数组的储存
数组在内存中的储存是连续储存的,而数组名就是首元素的地址。
用代码展现其地址:
int main()
{
int arr[5] = {0};
int sz = sizeof(arr)/sizeof(arr[0]);
for(int i=0; i<sz; ++i)
{
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
🍌运行结果:
&arr[0] = 012FF704
&arr1 = 012FF708
&arr[2] = 012FF70C
&arr[3] = 012FF710
&arr[4] = 012FF714
可以看到数组中的每个元素地址间相差4(byte),而4刚好是一个整形数据的长度,说明数组中的数据是连续的。
二维数组与一维数组是基本一样的,它可以看成多个一位数组,而且这些数组都是相连续的,直观图如下:
4.arr &arr &arr[0]三个地址的区别
🔥arr:数组中首元素的地址
🔥&arr:整个数组的地址
🔥&arr[]:数组首元素地址
int main()
{
int arr[10] = { 0 };
printf(" arr =%p\n", arr);
printf(" &arr =%p\n", &arr);
printf("&arr[0]=%p\n", &arr[0]);
puts("");
printf(" arr+1 =%p\n", arr + 1);
printf(" &arr+1 =%p\n", &arr+1);
printf("&arr[0]+1=%p\n", &arr[0]+1);
return 0;
}
🍌运行结果:
arr =005FF6FC
&arr =005FF6FC
&arr[0]=005FF6FC
arr+1 =005FF700
&arr+1 =005FF724
&arr[0]+1=005FF700
这里可以看到 打印他们的地址是相同的,而打印下一个地址式差异才显示出来:
arr 从005FF6FC变到了005FF700增加了4,是一个整形数据的长度
&arr 从005FF6FC变到了005FF724 增加了40(16+16+4+4),是10个整形的长度刚好是地址的长度。
&arr[0]从005FF6FC变到了005FF700,增加了4,和arr是一样的。
🍎总结:
arr:是首元素地址,它的下一个地址是下一个元素的地址
&arr[0]:首元素地址,和arr的效果一样
&arr:整个数组的地址,他的下一个地址是整个数组最后一个元素地址的下一个地址
⭐️有两个例外arr不是首元素地址:
1️⃣使用sizeof关键字求数组长度:sizeof(arr)/sizeof(arr[0]).
2️⃣上面提到的&arr中arr也不是首元素的地址。
5.数组与指针
在定义一个数组时系统会为该数组在内存中找到一个存储空间,而数组的名字就是这块内存的首地址。
看到这里你是否有些思考或者有些许疑惑?内存,地址······指针?对的少年,你没猜错,数组可以在一定程度看成指针,下面我们来仔细看看他们的关系吧:
🍈 现在简单的使用指针来进行数组的遍历:
int main()
{
int arr[10];
int* p = &arr[0];
for (int i = 0; i < 10; i++)
{
scanf("%d", p);
p++;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
}
小练手
题目要求:运用指针完成数组逆序函数void arr_reverse(int *a,int b)
功能效果:将数组a[6]={1,5,2,3,4,7}的顺序改为{7,4,3,2,5,1}
🍬代码:
void arr_reverse(int* a, int right)//a来接受数组,right接受数组的元素个数
{
int left = 0;
while (left < right)
{
int tmp = *(a + left);
*(a + left) = *(a + right);
*(a + right) = tmp;
left++;
right--;
}
}
如果觉得简单的话就再思考一下这道题:
实现字符数组逆序函数void string_reverse(int *string);要求不能使用字符串的库函数
🎍代码:
void reverse_string(char* string)//string接受字符数组
{
int right = 0;
int left = 0;
char tmp = '\0';
while (*(string + right) != '\0')
{
right++;
}
if (right > 0)
right--;
if (left < right)
{
tmp = *(string + left);
*(string + left) = *(string + right);
*(right + string) = '\0';
reverse_string(string + 1);
}
*(string + right) = tmp;
}
写在最后
本篇介绍了数组还有其与指针的一些关系,读起来可能有点枯燥(小YU已经很努力的压缩内容了QAQ),文章主要目的是帮助初学者迅速了解和巩固知识,这次留了道小题的目的是让大家思考一下,不懂也没事,毕竟进来学习的小伙伴们肯定是很努力的,咱们一起加油,一定可以追逐到心中那个身影!