💻前言
🍁这篇博客总结数组当中的知识点,理解熟悉数组的使用。
💻一.数组的创建和初始化
数组是一组相同类型元素的集合。
1.一维数组
创建格式
类型 数组名[常量表达式] = {初始化部分};
[ ]中的常量表达式用来指定数组的大小
创建实例
//可以不进行初始化 int arr1[10]; char arr2[10]; float arr3[1]; double arr4[20];
注:数组创建,在C99标准之前, [ ] 中要给一个常量才可以,不能使用变量;在C99标准支持了变长数组的概念,但变长数组是不能被初始化的。
#include <stdio.h> int main() { //支持C99标准的编译器上,数组的大小可以是变量 int n = 0; scanf("%d", &n); int arr[n]; //这个数组不能初始化 int i = 0; //输入 for (i = 0; i < n; i++) { scanf("%d", &arr[i]); } //输出 for (i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; }
初始化
数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
数组在创建的时候如果想不指定数组的确定的大小就得初始化,此时数组的元素个数根据初始化的内容自动确定。
//不完全初始化,剩余的元素默认初始化为0 int arr[10] = { 1,2,3 }; int arr1[10] = { 1,2,3,4,5,6,7,8,9,0 }; int arr2[] = { 1,2,3 };//数组大小为三 char ch1[10] = { 'a', 'b', 'c' }; //a b c 0 0 0 0 0 0 0 char ch2[10] = "abc"; //a b c \0 0 0 0 0 0 0
对于下面的代码要区分
char ch3[] = { 'a', 'b', 'c' }; //数组大小为3,没有'\0',以字符串的形式打印会乱码 char ch4[] = "abc"; //数组大小为4,可以以字符串的形式正常打印
使用实例
[ ] ,下标引用操作符。它其实就数组访问的操作符。
- 数组是使用下标来访问的,下标是从0开始。
- 2. 数组的大小可以通过计算得到。
#include <stdio.h> int main() { int arr[10] = {0};//数组的不完全初始化,下标的范围是0-9 //计算数组的元素个数 int sz = sizeof(arr)/sizeof(arr[0]); //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以: int i = 0;//做下标 for(i=0; i<10; i++)//这里写10,好不好? { arr[i] = i; } //输出数组的内容 for(i=0; i<10; ++i) { printf("%d ", arr[i]); } return 0; }
2.二维数组
创建格式
类型 数组名[常量表达式][常量表达式] = {初始化部分};
创建实例
int arr[3][4];//表示3行4列,可以存放12个元素 char arr[3][5]; double arr[2][4];
初始化
二维数组如果有初始化,行可以省略,列不能省略
//不完全初始化,剩余的元素默认初始化为0 int arr[][4] = {{2,3},{4,5}} int arr1[3][4] = {1,2,3,4,2,3,4,5,3,4,5,6}; int arr1[3][4] = { {1,2}, {3, 4}, {5, 6}}; int arr2[][4] = { 1,2,3,4,5,6}; //列不可以省略
使用实例
二维数组的使用也是通过下标的方式,行和列的访问都是从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; }
💻二.数组在内存中的存储
这里我们打印数组中每个元素的地址进行分析!
1.一维数组
观察打印结果我们可以知道,数组在内存中是连续存放的,且数组元素由低址到高地址递增存储。
2.二维数组
与一维数组一样二维数组在内存中也是连续存储的。
💻三.数组越界和数组名
1.对于二维数组的理解
对于一维数组的数组名可以很好的理解,这里重点刨析一下二维数组的数组名,以下面这个代码为例:
#include <stdio.h> int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; printf("arr[1][2] =%d\n", arr[1][2]); return 0; }
这里的arr便是二维数组的数组名可以理解,我们进入调试打开监视窗口观察这个数组
这里的二维数组,由三个模块组成,每个模块里放着四个元素;所以,可以类似的认为,这三个模块便是三个一维数组,而这三个一维数组又作为了二维数组的三个元素,可以把二维数组理解为:一维数组的数组(一维数组作为数组的元素而组成的数组)。
所以,这里二维数组中的每一行其实都是一个一维数组就好理解了,而上面的代码三行的数组名分别为:arr[0],arr[1],arr[2],同时注意一下图片中数组的类型。
2.数组名代表什么
对于一维和二维数组,一般情况下数组名表示首元素的地址:
但是有2个例外:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
一维
二维
3.数组越界
通过下标访问数组元素,如果超出了下标的范围,也就是超出了数组的合法空间的访问,就称为数组越界访问;越界访问编译器并不会报错,但这样的程序是错误的,所以我们写代码时,一定要自己做越界的检查。
一维数组越界
由于不在数组范围内,所以打印出的是一个随机值。
二维数组的越界有俩种情况:
在二维数组范围内越界,arr[1][5]超出了数组arr[1]的空间范围,进入了arr[2]的空间范围,访问到了arr[2]中的第二个元素!
在二维数组范围外越界,与一维数组一样,打印出的是一个随机值。
💻四.理解数组作为函数的参数和冒泡排序
当数组作为函数参数的时候,要知道数组传参传过去的是什么,否则容易写出错误的代码,下面穿插冒泡排序讲解。
首先理解冒泡排序
冒泡排序的核心思想:俩个相邻的元素进行比较!
比如要将一组数据从小到大排序
如果有n个元素,将相邻元素俩俩比较交换位置,则需要进行n-1趟冒泡排序,每次排序确定一个数的最终位置;第一趟排序需要进行n-1次比较,下一趟排序比上一趟少一个元素,所以比较次数也会少一次,直到最后剩下一个元素,排序完成。
代码实现:
数组传参的时候,形参有2种写法:
1. 数组
2. 指针
//形参是数组的形式 void bubble_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++) { if (arr[j] > arr[j + 1]) { //交换 int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } //形参是指针的形式 void bubble_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++) { 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 }; //0 1 2 3 4 5 6 7 8 9 int sz = sizeof(arr) / sizeof(arr[0]); //不可以放在下面的自定义函数中 //冒泡排序的算法,对数组进行排序 bubble_sort(arr, sz); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
在求数组的长度时千万不能在自定义函数内部去求,数组传参实际上传递的是数组首元素的地址而不是整个数组,所以在自定义函数内部计算一个函数参数部分的数组的元素个数是错误的。