一维和二维数组

72 阅读6分钟

数组

1.一维数组的创建和初始化

1.1数组的创建

比如:int arr[10];

数组类型 数组名 [常量或者常量表达式]也可以是变量,但只能在支持C99的编译器上编译

1.2数组的初始化

指在创建数组的同时给数组的内容一些合理初始值(初始化)

不完全初始化,剩余的元素默认初始化为0:int arr[10] = {1,2,3};

char ch1[10] = {'a','b','c'};'\0'

char ch2[10]="abc";

1.3一维数组的使用

下标引用操作符(下标从0开始)

数组是使用下标来访问的

数组的大小可以通过计算来得到:int sz = sizeof(arr)/sizeof(arr[0]);

随着数组下标的增长,元素的地址,也在有规律地增长(由低到高),数组在内存中是连续存放的

2.二维数组的创建

2.1二维数组的创建

int arr[3] [4];//3行4列

1 2 3 4

2 3 4 5

3 4 5 6

char arr[3] [5];

double arr [2] [4];

2.2二维数组的初始化

int arr[3] [4]={1,2,3,4,2,3,4,5,3,4};

#include<stdio.h>
int main()
{
	//完全初始化
	int arr0[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
	//不完全初始化
	int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4 };
	//分组
	int arr1[3][4] = { {1,2},{3,4},{5,6} };
	//二维数组的行可以省略,但是列一定不能省略!!!
	int arr2[][4] = { {1,2,3,4},{2,3} };
}

列数确定下来就可以确定行

2.3二维数组的使用

也是通过下标的方式(下标不管是行还是列都是从0开始的)

二维数组的序号.png

可以把二维数组理解为:一维数组的数组

像上面的例子可以看成是含有3个元素的一维数组(每行为一个元素),每个元素是一个一维数组

访问该数组元素

arr[0] [j]

arr[1] [j]

arr[2] [j]

类似于一维数组的访问 arr2[i] (i:1~9)

可以认为arr[0] arr[1] arr[2]都是一维数组名

#include<stdio.h>
int main()
{
	int arr[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++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

二维数组的基本访问结果.png

快速查找数组里面的某一个数

#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
	//二维数组的基本访问
	int i = 0;
	printf("%d\n", arr[2][0]);
	/*for (i = 0;i < 3;i++)
	{
		int j = 0;
		for (j = 0;j < 4;j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}*/
	return 0;
}

在数组里面快速查找.png

输入数组中的数字,并且按照几行几列输出

#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };
	//二维数组的基本访问
	int i = 0;
	//printf("%d\n", arr[2][0]);
	for (i = 0;i < 3;i++)
	{
		int j = 0;
		for (j = 0;j < 4;j++)
		{
			scanf_s("%d", &arr[i][j]);
		}

	}
	for (i = 0;i < 3;i++)
	{
		int j = 0;
		for (j = 0;j < 4;j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

自己输入数组的数字.png

(三维数组也是一样的道理,按照坐标定位元素)

2.4二维数组在内存中的存储

也是连续存储

在内存中的布局方式 int arr[3] [4]<=>int arr2[12]

#include<stdio.h>
int main()
{
	int arr[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++)
		{
			printf("&arr[%d][%d]=%p\n", i, j, &arr[i][j]);
		}
	}
	return 0;
}
	

二位数组在内存中的存储.png

每个元素的存储位置都相差4个字节

二维数组的存储格式.png

3.数组越界

数组的下标是有范围的。

数组的下规定是从0开始的,如果数组有n个原元素,最后一个元素的下标就是n-1。

所以数组的下标如果小于0.或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。

c语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6 };//0-5
	int i = 0;
	//0-9
	
		for (i = 0;i < 10;i++)
		{
			printf("%d ", arr[i]);
		}
	
	return 0;
}

越界访问的结果.png

可以通过计算数组的大小进行检查:

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6 };//0-5
	int i = 0;
	//0-9
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0;i < sz;i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

防止越界访问的结果.png

//二维数组的越界访问
#include<stdio.h>
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0;i < 3;i++)
	{
		int j = 0;
		for (j = 0;j <= 4;j++)
		{
			printf("%d ", arr[i][j]);
		}
	}

	return 0;
}

二维数组的越界访问.png

4.数组作为函数参数

4.1冒泡排序的正确设计

冒泡排序的核心思想:

两个相邻的元素进行比较

#include<stdio.h>
void bubble_sort(int arr[],int sz)
//形参是指针的形式:void bubble_sort(int* arr,int sz)    
{
	//趟数
	//int sz = sizeof(arr) / sizeof(arr[0]);    地址是应该使用指针来接收,所以arr这里看似是数组,本质是指针变量
	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 };
	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;
}

冒泡排序的结果.png

数组传参的时候有2种写法:

1.数组2.指针 如上

4.2数组名是什么

数组名确实能表示首元素的地址

但是有2个例外

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节

#include<stdio.h>
int main()
{
	int arr[10];
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);

	int n = sizeof(arr);//40
	printf("%d\n", n);
	return 0;
}

什么是数组名.png

2.取地址&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

#include<stdio.h>
int main()
{
	int arr[10];
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr);
	//int n = sizeof(arr);//40
	//printf("%d\n", n);
	return 0;
}

取地址数组名结果.png

对比

#include<stdio.h>
int main()
{
	int arr[10];
	printf("%p\n", 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);
	
	return 0;
}

三组数组名结果.png

4.3二维数组的数组名的理解

#include<stdio.h>
int main()
{
	int arr[3][4];
	int sz = sizeof(arr);
	printf("%d\n", sz);
	arr;//二维数组的数组名也表示数组首元素的地址,第一行的地址
	arr + 1;//第二行的地址
	sizeof(arr) / sizeof(arr[0]);//算行数
	sizeof(arr[0]) / sizeof(arr[0][0]);//算列数
	return 0;
}

计算行数和列数.png