目录
1、一维数组的创建和初始化
数组(Array)是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。 这些有序排列的同类数据元素的集合称为数组。
数组是用于储存多个相同类型数据的集合。
1.1、数组的创建
type_t arr_name[const_n]
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
#include <stdio.h>
int main()
{
//创建整型数组,可以是常量和常量表达式
int arr1[2+3];
int arr2[5];
//字符数组的各种形式
char ch1[] = "abc";
char ch2[] = { 'a' ,'b', 'c', '\0'};
char ch3[] = { 'a' ,'b', 'c' };
char ch4[3] = { 'a' ,'b', 'c' };
printf("%s\n", ch1);
printf("%s\n", ch2);
printf("%s\n", ch3);
printf("%s\n", ch4);
return 0;
}
输出:
- ch1字符串最后有 ‘\0’ 作为字符串的结束标志,所以输出为abc
- ch2字符数组需要在最后加上一个 '\0' ,表示遇到 '\0' 结束,否则会打印出乱码
- ch3字符数组没有在最后加上 '\0' ,所以会打印出“烫烫烫...”
- ch4那么指定数组的大小为3,不在最后加 '\0' 也会出现乱码的情况。
//这样的代码在c99标准之前是不可以的,但是在c99标准之后,允许这样来创建数组,但是不可以初始化。
//vs 2022 不支持变长数组
int n = 0;
int arr[n];
1.2、数组的初始化
//数组的初始化是指,在创建数组的同时给数组的内容一些合理的初始值(初始化)
int main()
{
int arr1[5] = { 1, 2, 3 };//不完全初始化
int arr2[5] = { 1,2,3,4,5 };//完全初始化
int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };//如果初始化可以不用指定数组的大小
return 0;
}
1.3、一维数组的使用
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//计算数组元素的个数,数组的大小除以数组每个元素的大小就是元素个数
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
//在屏幕上打印数组的元素
printf("%d ", arr[i]);//[]:是下标引用操作符
}
return 0;
}
//输出结果: 1 2 3 4 5 6 7 8 9 10
1.4、一维数组在内存中的存储
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
//打印出数组每个元素的地址
printf("&arr[%d] = %p\n", i, &arr[i]);
}
return 0;
}
输出:
我们发现数组在内存中是连续存放的,每个元素的地址加4就是下一个元素的地址,这是因为数组存放的数据类型是int,而int的大小是4个字节。
2、二维数组的创建和初始化
二维数组跟一维数组本质上差不多,可以把二维数组看做是存放一维数组的一维数组,它的每个元素的一维数组。
2.1、二维数组的创建
//二维数组的创建
int arr[2][3];//两行三列的一个整型二维数组
char ch[3][5]; //三行五列的一个字符型二维数组
double arr2[5][6]; //五行六列的双精度浮点型二维数组
2.2、二维数组的初始化
//二维数组的初始化
int arr1[2][3] = {{1,2,3},{4,5,6}};
int arr2[3][4] = {1,2,3,4};
int arr3[][3] = {{1,4},{2,5}};//二维数组如果有初始化,行可以省略不写,但是列不能省略。
2.3、二维数组的使用
//实现二维数组的输入和输出
int main()
{
int arr1[2][3] = { 0 };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
//输入值
scanf("%d", &arr1[i][j]);
}
}
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
//打印值
printf("%d ", arr1[i][j]);
}
printf("\n");
}
return 0;
}
输出:
2.4、二维数组在内存中的存储
int main()
{
int arr1[2][3] = { {1, 2, 3}, {4, 5, 6} };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
//打印各个元素的地址
printf("&arr1[%d][%d] = %p\n", i, j, &arr1[i][j]);
}
}
return 0;
}
输出:
可以看出二维数组在内存中的存储也是连续存放的。
3、数组越界
数组的下标是有范围限制的,数组的下标规定是从0开始的,如果数组有n个元素,那么最后一个元素的下标是n-1,所以数组的下标如果小于0,或者大于n-1,那么数组就越界访问了,超出了数组合法空间的访问。
需要注意的是:C语言本身并不做数组下标的越界检查,编译器也不一定报错。
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i <= 10; i++)
{
printf("%d ", arr[i]);//这里会越界访问,因为数组一共有10个元素,下标为0-9,但是这里会打印arr[10],所以越界了、
}
return 0;
}
结果:
4、数组作为函数的参数
例如我们想要实现一个冒泡排序:
错误代码:
我们发现并没有达到预期的效果,没有完成升序的排序,这是为什么呢?
因为数组作为函数参数时,数组名是函数首元素的地址,所以bubble_sort函数的参数列表需要一个指针来接收地址,而sizeof(a)计算的是指针的大小,在32位平台上指针的大小是4个字节,在64位平台上指针的大小是8字节,然后sizeof(a[0])相当于计算整型的大小,也是4个字节,所以函数内得到的sz等于1,所以达不到预期的效果。
这里需要特别注意,数组名和数组首元素之间的“羁绊”:
我们似乎发现这三个的地址都一样,但是别被表象误导了。我们试着加1来看一看
可以得出结论:
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
- &数组名,取出的是数组的地址,表示整个数组的地址
除了以上两种情况外,所以的数组名都表示数组首元素的地址
//冒泡排序正确代码
void bubble_sort(int a[], int sz)
{
for (int i = 0; i < sz-1; i++)
{
for (int j = 0; j < sz - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 3, 4, 2, 5, 7, 6, 9, 1, 8, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
结果:
5、数据实例
5.1、三子棋
5.2、扫雷游戏
三子棋和扫雷代码详见下期更新。