#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//---------------------------------------------------------------------------------------2. 二维数组的创建和初始化
//-------------------------------------------------------------------------------------------------2.1 二维数组的创建
// 1 2 3 4
// 2 3 4 5
// 3 4 5 6
int main()
{int arr1[3][4]; // (3行4列)char arr2[5][10]; // (5行10列)double arr3[6][6]; // (6行6列)return 0;
}
//-------------------------------------------------------------------------------------------------2.2 二维数组的初始化
int main()
{int arr1[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 }; // 1 2 3 4// 2 3 4 5// 3 4 5 6 int arr2[3][4] = { 1,2,3,4,2,3,4,5,3,4 }; // 如果元素不够,用0补齐(不完全初始化)// 1 2 3 4// 2 3 4 5// 3 4 0 0 int arr3[3][4] = { {1},{2,3},{3,4,5} }; // 可以再分组(不完全初始化)// 1 0 0 0// 2 3 0 0// 3 4 5 0 //二维数组 可以省略 行 数,不能省略 列 数int arr3[][4] = { {1,2,3,4},{2,3} }; //行数输入了几行,就是几行// 1 2 3 4// 2 3 0 0 return 0;
}
//-------------------------------------------------------------------------------------------------2.3 二维数组的使用
// 二维数组的使用也是通过下标的方式
//不管是 行 还是 列 都是从0开始访问
int main()
{//int arr1[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };//----------------------------------------------------自己 输入一个 二维数组int arr1[3][4] = { 0 }; int i = 0;for (i = 0; i <3; i++){int j = 0;for (j = 0; j <4; j++){scanf("%d", &arr1[i][j]);}}//---------------------------------------------------- 打印 整个 二维数组for (i = 0; i <= 2; i++){int j = 0;for (j = 0; j <= 3; j++){printf("%d ", arr1[i][j]);}printf("\n");}//--------------------------------------------------------打印数组中的某个元素printf("%d\n", arr1[2][2]); // 打印 3行 3列 位置的元素printf("%d\n", arr1[2][0]); // 打印 3行 1列 位置的元素return 0;
}
//可以把二维数组理解为由一维数组组成的数组
访问第一行元素 arr[0][j]
访问第二行元素 arr[1][j]
访问第三行元素 arr[3][j]
//-----------------------------------------------------------------------------------------2.4 二维数组在内存中的存储
// 打印出 二维数组中的地址
int main()
{int arr1[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };int i = 0;for (i = 0; i <= 2; i++){int j = 0;for (j = 0; j <= 3; j++){printf("&arr1[%d][%d]=%p\n", i,j,&arr1[i][j]);}}return 0;
}
//二维数组在内存中也是 连续存放的,各相差4个字节(一个字符四个字节)
&arr1[0][0]=00000027016FF598
&arr1[0][1]=00000027016FF59C
&arr1[0][2]=00000027016FF5A0
&arr1[0][3]=00000027016FF5A4
&arr1[1][0]=00000027016FF5A8
&arr1[1][1]=00000027016FF5AC
&arr1[1][2]=00000027016FF5B0
&arr1[1][3]=00000027016FF5B4
&arr1[2][0]=00000027016FF5B8
&arr1[2][1]=00000027016FF5BC
&arr1[2][2]=00000027016FF5C0
&arr1[2][3]=00000027016FF5C4
//二维数组 可以省略行数,不能省略 列 数
// int arr[][4]={1,2,3,4,5,6};
//---------------------------------------------------------------------------------------------------------------3.数组越界
数组的下标是有范围限制的。
数组的下标规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,
所以程序员写代码时,最好自己做越界的检查。
//------------------------------------------------一维
int main()
{int arr1[] = { 1,2,3,4,5,6 };int sz = sizeof(arr1) / sizeof(arr1[0]); //用公式计算得到数组大小int i = 0;for (i = 0; i < sz; i++) //写 sz 避免出现数组越界访问
// for (i = 0; i < 9; i++) // 数组越界{printf("%d ", arr1[i]);}return 0;
}
//-----------------------------------------------二维
int main()
{int arr1[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };int i = 0;for (i = 0; i <3; i++){int j = 0;for (j = 0; j <4; j++)// for (j = 0; j <= 4; j++) //数组越界{printf("%d ", arr1[i][j]);}printf("\n");}return 0;
}
//--------------------------------------------------------------------------------------------------4.数组作为函数参数
//写代码时,常常会将数组作为参数传给函数。比如实现一个冒泡程序
//冒泡排序的核心思想,两个相邻的元素进行比较
//一趟冒泡排序让一个数据来到他最终应该出现的位置
//----------------------------------------------------------------------------------------4.1 冒泡排序函数的错误设计
//数组传参,形参有两种写法: 1.数组 2.指针
//-------------------------------------失败原因------------------------------------
数组传参传的是数组内元素的首地址
地址应该使用指针来接收
所以 这里的 形参 int arr[],表面上是数组,本质上是个指针变量
因此 sizeof(arr) 计算出来的就不是数组大小了,是指针大小4(x86环境)
sizeof(arr[0]) 也是4
所以 sz 求出来为 1,导致冒泡失败
修改方法,把 sz 放到函数外部求,传到函数内部
void bubble_sort(int arr[], int sz) // 1. 形参是数组形式:便于理解。 []数值可写可不写
//void bubble_sort(int* arr, int sz) // 2. 形参是指针形式{//确定需要多少趟冒泡排序(也就是数组元素个数-1)//int sz = sizeof(arr) / sizeof(arr[0]); //失败原因,解决方法就是sz 在外部求,传到内部 -----------0 1 2 3 4 5 6 7 8 9 -----冒泡成功int i = 0;for (i = 0; i < sz - 1; i++) //sz-1趟冒泡排序{//一趟冒泡排序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) // 2. 形参是指针形式:
//{
//
//}int main()
{//把数组内容排成升序int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);//冒泡排序算法,对数组进行排序//bubble_sort(arr); // 数组名本质是数组首元素的地址---------------------------失败原因bubble_sort(arr,sz); //数组传参,给数组名就行,把数组元素个数也传过去// 打印出来int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]); // 8 9 7 6 5 4 3 2 1 0-------------------------冒泡失败}return 0;
}
//------------------------------------------------------------------------------------------------4.2 数组名是什么?
//数组名确实能表示首元素的地址
//但是有两个例外
// 1. sizeof(数组名), 这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
// 2. &数组名, 这里的数组名也表示整个数组,取出的是 整个数组的地址
//除了这两种情况之外,以后见到的所有的 数组名arr, 代表的都是首元素地址
int main()
{int arr[10];printf("%p\n", arr); // 打印数组名 arr 首元素的地址printf("%p\n", arr+1);printf("---------------------------------------\n");printf("%p\n", &arr[0]); //打印首元素地址printf("%p\n", &arr[0]+1);printf("---------------------------------------\n");printf("%p\n", &arr); // 打印数组的地址printf("%p\n", &arr+1);// 000000148B8FFB78 //跳了4个字节,1个元素// 000000148B8FFB7C// -------------------------------------- -// 000000148B8FFB78 //跳了4个字节,1个元素// 000000148B8FFB7C// -------------------------------------- -// 000000148B8FFB78 //跳了40个字节,整个数组的10个字节// 000000148B8FFBA0int n = sizeof(arr);printf("%d\n", n); // 算出 40,数组的大小,40个字节return 0;
}
//------------------------------------二维数组数组名的理解
int main()
{int arr[3][4];int sz = sizeof(arr); //计算整个元素的大小 12个元素,每个元素4个字节printf("%d\n", sz); // 输出 48 (3*4*4)arr; // 二维数组的数组名 表示的是 第一行(首行) 的地址,不是0*0元素的地址printf("%p\n", arr);printf("%p\n", arr + 1);// 000000A2C0AFFCC8 跳过16个字节,4个元素,也就是跳过一行// 000000A2C0AFFCD8printf("%d\n", sizeof(arr) / sizeof(arr[0])); //计算数组的行数(数组所有字节 / 数组每行的字节数)printf("%d\n", sizeof(arr[0]) / sizeof(arr[0][0])); //计算数组的行数(数组首行字节数 / 数组一个字节数)return 0;
}