1.一维数组的创建与初始化
1.1数组的创建
数组是一组相同类型数据的集合
tydef_t arrname [const_t]
c99标准前,数组的大小必须是常量表的式
c99标准中,引入变长数组的概念,及数组大小是可以用变量表示的。变长数组无法初始化。
int main() { int arr[8]; char ch[10]; float arr2[15]; return 0; }
举例创建如上数组,创建格式为 类型+数组名+[数组大小]
1.2 数组的初始化
数组的初始化是指,在创建数组的同时给数组的内容进行赋值
int main() { int arr[8] = { 1,2,3,4,5,6,7,8 }; char ch[10] = { 'd','s','df','f','c','h','g' }; float arr2[15] = { 1.23,5.65,7.32,5.62,9.35 }; char arr3[] = "faoijfaj";//可以不指定大小,大小会根据初始化内容决定 return 0; }
初始化可以完全初始化,也可以不完全初始化,剩余的默认初始化为0,但绝不可以初始化超过数组大小。
这里须注意一下字符数组两种初始化的区别
char arr[]="fafafa".char arr2={'f','a','f','a','f','a'} 前者有结束标志“\0”,后者没有。
1.3一维数组的使用
利用下标引用操作符引用数组
int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9 }; for (int i = 0; i < 10; i++) { printf("%d", arr[9]); } int size = sizeof(arr) / szieof(arr[0]);//计算数组元素个数 return 0; }
1.4一维数组的存储
因为数组的地址是连续存放的,我们在应用数组的地址时,当存放给一个指针时,该指针也可通过首地址找出后面元素的地址
所以在某种程度,指针可以表示数组,数组名代表一个指针。
我们先看看的地址
int main() { int arr[10] = { 0 }; int i = 0; int se = sizoef(arr) / sizeof(arr[0]); for (i = 0; i < se; i++) { printf("%p\n", &arr[i]); } return 0; //这里一个整型元素 4个字节,总大小为40字节。 //打印出每个元素的地址 我们发现两两地址刚好差 4,即元素在数组中是连续存储的。 //随着下标的递增,地址由低到高变化。 }
再看看利用和指针实现数组元素的访问。
int main() { int arr[10] = { 0 }; int i = 0; int se = sizoef(arr) / sizeof(arr[0]); int* p = &arr[0];//int *p=arr; for (i = 0; i < se; i++) { printf("%p\n", p + i); } for (i = 0; i < se; i++) { printf("%d\n", *(p + i));//打印1到10 } return 0; }
2.二维数组
2.1二维数组的创建
类比于一维数组创建,这里直接创建二维数组。
//数组创建 int arr[3][4]; char arr[3][5]; double arr[2][4];
在理解二维数组上,因为它1是二维,则我们理解为他是几行几列的数据存储,前一个代表行,后一个代表列,如同行列式里的方阵。
2.2二维数组的初始化
int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };//直接初始化 char arr[5][6] = { {1,2},{3,4},{5,6} };//一行行的初始化,且不完全初始化 //在这里初始化时,不同于一维数组可以省略数组大小,在二维数组中,行可以省略, //但列不能省略, //省略行,不省略列,计算机还是可以通过计算确定 //数组的存储方式,但若省略了列,只有行,此时二维数组的存储情况较多,计算机无法判断。 int arr[][4] = { {1,2},{3,4},{5,6} }; return 0; }
这里我们举例 int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };//直接初始化
我们可以这样来理解它
2.3二维数组的使用
仍然通过下标来引用数组,且行列编号都是从0开始
int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; //1 2 3 4 //5 6 7 8 //9 10 11 12 printf("%d", arr[1][2]);//打印7 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%2d", arr[i][j]);//右对齐2位打印整数 } printf("\n"); } //打印数组的每一个元素 //先行遍历,在列遍历, return 0; }
2.4二维数组在内存中的存储
还是观察地址
int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%p\n", &arr[i][j]); } } return 0; }
我们发现元素的地址还是连续的,故我们理解的是几行几列,但实际二维数组元素的地址是连续的。
与一维数组一样,我们知道了首地址,传给一个指针时,我们可以通过首地址间接访问二维数组的所有成员。
此时我们还可以这样理解二维数组:
我们将二维数组的每一行看做一个一维数组,则arr[0]就是第一行的数组名,arr[1]是第二行的数组名
......,相当于int arr[0] [4],int arr[1] [4],int arr[2] [4],int arr[3] [4]。
3.数组的越界
在数组下标访问的过程中,若访问超出数组大小范围,下标越界访问,编译器不一定会报错,但该代码已经是错误了。
4.数组作为函数参数
这里我们用冒泡排序来举例,数组做函数参数:
void bubble_sort(int arr[])//参数本质是一个指针 int * arr { for (int i = 0; i < 10 - 1; i++) { for (int j = 0; j < 10 - i - j; j++) { if (arr[j] > arr[j+1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } } } int main() { int arr[] = { 4,6,8,2,8,6,9,5 }; //排序 bubble_sort(arr); int i = 0; for (i = 0; i < 10; i++) { printf("%d", arr[i]); } return 0; }
我们发现在用数组传参时,本质上就是传的数组首元素的地址,所以在传参时,我们仍可以用对应类型的指针作为参数,来接收数组。
根据二维数组的特点,我们可以实现一个三子棋游戏。