爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)

作者:阿辉不一般
你说呢:生活本来沉闷,但跑起来就有风
专栏:爱上C语言
作图工具:draw.io(免费开源的作图网站)
请添加图片描述

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主,如有不足还请指点,博主及时改正,感谢大家支持!!!

文章目录

  • 前言
  • 一维数组
    • ✈️一维数组的创建
    • ✈️一维数组的初始化
    • ✈️一维数组的使用
    • ✈️一维数组在内存中的存储
  • 二维数组
    • ✈️二维数组的创建
    • ✈️二维数组的初始化
    • ✈️二维数组的使用
    • ✈️二维数组在内存中的存储
  • 关于数组名的理解
    • ✈️一维数组的数组名
    • ✈️二维数组的数组名
  • 越界访问

前言

Hallo!各位小伙伴,你是否还在为C语言的数组而烦恼,是否对对数组的使用感到头大,那么请你不要错过这篇博客,这篇博客会详细讲一维二维数组的创建、使用、内存中的存储以及数组名是什么,今天阿辉将通过这篇博客来一步一步拿捏数组内容充实,干货满满,建议关注博主,与博主一起学习!!

一维数组

数组,数组,数组到底是个啥玩意?

数组就是一组同类型的元素的集合

当我们想要创建一组相同类型的变量,就可以使用数组,相当方便

✈️一维数组的创建

了解了数组到底是个啥我们来看看它是如何创建的

type_t  arr_name [const];

//type_t指的是数组的类型
//arr_name数组名(自定义)
//const是常量也可以是表达式,用来给定数组大小

例:

#include
int main()
{
	int arr1[3];
	char arr2[5];
	float arr3[8];
	double arr4[2+3];
}

我们可以创建不同类型的数组,可以用常量或常量表达式来给定数组大小

✈️一维数组的初始化

废话少说,咱直接上代码

int main()
{
	int a1[10];//仅仅创建数组并未初始化,元素均为随机值
	int a2[10] = {0,1,2,3,4,5,6,7,8,9};//10个元素均给定,完全初始化
	int a3[10] = {0,1,2};//不完全初始化,未给定元素均为0
	int a4[] = {1,2,3,4};//未给定数组大小,由初始化元素个数确定数组大小
}

下图监视中各不同初始化数组中的存放
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第1张图片
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第2张图片
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第3张图片
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第4张图片
我们来看看数组初始化中的特例字符数组

int main()
{
	char a1[] = "abc";
	char a2[] = {'a','b','c'};
}

给数组初始化要用{ },而字符数组还可以用" "来初始化
这两者有什么不同呢?我们接着往下看
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第5张图片

上图我们可以看到数组a1比较于数组a2多了一个元素\0,这样会有一个问题
比如,由于没有\0使用strlen()函数计算字符串长度时会导致越界访问

✈️一维数组的使用

这里我们引入操作符[ ]下标引用操作符,用它我们可以轻松访问到数组中的各个元素,我们看下面的这个例子

#include
int main()
{
	int a[10]={0};//数组的不完全初始化,数组内元素均为0
	for(int i = 0; i < 10; i++)//为数组内元素赋值
	{
		a[i]=i;
	}
	for(int i = 0; i < 10; i++)//打印各个元素
	{
		printf("%d ",a[i]);
	}
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第6张图片

上述代码可以看到,我们用a[i]可以访问到数组中的各个元素,从而实现对数组内存储的元素进行修改,i代表元素的下标,第一个元素的下标为0向后顺序递增

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第7张图片

✈️一维数组在内存中的存储

不知道大家有没有想过数组在内存中怎样存储的,申请多大的空间呢?

数组在内存中是顺序存储的,申请的空间大小由数组的类型和数组中元素个数共同决定

#include
int main()
{
	int a[10] = {0,1,2,3,4,5,6,7,8,9};
	for(int i = 0; i < 10; i++)//打印各个元素地址
	{
		printf("a[%d] = %p\n", i, &a[i]);
	}
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第8张图片

由上图各个元素地址我们不难发现,每个元素地址相差4个字节,而int类型刚好就是4个字节,数组内每个元素都是整型同时随下标增长地址连续递增。
由此,我们得出结论:数组在内存中连续存放,申请空间大小为类型大小与元素个数的乘积

二维数组

二维数组这个名字很唬人的家伙其实也是个纸老虎,它的本质就是以数组为元素的数组,也就是数组的数组,明明就是套娃

✈️二维数组的创建

int main()
{
	type_t arr_name[const][const];
}

二维数组的创建与一维数组的创建是类似的,区别就在于二维数组有了行和列的区分,比如二维数组a[i][j]中的i代表行数j列数

✈️二维数组的初始化

int main()
{
	int a1[2][3];//未初始化,数组内个元素为随机值
	int a2[2][3]={1,2,3,2,3,4};//完全初始化,1,2,3放第一行,2,3,4放第二行
	int a3[2][3]={1,2};//不完全初始化,未给定元素为0
	int a4[2][3]={{1,2},{2,3}};//这种表示{1,2}放第一行,{2,3}放第二行,未给定元素为0
	int a5[][3]={1,2,3,4};//给定列数没有行数,1,2,3第一行放满,剩下一个4放第二行,这个数组就是2行
}

附上a4a5的图解释
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第9张图片
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第10张图片
提示:二维数组的行可以省但列不能省略

✈️二维数组的使用

与一维数组一样二维数组也是通过下标来找到元素的,二维数组的行和列的下标都是从0开始递增

int main()
{
	int a[3][3]={0};
	for(int i = 0; i < 3; i++)
	{
		for(int j = 0; j < 3; j++)//给二维数组赋值
		{
			a[i][j]=j;
		}
	}
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第11张图片

❤️对于二维数组的访问使用我们一般使用两层for循环来遍历二维数组

✈️二维数组在内存中的存储

int main()
{
	int a[3][3] = { 0 };
	//打印数组中各个元素的地址
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf("a[%d][%d] = %p\n", i, j, &a[i][j]);
		}
	}
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第12张图片
从上图中不难发现不论是相同行的元素还是不同行的元素之间都相差4个字节,所以二维数组在内存中也是连续存放的

关于数组名的理解

✈️一维数组的数组名

一般来说:一维数组的数组名就是首元素地址

那我们如何理解呢?

这里我们用一个经典的代码为大家解惑

//错误的冒泡排序
void bubble_sort(int a[])
{
	int sz = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < sz; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{
			if(a[j] > a[j + 1])
			{
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int a[10] = { 1,3,7,2,4,8,5,9,6,0};
	int s = sizeof(a) / sizeof(a[0]);
	bubble_sort(a);
	for (int i = 0; i < s; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第13张图片
我们可以看到数组a并没有被排序,这是为什么呢?我们调试看看
爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第14张图片

我们发现当程序运行到函数内部时,sz的值是1,这是为什么呢?
首先我们介绍一下sizeof这个操作符,sizeof是由来计算变量(类型)所占空间的大小,不关注存储的内容,单位是字节,当数组名a传给bubble_sort时,人模狗样的用int a[]接收,实际上它根本不是数组而是指针变量,里面存着首元素地址,在X86环境下指针变量占4个字节,一个整型也占4个字节,所以sizeof(a)/sizeof(a[0])的值为1

正确的冒泡排序

void bubble_sort(int a[],int sz)
{
	for (int i = 0; i < sz; i++)
	{
		for (int j = 0; j < sz - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				int tmp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int a[10] = { 1,3,7,2,4,8,5,9,6,0};
	int s = sizeof(a) / sizeof(a[0]);
	bubble_sort(a,s);
	for (int i = 0; i < s; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

特殊的一维数组的数组名有两种情况下并非首元素地址,而是整个数组的地址

  • sizeof(数组名) 这里数组名是整个数组的地址,单位是字节,计算的是整个数组所占空间的大小
  • &数组名 这里去除的是整个元素的地址

我们如何理解它们呢?上代码

int main()
{
	int a[10] = { 0 };
	//打印地址
	printf("a = %p\n", a);
	printf("&a = %p\n", &a);
	return 0;
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第15张图片
我们发现a&a打印出来的地址并没有区别,是不是懵逼了
其实它们的区别并不体现在这,我们接着看

int main()
{
	int a[10] = { 0 };
	//打印地址
	printf("a = %p\n", a);
	printf("a + 1 = %p\n", a + 1);
	printf("&a = %p\n", &a);
	printf("&a + 1 = %p\n", &a + 1);
	return 0;
}

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第16张图片
上图可以看出a+1跳过了4个字节&a+1跳过了40个字节,而数组a就是40个字节,它们的区别就在这里,看完这个二维数组的数组名就很好理解了❤️

✈️二维数组的数组名

了解了一维数组的数组名,二维数组就简单点了,不多bb,直接上强度:

对于a[2][3]这个数组数组名a就是首行素的地址,就是第一行一整行的地址,那么有没有谁是首元素的地址,还真有,因为二维数组是套娃来的,a[0]就相当于第一行这个一维数组的数组名,这下大家懂了吧

越界访问

数组的下标是有范围限制的。 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。 所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第17张图片
上图我们可以看到,虽然造成了越界访问,但是VS并没有报错也没有警告,还把它打印出来了,这是很危险的很容易出bug,所以程序员写代码时,最好自己做越界的检查


感谢老铁能看到这,到这里数组的分享就到此为止了,如果觉得阿辉写得不错的话,记得给个赞呗,你们的支持是我创作的最大动力

爱上C语言:超详细讲解数组,学数组看这一篇就够了(数组篇)_第18张图片

你可能感兴趣的:(爱上C语言,c语言,开发语言)