C语言 浅谈数组

前言:博主初学c语言,水平有限,文章若有错误,望指正。

目录

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

1.1一维数组的创建

1.2一维数组的初始化 

1.3一维数组的使用

1.4一维数组的存储

2.二维数组的创建和初始化

2.1二维数组的创建

2.2二维数组的初始化

2.3二维数组的使用

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

3.数组越界

 4.数组作为函数参数

4.1数组名是什么?

4.2数组名作为函数参数 


在写代码时,假设要创建10个变量,你会怎么做?你可以直接 int a,b,c,d,......创建10个变量出来。但这样写是不是太麻烦了,我们有没有更简便的创建方式?当然是有的,这种方式就涉及到我们接下来要讲的数组了。

数组是什么?我们可以认为数组是一系列相同元素的集合。这个集合可以是一维的,二维的等等...我们这次主要讲的是一维数组与二维数组。

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

1.1一维数组的创建

type_t   arr_name   [const_n];

type_t :数组类型。

arr_name :数组名

const_n:常量表达式,通常是指数组的个数(有时我们也叫做数组长度),所以我们不能用n(一个变量)来定义一个数组。这里说一个我经常遇到的问题,我们在使用vs编译器时是不能用变量创建数组的。

再来举例说明如何创建数组,比如我要创建一个有十个元素的数组。

int arr[10];

这样数组就创建出来了,但是只创建数组是一个不好的编程习惯,这样创建的数组缺少可控性。通常我们在创建数组后会对数组进行初始化

1.2一维数组的初始化 

而初始化就是在创建数组的同时对数组合理地赋初值。我们可以这样赋值

int arr1[5] = { 1,2,3,4,5 };//完全初始化
char arr2[5] = "hehe";      //同上
char arr3[5] = { 'h', 'e', 'h', 'e' };//不完全初始化,没被赋值的元素默认被赋值成0
int arr4[5] = { 1,2,3 };              //同上
int arr5[] = { 1,2,3 };               //省略数组长度,赋值1,2,3时,程序将数组长度默认设置成3个
char arr6[] = { 'h', 'e', 'h', 'e' }; //同上,数组长度为4,该字符数组中的字符串没有结束标志

对整形数组赋值时我们通常把要赋的值以逗号间隔,放进大括号中。如果是对字符串数组赋值,我们也可以引用双引号创建数组(如arr2)。arr4的创建方式是不完全初始化,对一个数组进行不完全初始化时,其余数组元素均为0。我们也可以不写数组长度,像arr5 的创建方式,arr5的长度为3,你放几个数,数组就多长。

但是我们初始化的arr2,arr3和arr6 有区别吗?

用strlen求出各自的长度,结果arr6输出的值有误,打印每个数组,又是arr6 打印结果有误。

这又是为什么?由于如果我们在arr的[]中什么都不放,以'h', 'e', 'h', 'e'的形式初始化arr6,数组长度为4。'0'在数组中没空间存储,而我们知道 '\0'是字符串的结束标志,'\0'与'0' 的意义又是相同的,所以arr6 的字符串没有结束标志,程序不知道它的结束标志在哪,所以会出现随机值。这是c语言中常见的错误,在写代码的过程中要注意。

1.3一维数组的使用

对数组初始化后,我们要怎么来使用呢?比如有一个数组,要怎么打印数组里的元素?我们可以通过 ' [ ] ' 下标访问操作符来访问数组元素。这里要注意的是,数组的下标总是从0开始。

int main()
{
	int arr[10] = { 0 };//不完全初始化
	int i = 0;
	//对数组元素进行赋值,数组下标从0开始,循环也从0开始
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
	}

	//输出数组内容
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

我们通过下标访问操作符 ' [ ] ' 以及循环打印了数组的每个元素。

又一个问题,如果数组元素很多,我们要怎么求数组长度?总不可能一个个数吧。

int sz = sizeof(arr) / sizeof(arr[0]);

这里我们用sizeof操作符,sizeof(arr)算出整个数组大小,sizeof(arr[0])是第一个元素大小,用总量除以一个元素的大小就能算出整个数组的元素个数。 

1.4一维数组的存储

先打印数组中每个元素的地址,观察它们有什么规律

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("&arr[%d] = %p\n", i, &arr[i]);
	}
	return 0;
}

可以看到,通过下标的增长,元素的地址也在不断地增长,由于数组元素的地址是连续存放的,我们要找到其他元素的地址只需要知道第一个元素的地址,因此我们能通过指针来访问数组。

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int i = 0;
	int* p = &arr[0];//指针变量为数组首元素地址
	for (i = 0; i < 10; i++)
	{
		printf("&arr[%d] = %p\n", i, p + i);
	}
	return 0;
}

注意:这里的p + i并不是将首元素地址+i,而是将地址跳过i 个元素(假设p 为首元素地址),而我们创建一个整形数组,p + 1 表示的是下标为1 的数组元素地址,这个1 是跳过1个整形后的地址,地址将会加上两个整形的大小,也就是4。

2.二维数组的创建和初始化

2.1二维数组的创建

和一维数组类似,二维数组只是多加了个[ ],比如创建一个3行4列的二维整形数组。

int arr[3][4];

2.2二维数组的初始化

当然,和一维数组一样,我们要养成编程好习惯,在创建数组时随便把数组初始化。不过要注意的是 :二维数组的数组长度不能全部省略,我们可以省略行,但是不能省略列的个数

int arr[][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

原理很简单,既然是二维数组,那就要有行和列。我们知道数组第二行的开头是接在第一行的最后一个元素后面,可我连第一行在哪结束都不知道,那程序要怎么给数组赋值? 所以我们是不能省略二维数组的列数的。当我们省略行数时,程序会根据列数确定一行中有多少个元素,所以,我们在创建二维数组时,列数一定不能省略。

跟一维数组不同,我们在对二维数组赋值的时候是可以在具体的行中赋值(当然,赋是值的数量不能超过一行的列数)。比如,我们创建一个三行三列的二维数组,把1,2放在第一行,3,4放在第二行,5,6放在第三行,我们可以这样赋值。

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

2.3二维数组的使用

和一维数组一样,我们也是通过下标访问的方式来使用数组内的元素。例如要打印一个二维数组,我们通常是用循环嵌套的方式打印。

int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

打印结果 

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

哈哈相信你已经知道结论,没错,和一维数组一样,二维数组中的每个元素的地址都是按从高到低连续存放的。我们也打印二维数组中每个元素的地址。

int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("&arr[%d][%d] = %p\n", i,j,&arr[i][j]);
		}
	}
	return 0;
}

结论和一维数组相同。 

3.数组越界

在数组中下标规定都是从0开始的,也就是说最后一个元素的下标是n - 1。下标小于0或下标大于n - 1时,下标超出规定的合法范围,使用这样的下标就是越界访问。但是编译器在检查错误时不会对越界访问报错,所以在写代码时我们要注意自己的代码有没有越界访问的行为,从而减少产生不可预知的错误。

同样,二维数组也会有越界的情况,这里不再赘述 

 4.数组作为函数参数

4.1数组名是什么?

首先思考一个问题,数组名是什么?打印数组名的地址,会打印出整个数组的地址吗?

int main()
{
	int arr[10] = { 1,2,3,4,5 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	printf("%d\n", *arr);
	return 0;
}

会打印出什么呢? 可以看到数组名的地址和数组首元素地址相同,对数组名解引用,打印出的是数组首元素。可以得出结论,数组名就是数组首元素的地址。

但是有两个特例:1.sizeof(数组名)计算的是整个数组的大小,数组名代表整个数组。

2.&数组名,取出的是整个数组的地址。两种情况下,数组名表示的都是整个数组。

通过刚刚的讲解,我们知道数组名代表的是首元素的地址,对数组名arr 加1 ,地址增加4(一个整形的大小),而对&arr 加1 ,地址增加20,跳过5个整形,而我们创建的数组长度是5,所以&arr + 1 跳过的是整个数组,&arr代表的是整个数组的地址。这样我们就能直观的理解这两种特殊情况了。

4.2数组名作为函数参数 

我们知道在大多数情况下,数组名都代表数组首元素地址。那我们能不能在函数中计算数组的长度呢?用一个数组传参给函数,计算该数组的长度。

通过调试,sz的值不是我们想要的3。这是因为数组名代表数组首元素的地址,数组传参时只传了一个地址(这里int arr[]本质上表示的是一个指针,我们也能写成int* p,但是int arr[]更好理解),所以sizeof(arr)算出的是4(一个整形的大小)。那要计算数组长度时应该怎么做?我们可以在主函数就计算出数组长度,再把数组长度作为实参传给函数。 

这里挖个坑,过几天我会整理用函数编写冒泡排序的思路,到时用这个例子再讲一次计算数组长度的正确写法。


还没复习高数的我对明天的高数期中考瑟瑟发抖...

如果觉得博主写的不错,动动小手,点赞支持一下呗。

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值