【参考课程:B站 BV1cq4y1U7sg BV1Vm4y1r7jY】
//数组-一组数 //数组:一组相同类型元素的集合
目录
1.一维数组
一维数组的创建与初始化
数组的创建
type_t arr_name[const_n];
//type_t 数组元素类型
//const_n 常量表达式,用于指定数组大小
E.g.
int main() { //int整型类型的 含6个元素的 名称叫arr的数组 int arr[6]; //char字符型类型的 含7个元素的 名称叫ch的数组 char ch[7]; //【注意】!!! 创建数组时 [ ] 内只能放常量表达式! //常变量也不行!!! int n = 8; int arr错误[n]; //这样是错误的!!! //*C99语法支持-变长数组 --数组的大小是变量 return 0; }
数组的初始化
//初始化:创建并赋值
int main()
{
/*完全初始化*/
//整型数组的创建
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3}; //没有给定数组大小时,数组根据初始化元素个数来确定大小
//字符型有两种创建模式
//①一个一个元素输入创建
char ch1[7] = { 'm','e','t','t','r','i' };
char ch2[] = { 'm','e','t','t','r','i','\0' };
//②以字符串的形式输入创建 //!!!以字符串形式创建时,最后隐含了一个结束符 [\0]
char ch3[7] = "mettri"; //[m][e][t][t][r][i][\0] --7个元素
char ch4[] = "mettri"; //[m][e][t][t][r][i][\0] --7个元素
/*不完全初始化*/
int arr3[10] = { 1,2,3,4,5 };
char ch5[6] = { 'm','e','t' };
char ch6[6] = "met"; //[m][e][t][\0] --4个元素
return 0;
}
完全初始化与不完全初始化
【整型数组】不完全初始化的空位用 [0] 填充
【字符型数组】不完全初始化的空位用 结束符[\0] 填充
字符型数组的两种初始化方式
- 一个一个元素输入的创建 E.g. ch1、ch2、ch5
- 以字符串形式输入创建 E.g. ch3、ch4、ch6
//将ch2与ch4对比:
*以字符串形式输入,在字符串末尾隐含一个 结束符[\0]
*数组的结束标志
- 通过看数组 [ ] 内定义的数组长度来及时结束数组
- 由数组末尾隐含的 结束符[\0] 来结束
当二者都缺少时:
#include<stdio.h> #include<string.h> int main() { char test[] = { 'C','P','U' }; printf("%s\n", test); printf("%d\n", strlen(test)); return 0; }
程序不知道数组在哪里结束,于是在打印我们初始化的内容后会继续打印随机乱码
而字符串长度也会输出一个随机值:
//注意区分字符串与数组长度!此处数组长度是3:
一维数组的使用
下标引用操作符 [ ]:数组下标从0开始
#include<stdio.h> int main() { int arr[10] = { 0 }; //数组的下标从0开始 arr[5] = 6; //此处[]为下标引用操作符 int i = 0; //计算数组大小 int sz = sizeof(arr) / sizeof(arr[0]); //数组占用的总空间/单个元素占用的空间 for (i = 0; i < sz; i++) //利用循环打印出完整的数组 { printf("%d ", arr[i]); } return 0; }
输出结果:
一维数组在内存中的储存
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("&arr[%d] = %p\n", i, &arr[i]); //%p -按地址的格式打印 -十六进制的打印
}
return 0;
}
打印数组每一位元素的地址,输出结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/0d911230638251fc8572a79c110292e2.png)
*由以上结果得出结论:
- 一维数组在内存中是连续存放的
- 随着数组下标的增长,地址是由低到高变化的
#include<stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//数组名是数组首元素地址
int* p = arr; //创建整型指针p,把数组首元素地址交给p
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *p);
p++;
//∵数组在内存中是连续储存的
//∴把数组首元素地址交给p后能通过p++获取整个数组的元素
}
return 0;
}
输出结果:
2.二维数组
二维数组的创建与初始化
二维数组的创建
int main()
{
//创建3行4列的整型数组
int arr[3][4];
//创建5行7列的字符型数组
char ch[5][7];
return 0;
}
二维数组的初始化
//初始化:创建并赋值
在二维数组有初始化的前提下:行可省略,列不可省略
int main()
{
//在数组初始化的情况下:
int arr1_ok[][4] = { 1,2,3,4,5,6,7,8,9,10 }; //可以省略行数
int arr_error[3][] = { 1,2,3,4,5,6,7,8,9,10 }; //不能省略列数
return 0;
}
完全初始化
- 行一起初始化
int main() { int arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; return 0; }
- 一行一行初始化(拆分为一维数组)
int main() { int arr1[3][4] = { { 1,2,3,4 },{ 5, 6, 7,8},{9,10,11,12} }; return 0; }
不完全初始化
- 行一起初始化 //int 填充[0];char填充[\0]
int main() { int arr1[3][4] = {1,2,3,4,5,6,7}; return 0; }
- 拆分为一维数组初始化 //int 填充[0];char填充[\0]
int main() { int arr1[3][4] = { { 1,2 },{ 5 },{ 9,10,12 } }; return 0; }
二维数组的使用
二维数组的下标 行、列都是从0开始编号的:
[0][0] | [0][1] | [0][2] | ...... |
[1][0] | [1][1] | [1][2] | ...... |
[2][0] | [2][1] | [2][2] | ...... |
*二维数组:arr //它的第一行一维数组:arr[0] //第二行:arr[1] //第三行:arr[2]
打印二维数组
#include<stdio.h> int main() { int arr[3][4] = { { 1,2,3,4 },{ 5, 6, 7,8},{9,10,11,12} }; int i = 0; //行号 int j = 0; //列号 for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); //为使上下对齐 -%d后使用tab键代替空格 } printf("\n"); } return 0; }
输出结果:
二维数组在内存中的储存
#include<stdio.h> int main() { int arr[3][4]; int i = 0; //行号 int j = 0; //列号 for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("&arr[%d][%d] = %p ",i,j,&arr[i][j]); } printf("\n"); } return 0; }
输出结果:
*由以上结果得出结论:
- 二维数组在内存中是连续存放的 //一行内部连续 //跨行也是连续的
- 随着数组下标的增长,地址是由低到高变化的
*二维数组在空间中的存放方式类似于一维数组
∴列数不可省略 →只有确定一行有多少个元素,才能知道下一行要存放在哪里
#include<stdio.h>
int main()
{
int arr[3][4] = { {1,2},{3,4},{5,6} };
int i = 0;
int* p = &arr[0][0];
for (i = 0; i < 12; i++)
{
printf("%d ", *p);
p++;
}
return 0;
}
输出结果:
3.数组作为函数参数
数组名是数组首元素地址,但有2个例外:
- sizeof(数组名) -数组名表示整个数组 -计算的是整个数组的大小,单位是字节
- &数组名 -数组名表示整个数组 -取出的是整个数组的地址
【&数组名】 -数组名表示整个数组 -取出的是整个数组的地址:
#include<stdio.h> int main() { int arr[10] = { 0 }; printf("%p &arr取数组地址\n", &arr); printf("%p &arr[0]取下标0元素地址\n", &arr[0]); printf("%p arr -数组名表示数组首元素地址\n", arr); return 0; }
打印结果为:
//输出地址一样是因为数组地址与数组首元素地址相同
//为证明 &arr 与 arr 具有不同的含义:
#include<stdio.h> int main() { int arr[10] = { 0 }; printf("%p &arr取数组地址\n", &arr); printf("%p &arr+1 取数组后一位的地址\n", &arr + 1); printf("-----------------------------------------------------------------------------------------\n"); printf("%p arr数组名表示数组首元素地址\n", arr); printf("%p arr+1 表示取数组首元素后一位地址,即数组第二位元素地址\n", arr + 1); return 0; }
打印结果为:
【冒泡排序法】
//【冒泡排序法】 //两两相邻的元素进行比较,并且可能的话需要交换顺序 void bubble_sort(int arr[],int sz) { //确定趟数 //趟数 = 元素个数 - 1 for (int i = 0; i < sz - 1; i++) { //一趟排序的过程 //一个数需要和原来在它后面的数比较 //原来在它前面的数已经排好序了 for (int j = 0; j < sz - i - 1; j++) //∴ 需要比较 sz - (i + 1) - 1 次 //-(i + 1) :数组下标从0开始 //再 -1:要减去自己 { // j <= sz - (i + 1) - 1 即: j < sz - i - 1 if (arr[j] > arr[j + 1]) //按升序排 -当前一个元素大于后一个元素时交换 { //交换元素 int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; //将数组排序为升序 //采用【冒泡排序法】 int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr,sz); return 0; }
排序结果:
*冒泡排序的经典错误代码:
void bubble_sort(int arr[]) //err:arr[] 的本质是数组第一个元素的指针,所以这里传递的形参实际上是指针 { int sz = sizeof(arr) / sizeof(arr[0]); //∴在计算元素个数时,sz永远为 1 for (int i = 0; i < sz - 1; i++) { for (int j = 0; j < sz - i - 1; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; bubble_sort(arr); return 0; }
∴实际上:
void bubble_sort(int arr[]) //实际上是: void bubble_sort(int*arr)
形参传递的数组无法在其函数内部计算元素个数
∴要使用元素个数 -需要在函数外计算好后把元素个数传参到函数内部