/知识点汇总/
概念:
数组就是一组相同类型元素的集合,或者说表示类型相同的数据
数组的创建方式:
type_t arr_name[const_n];
//type_t ---- 数组的元素类型
//arr_name ---- 数组名
//const_t --- 常量表达式,指定数组的大小
举例说明数组的创建:
#include
int main()
{
int arr[5];
int arr2[2 + 3];
char arr3[8];
//C99之前数组只能是常量来指定大小
//C99之后引用了变长数组的概念,数组的大小就可以使用变量来指定了
//但是C99标准下,数组的大小可以使用变量指定,但是数组不能初识化
//但是Vs2022/Vs2019 不支持C99的变长数组
int n = 0;
scanf("%d",&n);
int arr4[n];
return 0;
}
概念:
数组的初识化是指,在创建数组的同时给数组的内容一些合理初始值(初识化)
#include
int main()
{
int arr[10] = { 1,2,3 };//不完全初识化,剩余元素默认补0
int arr2[4] = { 1,2,3,4 };//完全初始化
char arr4[3] = { 'a',98,'c' };
int arr3[] = {1,2,3};//省略数组大小, 由编译器自行判断数组内的元素
char arr7[] = {'a','b','c'};//'/0'字符串结束标志
char arr8[] = "abc";
return 0;
}
[] —> 下标引用符
#include
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n",arr[5]);//6
//通过[]下标引用操作符,打印数组元素
int i = 0;
//for (i = 0; i < 10; i++)
//{
// printf("%d\n",arr[i]);
//}
//计算数组元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
//更改元素
for (i = 0; i < sz; i++)
{
arr[i] = 10 - i;
}
for (i = 0; i < sz; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
观察存储情况
#include
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]);
//&arr[0] = 0019F908
//&arr[1] = 0019F90C
//&arr[2] = 0019F910
//&arr[3] = 0019F914
//&arr[4] = 0019F918
//&arr[5] = 0019F91C
//&arr[6] = 0019F920
//&arr[7] = 0019F924
//&arr[8] = 0019F928
//&arr[9] = 0019F92C
//观察结果可知,相邻元素之间相差4,单位字节
}
return 0;
}
小结:
(1)、数组在内存中是连续存放的
(2)、随着下标的增长,地址是由低到高变化的
[ ][ ] – 几行几列
#include
int main()
{
int arr[3][5];//整型二维数组创建
char arr2[4][10];//字符型二维数组创建
int arr3[2][3] = {0};
//char arr4[3][5] = {1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};
char arr4[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
//大括号的作用约束,元素是否按照顺序放满之后再存放下一行
int arr5[4][10] = { {1,2},{2,3,4},{5,5,5} };
//注意:行可省略,列不可以省略
//因为列确定一行所放的元素,行根据初识化几行就放几行
int arr6[][3] = {1,2,3,4,5,6,7,8,9};//3行3列
return 0;
}
小结:
(1)、对于二维数组,如果初识化了,对于行数是可以省略的,但列是不能省略的
(2)、大括号的作用约束,元素是否按照顺序放满之后再存放下一行
二维数组的访问依然是通过下标进行访问
#include
int main()
{
int arr[3][5] = { {1,2},{4,5},{6,7,8} };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}
#include
int main()
{
int arr[3][5] = { {1,2},{4,5},{6,7,8} };
int i = 0;
//数组元素1~15
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
arr[i][j] = i * 5 + j + 1;
}
}
//打印1~15数组元素
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
#include
int main()
{
int arr[3][5] = { {1,2},{4,5},{6,7,8} };
int i = 0;
//打印数组元素
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("&arr[%d][%d] = %p\n",i,j, &arr[i][j]);
//&arr[0][0] = 00AFFC00
//&arr[0][1] = 00AFFC04
//&arr[0][2] = 00AFFC08
//&arr[0][3] = 00AFFC0C
//&arr[0][4] = 00AFFC10
//&arr[1][0] = 00AFFC14
//&arr[1][1] = 00AFFC18
//&arr[1][2] = 00AFFC1C
//&arr[1][3] = 00AFFC20
//&arr[1][4] = 00AFFC24
//&arr[2][0] = 00AFFC28
//&arr[2][1] = 00AFFC2C
//&arr[2][2] = 00AFFC30
//&arr[2][3] = 00AFFC34
//&arr[2][4] = 00AFFC38
//通过结果可知,相邻的元素间隔是4,单位字节
//连续存放的,第一行放完接着第二行放
//行数可以省略,列数不可以省略
//随下标的增长,地中也是由低到高的存放的
}
printf("\n");
}
return 0;
}
小结:
(1)、通过结果可知,相邻的元素间隔是4,单位字节
(2)、连续存放的,第一行放完接着第二行放
(3)、二维数组可理解为一维数组的数组
(4)、行数可以省略,列数不可以省略
(5)、随下标的增长,地址也是由低到高的存放的
(6)、二维数组名:
如:arr[m][n] ---->数组名为arr[m]
数组的下标规定是从0开始的,如果有n个元素,那么最后一个元素的下标就是n-1,
所以,当数组的下标小于0或大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
另外,C语言本身并不会进行越界检查,部分编译器也不一定会进行报错,容易导致程序出现难以发现的bug
#include
int main()
{
int arr[10] = {1,2,3,4,5,6};//下标0~9
int i = 0;
for (i = 0; i <= 10; i++)//下标=0时,数组越界
{
printf("%d ",arr[i]);//数组越界
}
return 0;
}
数组可以作为函数的参数
经典的冒泡排序
核心思想:两两相邻的元素比较
冒泡排序的思路:
第一趟:
10 9 8 7 6 5 4 3 2 1
9 10 8 7 6 5 4 3 2 1
9 8 10 7 6 5 4 3 2 1
......
9 8 7 6 5 4 3 2 1 10//此时完成一趟冒泡排序,将第一个元素排到了最后
第二趟:
8 9 7 6 5 4 3 2 1 10
8 7 9 6 5 4 3 2 1 10
.....
8 7 6 5 4 3 2 1 9 10//此时完成二趟冒泡排序,将第二个元素排到了最后
......
依次类推:
直至完成升序排序
数组作为函数的参数例程 — 冒泡排序
#include
int main()
{
int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
//将arr数组进行排序,排序为升序
//冒泡排序
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
//趟数
for (i = 0; i < sz-1; i++)
{
int j = 0;
for (j = 0; j < sz - 1-i; j++)// sz - 1-i元素比较的对数
{
if (arr[j] > arr[j + 1])//交换
{
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
数组作为函数的参数例程 — 函数写法 – 待优化
#include
void sort(int arr[] ,int sz)
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)// sz - 1-i元素比较的对数
{
if (arr[j] > arr[j + 1])//交换
{
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
//将arr数组进行排序,排序为升序
//冒泡排序
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
小结:
发现数组传参过去,代码执行结果与预想不同
探讨数组传参
数组名就是地址,通常来说数组名就是数组首元素的地址
但是有两个例外:
(1)、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
(2)、& — 取地址操作符 —> &数组名,表示整个数组,取出的是整个数组的地址
除此之外,所遇到的数组名都是数组首元素地址
数组名例程
#include
int main()
{
int arr[10] = { 0 };
printf("%p\n",arr);
printf("%p\n", &arr[0]);
printf("%d\n", sizeof(arr));
//对数组名进行加1的情况
printf("%p\n", arr);
printf("%p\n",arr+1);//+4
printf("%p\n", &arr[0]);
printf("%p\n",&arr[0]+1);//+4
printf("%p\n",&arr);
printf("%p\n",&arr+1);//+28(十六进制) --> +40(十进制),加的整个数组大小
return 0;
}
小结:
(1)、sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
(2)、& — 取地址操作符 —> &数组名,表示整个数组,取出的是整个数组的地址
(3)、数组传参的本质就是传首元素的地址
探讨了数组名作为参数的知识,再回到冒泡排序
数组作为函数的参数例程 — 冒泡排序函数写法 — 优化
#include
//首元素地址,本质就是指针接收参数int arr[] ----> int* arr即可等价
void sort(int* arr ,int sz)
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)// sz - 1-i元素比较的对数
{
if (arr[j] > arr[j + 1])//交换
{
int temp = 0;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main()
{
int arr[] = { 10,9,8,7,6,5,4,3,2,1 };//降序
//将arr数组进行排序,排序为升序
//冒泡排序
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
sort(arr,sz);//学习了什么是数组名,可知这里的arr,不是特殊情况,就是数组的首元素地址
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
半亩方糖一鉴开,天光云影共徘徊。
问渠哪得清如许?为有源头活水来。–朱熹(观书有感)