C语言数组

为什么需要数组?

我们创建少量相同类型变量时,通常会逐个创建,但是当我们创建的相同类型变量越来越多时,逐个创建变量就不太实用了,我们最好一口气创建许多变量,这时引入数组,可以快速方便地创建多个相同类型的变量,大大提升了我们的效率。

一.一维数组

1.概念

什么是数组?

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

相同类型:全是整形,全是字符型,全是浮点型……

2.创建

数组创建的方式:

type_t arr_name [const_n]
//type_t 是指数组的元素类型
//const_n 是一个常量表达式用来指定数组大小

一般创建方式:

int arr[10];

注意[ ]中是常量表达式

可以用define定义变量n来创建数组:

#define n 10
int main(){
int arr[n] ;
return 0;
}

以下这样的就不行:

int n = 10;
int arr[n];

注:在c99中引入了变长数组的概念,允许数组的大小用变量来指定,若编译器不支持c99中的变长数组,那就不能使用,如:VS2019是不支持变长数组的。

还有个例外:

const int n = 0;  //这样是错误的,用const修饰的n不能创建数组
int arr[n] ;

 在c语言中,const修饰的变量叫常变量,还是变量。(c++中是常量)

3.初始化 

创建的同时赋值

int arr[10]={1,2,3,4,5};//不完全初始化,剩下的元素补为零
int arr[10]={1,2,3 4,5,6,7,8,9,0};//完全初始化
//int arr[10];不初始化数组中赋予随机值,有时编译器会报错不让我们使用

注:变长数组不能初始化。 

再来对比这两种创建方式: 

int arr1[10] = { 1,2,3,4 };
int arr2[] = { 1,2,3,4 };

 

 调试后,我们发现初始化时[  ]中不给值,数组创建的元素个数会按照我们给的值进行创建。

而我们注明元素个数后,即使我们给的数不够10个,它也会将我们剩下的元素赋为零

int型:(以上都以int为例)

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

char型:

char arr[3] = { 'a','b','c' };
char arr[3] = { 'a',98 ,'c' };//b的阿斯特码值是98,所以这个地方也可以用98
char arr[ ] = "abc";//注意此处数组中有4个元素 a b c \0

4.使用

我们想要精确使用每个元素时,要用[  ]下标引用操作符

每个元素已被标号,注意标号是从0开始,如下图:

我们想打印5时,应引用第4号元素 

#include<stdio.h>
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d", arr[4]);    //arr和4是[ ]的两个操作数
}

 


数组的元素个数可以通过计算获得 :

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

 

 


 

这里注意strlen和sizeof求字符串长度

#include<stdio.h>
#include<string.h>
int main() {
	char str[] = "abcd";
	printf("%d ", sizeof(str));
	printf("%d ", strlen(str));
	return 0;
}

 

结果是5和4

原因:

 str数组中存放的有'a' 'b' 'c' 'd' '\0'

strlen函数的原理是每检测到一个字符+1,直到检测到'\0'停止,所以不包括‘\0’,结果是4

sizeof计算的是数组中元素的总是,不管是不是'\0',所以结果是5

 

 5.在内存中的存储

 接下来,我们要从基层更深入的了解数组每个元素存储的方式

看代码:

#include<stdio.h>
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0;i < sz;i++) {
		printf("&arr[%d] = %p\n",i, &arr[i]);   //%p--打印地址(16进制)
	}
}

 

 

结果差4,原因是int型的数组中元素都是int型,元素大小为4个字节,导致他们地址差4,刚好差出一个整形元素 

由上我们可以得出:

  • 结论1:一维数组在内存中连续存放。
  • 结论2:随着数组下标增长,地址是由低到高变化的。

所以,我们也可以应用连续存放的性质,

#include<stdio.h>
int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0;i < sz;i++) {
		printf("arr[%d] = %d\n",i, *(arr+i));//指针+1跳过1个整形,因为数组是连续存放的
	}                                        //所以直接指引到下一个元素
}

 

 同样可以使用每一个元素


二.二维数组

1.创建

创建方式与一维数组大同小异

int arr[3][5];//三行五列的数组

2.初始化

我们先将它们完全初始化:

int main() {
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	return 0;
}

我们发现了二维数组的赋值规律。 


 不完全初始化:

第一种情况:如果赋6个值,是否将第6个数赋到第二行?

int main() {
	int arr[3][5] = { 1,2,3,4,5,6 };
	return 0;
}

 我们发现,6确实被赋值到第二行,且没有赋值被初始化为0

第二种情况:括号中的括号代表的是什么?

int main() {
	int arr[3][5] = { {1,2},{4,5},{5,6} };
	return 0;
}

 我们发现第一个被括起来的被初始化在第一行,第二个被括起来的被初始化在第二行,第三个被括起来的被初始化在第三行,没被初始化的元素默认为零。

第三种情况:可以省略行,但不能省略列 

int main() {
	int arr[][5] = { 1,2,3,4,5,6 };
	return 0;
}

 此处行被省略,但是有列,当满五个数时,第六个数自动被初始化到第二行

这时int arr[ ][5]中[ ]被默认为2

3.使用

与一维数组的使用基本相同,要注意首元素a[0][0],序号是从0开始。 

打印二维数组

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

 

也可改进一下:

#include<stdio.h>
int main() {
	int arr[3][5] = { 1,2,3,4,5,6 };
	int i = 0;
	int j = 0;
	for (i = 0;i < sizeof(arr)/sizeof(arr[0]);i++) {//arr[0]将第一行看成一个数组,用总大小除以第一行大小就是行数
		for (j = 0;j < sizeof(arr[0])/sizeof(arr[0][0]);j++) {//arr[0]——第一行大小,用第一行大小除以第一个元素大小就是列数
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

 这里我们可以将数组的行和列计算出来

4.在内存中的储存

 我们将每个数组元素地址打印出来

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

 

 观察结果我们发现:像一维数组那样,每个地址相差4,但是第二行的第一个地址与第一行末尾元素地址相差4,说明二维数组在内存中也是连续存放的,其中第一行与第二行也是连续的

 二维数组也可以想象成一维数组,第一行数组名arr[0],第二行数组名arr[1],第三行数组名arr[3]

 三.数组越界

数组的下标是有范围限制的

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

如果数组下标小于0,或者大于n-1,就代表数组越界了,但编译器不一定报错,但使用越界的值是导致的后果是不可知的,很可能引起程序错误。

 四.数组作为函数参数时的一些问题

数组名就是数组首元素的地址

有2个例外:

  • sizeof(数组名),数组名不是数组首元素的地址,数组名表示整个数组,计算的是整个数组的大小
  • &数组名,数组名不是数组首元素的地址,数组名表示整个数组,取出的是整个数组的地址

然而,数组名在函数传参给sizeof后时,数组名表示首元素地址

我们来设计一个函数计算数组元素个数

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

结果为1,而不是10

为什么呢?

原因是arr_size传过去的是arr数组的首地址并不是将整个数组的地址传过去,所以sizeof(arr)计算结果为arr首元素地址大小(32位,8个16进制数组,4byte),不是整个数组的大小

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘子13

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值