《数组隐藏的秘密:探寻数据存储与调用的窍门 》

数组

数组的概念

我们前面学过使用变量进行存储一个数据,如果我要是想要存储多个相同类型的数据的话不太方便,因此就有了数组,数组是啥呢?其实数组就是相同类型元素的集合

	int a = 10;
	int b = 20;
	int c = 30;
	
	int num[] = { 10,20,30 };
	

值得注意的是
在数组中可以存放一个或多个数据,数组的元素个数不能为零
在数组中存放的数据是相同类型的。

int num1[] = { 10 };
int num2[] = { 10,20,30 };
int num3[] = { };//err
int num3[] = { 10,"a",20};//err

数组分为一维数组和多维数组,最常见的多维数组是二维数组

一维数组的创建

现在我们对数组有了一定的了解,如何去创建数组呢?
数组创建的格式如下:

type  arr_name[常量值]
type: 数组存放数据的类型,
arr_name : 数组名,起的有意义就行
常量值: 代表数组存放数组元素的个数,根据实际需求去设置

存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的大小和数组的元素类型
假设我们要存储某个班20位同学的成绩,可以用到数组的创建;这个班排名前三同学的名字也可以使用数组的创建

int math[20];
char name[10];

数组的初始化

现在我们知道这个班级需要输入20位同学的成绩,但没有每位同学具体的分数,这时就需要数组的初始化来解决,数组的初始化是将数据用大括号括起来的

	int math[20] = { 85,86,87,90,91,92,95,91,75,85,86,87,90,91,92,95,91,75,65,77 };//完全初始化
	int math[20] = { 85,86,87,90,91 };//不完全初始化,剩下的元素默认初始化为0
	char name[10] = {'w', 'q'};
	char mame[10] = "abc";//字符数组在初始化时可以用字符串进行初始化,它将 \0 包含在内

数组在初始化时注意初始化不要超过 [ ] 里数字的个数,会造成初始值设定项值过多的错误

int mun[3] = { 1,2,3,4 };//err

数组的类型

数组有类型吗? 答案是有的,具体长啥样呢?其实去掉对应的数组名留下的就是数组的类型

int num[3];// 数组类型是int [3]
char arr[5];//数组类型是char [5]
float num1[4];//数组类型是float [4]

数组是一种自定义类型,它的每个类型都是不同的,同时需要注意的是数组元素的类型和数组的类型的区别

int num[3];// 数组元素的类型是int
char arr[5];//数组元素的类型是char
float num1[4];//数组元素的类型是float

一维数组的使用

了解了数组的基本语法,那我们怎么具体使用它呢?这就不得不唠唠数组的下标了
数组的下标
C语言规定数组其实是有下标的,从0开始,假如有n个元素,其下标有 n-1 个,下标相当于我们对数组元素进行编号

int num[5] = { 1,2,3,4,5 };
//    下标     0 1 2 3 4

知道了数组的下标,如何进行对数据的访问呢?在C语言中提供了 下标引用操作符 [ ],专门用来访问数组元素的,假如我要访问该数组中下标为3的元素,num[3]即可 ;访问下标为4的元素,num[4]即可
代码如下

#include<stdio.h>
int main()
{
	int num[5] = { 1,2,3,4,5 };
	// 这里的5是指定数组元素个数的
	printf("%d\n", num[3]);
	//这里的3是指数组的下标为3的元素
	printf("%d\n", num[4]);
	return 0;
}

数组的打印

如果我们想要访问整个数组怎么办呢?我们可以使用 for 循环产生0 - 4的下标 进行访问即可
代码如下:

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

数组的输入

我们现在可以对数组进行打印,相对应的我们也可以对数组进行输入值
代码如下:

#include<stdio.h>
int main()
{
	int num[5] = { 1,2,3,4,5 };
	int i = 0;
	//输入
	for (i = 0; i < 5; i++)
	{
		scanf("%d", &num[i]);//注意这里不能有空格
	}
	//打印
	for (i = 0; i < 5; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

⼀维数组在内存中的存储

我们现在学会了数组的基本使用方法,还得需要知道数组在内存中是如何存储的,先通过一段代码及输出结果进行观察:

#include<stdio.h>
int main()
{
//当对数组进行初始化的时候,数组的大小可以省略。编译器会根据数组的初始化内容,自动计算数组的元素个数
	int num[] = { 1,2,3,4,5 };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("&num[%d] = %p\n", i, &num[i]);//%p - 用来打印地址的   & - 取地址操作符 - 取出变量在内存中的地址
	}
	return 0;
}

在这里插入图片描述
通过运行结果来看,因为这个数组元素是整型类型, 所以每两个相邻的元素之间相差4个字节,由此可以得出结论:
1.数组在内存中是连续存放的
2.随着数组下标的增长, 地址是由小(低)到大(高)变化的

sizeof计算数组元素个数

在遍历数组时,我们想知道数组元素的个数,如何计算呢?贴心的C语言给出了答案:使用sizeof,单位是字节,它可以对类型和变量进行计算,当然它也可以对数组进行计算,因为数组是属于自定义类型的。例如:

#include<stdio.h>
int main()
{
	int num[20] = { 0 };
	printf("%d\n", sizeof(num));//80
	return 0;
}

输出的结果是80,是数组在内存中的总大小,单位字节
那如何计算数组元素的个数呢?我们可以使用数组的总大小 / 单个数组元素的大小 = 数组元素个数,代码如下:

#include<stdio.h>
int main()
{
	
	int num[20] = { 0 };
	int ret = sizeof(num) / sizeof(num[0]);
	printf("%d\n", ret);//20个元素 

	return 0;
}

通过计算数组元素的个数,我们以后在写数组元素个数的地方就不用固定写死了,不管数组如何变化,其计算的结果也随之变化
举个代码的实例加以说明:

#include<stdio.h>
int main()
{
	//int num[5] = { 1,2,3,4,5 };
	//换成   10 也正常运行
	int num[10] = { 1,2,3,4,5 };
	int i = 0;
	int ret = sizeof(num) / sizeof(num[0]);
	//输入
	for (i = 0; i < ret; i++)
	{
		scanf("%d", &num[i]);//注意这里不能有空格
	}
	//打印
	for (i = 0; i < ret; i++)//这里原本的5换成了ret,即使前面数组的个数发生改变,后面输入和打印也会随着改变,代码不会出错
	{
		printf("%d ", num[i]);
	}
	return 0;
}

二维数组的概念

我们学习了一维数组,一维数组的元素是内置类型的,如果我们将一维数组的作为数组的元素,这就是二维数组;倘若将二维数组作为数组的元素,这就是三维数组,二维数组的数组我们统称为多维数组
在这里插入图片描述

二维数组的创建

我们对二维数组概念有了一定的了解后,那它该如何创建呢?
语法如下:

	type arr_name[常量值1][常量值2];
	int num[3][5];
	float arr[3][2];
	type:代表是数组每个元素的类型
	arr_name:数组名,起的要有意义
	常量值1:代表行号
	常量值2:代表列号

二维数组的初始化

在创建变量或数组是给定一些初始值,被称为初始化
那二维数组该如何初始化呢?和一维数组一样,也是用大括号起来

完全初始化

	int num[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };

不完全初始化

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

按行初始化

	int num[3][5] = { {1,2},{3,4},{5} };//这里第一行的元素是1,2,第二行是3,4, 第三行是5
	int num[3][5] = { 1,2,3,4,5 };//这里第一行的元素是1,2,3,4,5,第二行和第三行是 0

初始化时可以省略行但是不能省略列

	int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	int num[][5] = { 1,2,3 };
	int num[][5] = { {1,2},{3,4} };

二维数组的使用

二维数组的下标

二维数组也是有下标的,我们知道二维数组是有行和列的,只要确定了行号和列号就可以确定二维数组的元素了,在C语言中规定行号和列号的下标都是从0开始的
比如:

int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };

我想要第2行第5列的元素并将它打印在屏幕上,代码如下:

#include<stdio.h>
int main()
{
	int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	printf("%d\n", num[2][4]);//7
	return 0;
}

⼆维数组的输入和输出

我们知道了如何访问单个数组元素的方法,那如何访问整个数组呢?
可以使用输入和输出实现,比如:

int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };

这个数组的行号的范围0-2,;列号是0-4,通过创建行号和列号的循环进行输入和输出
代码如下:

#include<stdio.h>
int main()
{
	int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	int i = 0;//创建行号
	//输入
	for (i = 0; i < 3; i++)
	{
		int j = 0;//创建列号
		for (j = 0; j < 5; j++)
		{
			scanf("%d", &num[i][j]);
		}
	}
	//输出
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", num[i][j]);
		}
		printf("\n");//每打印一行后进行换行
	}
	return 0;
}

如果我想要按列来打印呢?
比如原本是:

1 2 3 4 5
2 3 4 5 6 
3 4 5 6 7

使用列来打印,也就是五行三列,效果如下:

1 2 3 
2 3 4 
3 4 5
4 5 6 
5 6 7

我们可以将行当成列,列当成行来看待,代码如下:

#include<stdio.h>
int main()
{
	int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	int i = 0;
	//输入
	for (i = 0; i < 5; i++)
	{
		int j = 0;
		for (j = 0; j < 3; j++)
		{
			scanf("%d", &num[j][i]);
		}
	}
	//输出
	for (i = 0; i < 5; i++)
	{
		int j = 0;
		for (j = 0; j < 3; j++)
		{
			printf("%d ", num[j][i]);
		}
		printf("\n");//每打印一行后进行换行
	}
	return 0;
}
//或者
#include<stdio.h>
int main()
{
	int num[][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
	int i = 0;
	int j = 0;
	//输入
	for (j = 0; j < 5; j++)
	{
		for (i = 0; i < 3; i++)
		{
			scanf("%d", &num[i][j]);
		}
	}
	//输出

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



二维数组在内存中的存储

我们知道了一维数组的在内存中是连续存放的,那二维数组呢?让我们通过一段代码及结果进行观察,代码如下:

#include<stdio.h>
int main()
{
	int num[3][5] = { 0 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("num[%d][%d] = %p\n", i, j, &num[i][j]);
		}
	}
	return 0;
}

在这里插入图片描述
通过输出的结果可以看出来:每一行内部的元素是相邻的,相邻的两个元素之间的地址相差四个字节(元素是整型类型的,它们的大小是四个字节),可以得出结论:二维数组在内存中也是连续存放的
在这里插入图片描述

C99中的变长数组

在C99标准之前,C语⾔在创建数组的时候,数组大小的指定只能使用常量、常量表达式,如果我们初始化数据的化可以忽略其大小
比如:

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

在C99中,引入了变长数组(VLA)这一概念,允许我们使用变量指定数组大小比如:

int n = 10;
int num[n];

由于数组长度在编译时才知道它的长度是多少,因此变长数组不可以初始化,其次,变长数组意思是可以使用变量来指定数组的大小,并不是说数组的长度是可以变化的
但是在VS上不支持变长数组,可以在DevC++ 或者小熊猫C++上使用,因为他们的底层都是使用的gcc,gcc支持C99中的变长数组的
代码如下:

#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int num[n];
	int i = 0;
	for (i = 0; i < n; i++)
	{
		scanf("%d", &num[i]);//数组名是地址,但这里是数组元素,因此需要使用&
	}
	for (i = 0; i < n; i++)
	{
		printf("%d ", num[i]);
	}
	return 0;
}

关于数组的练习:

练习1:练习1:多个字符从两端移动,向中间汇聚

编写代码,演示多个字符从两端移动,向中间汇聚
具体代码如下:

#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
int main()
{
	char arr1[] = "one piece is true!!!";
	char arr2[] = "********************";
	int left = 0;
	int right = strlen(arr1) - 1;
	while (left <= right)//这里是为了确定左右两边是否还有元素
	{
		Sleep(1000);//让程序睡眠1秒,也让我们看清楚程序是如何进行的
		system("cls");//清理控制台的数据
		arr2[left] = arr1[left];//演示字符从两端进行移动的过程
		arr2[right] = arr1[right];
		printf("%s\n", arr2);
		left++;//循环一次后将左边和右边的下标进行移动,向中间回拢
		right--;
	}
		return 0;
}

练习2:二分查找

在⼀个升序的数组中查找指定的数字n,一般情况下我们会遍历数组,但是这种方法效率比较低。
我们会采取二分查找的方法进行查找,,那么二分查找概念是啥呢?先看个生活中的例子吧,男生小帅买了一件价值80元的衣服,他让小美猜猜他花了多少钱,机灵的小美先从中间开始猜150元,小帅说大了,小美又说75元·······,二分查找也叫折半查找,意思是在有序的数据中从中间开始查找,这样查找会为我们提高效率。
在一个有序的整型数组中,输入一个数据,找出并且打印它的下标。
具体代码如下:

#include<stdio.h>
int main()
{
	int num[15] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int A = 0;
	scanf("%d", &A);//输入一个你想要查找的值
	
	int left = 0;//左下标
	int sz = sizeof(num) / sizeof(num[0]);//sizeof - 是用来计算字符串或者字符数组的个数的
	int right = sz - 1;//右下标
	int flag = 0;//假设没找到
	
	while (left <= right)//确定左下标和右下标的中间是否还有元素
	{
		//int mid = (left + right) / 2;//找到中间值
		int mid = left + (right - left);//假设left 和 right 的值都没有超过 int_max,如果它两相加可能会超过,
										//因此使用 mid = a + (b-a)可以避免代码出现bug,即使b - a是负数

		if (num[mid] < A)
		{
			left = mid + 1;//左边的值都比A小,让左边进行+1,再次进行比较
		}
		else if (num[mid] > A)
		{
			right = mid - 1;//右边的值都比A大,让左边进行-1,再次进行比较
		}
		else
		{
			flag = 1;
			printf("恭喜你找到了,下标是 %d \n", mid);
			break;
		}

	}

	//if (left > right)
	if (flag == 0)
	if (!flag)
	{
		printf("抱歉,没找到\n");//左右下标没有元素,说明元素不在这一组有序数据里,没有找到
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值