C语言笔记(数组篇一)知识点

希望文章能对你有所帮助,有不足的地方请在评论区留言指正,一起交流学习!

目录

1. 数组的概念

2.一维数组

2.1 ⼀维数组的创建

2.2.数组初始化

2.3⼀维数组的使用

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

2.5 sizeof计算数组元素个数

3.二维数组

3.1 ⼆维数组的创建

3.2⼆维数组的初始化

3.3⼆维数组的使用

3.4 ⼆维数组在内存中的存储

4.数组应用

4.1数组名字应用

4.2数组作为函数参数

5.数组越界


1. 数组的概念

数组是⼀组相同类型元素的集合;
• 数组中存放的是1个或者多个同类型的数据,但是数组元素个数不能为0。
• 数组中存放的多个数据,类型是相同的。
数组分为⼀维数组和多维数组,多维数组⼀般⽐较多⻅的是⼆维数组。

2.一维数组


2.1 ⼀维数组的创建
type_t arr_name [const_n]

type_t        指定数组中存放数据的类型,可以是: char、short、int、float 等,也可以自定义的类型。

arr_name   数组的名字

const_n      一个常量表达式,用来指定数组的大小

数组创建实例

    
//代码1
    int arr1[5 + 6];//常量表达式
	int arr2[5];
    char ch[8];
    double score[10];
//代码2
//使用变量来指定数组大小
	int n = 10;
	int arr3[n];//数组可以正常创建吗?

代码2 在VS2017的编译情况下,会出现报错。因此在VS2017的环境下,创建变量要使用常量。

2.2.数组初始化

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

数组的初始化⼀般使⽤⼤括号,将数据放在大括号中。

整型数组在不初始化的条件下,每个元素都默认为0

初始化程序

整型数组

(1)完全初始化  数组大小等于元素个数,如下

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

(2)不完全初始化 数组大小小于元素

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

剩余元素默认为0,可以调出监视窗口查看,发现从第五个元素之后的值都为0。

(3)不指定数组大小

int arr4[] = { 1,2,3 };

数组通过确定初始化的元素个数确定数组的大小

字符数组初始化

    //代码1
    char arr6[3] = { 'a','b','c' };
	char arr7[] = { 'a','b','c' };  //两种是一样的
	//代码2
	char arr8[10] = "abc";//使用字符串初始化  C后面还有一个"abc\0"  字符串中自带一个 \0
	char arr9[] = "abc";  //有四种元素  自带"\0"  看调试
    //代码3
    char  arr10[3]="abc";

代码1创建的字符数组中的变量是相同的    代码3和代码1中的初始化是一样的

代码2利用字符串创建的变量中,前四个元素值相同,数组的大小不同。使用字符初始化的话附赠一个'\0',字符串结束的标志。没有初始化的元素,默认为'\0'。

代码1和代码2的区别利用如下代码:

int main()
{
	char arr1[] = "abc";//  四个元素
	char arr2[] = { 'a','b','c' };//两者在内存中存储的区别,就决定了使用效果的区别  三个元素
	printf("%s\n",arr1);    //abc
	printf("%s", arr2);     //abc烫烫烫烫蘟bc 打印出来是随机值 本质就是内存的存储              
                            //存储的是随机值 arr1中有\0
						    //内存是连续的
	return 0;
}

运行结果如下

上述数组均为限制数组的大小,需要有结束标志结束数组。在arr2中的初始化中没有'\0',程序就会去寻找'\0'这个结束标志,而内存中存储的是随机值,因此出来的也是随机值。


2.3⼀维数组的使用

        ⼀维数组可以存放数据,存放数据的目的是对数据的操作,如何调取数组?

(1)数组的下标

        数组是有下标的,下标是从0开始的,假设数组有n个元素,最后⼀个元素的下标是n-1,下标就相当于数组元素的编号,如下:

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

[ ] ,这个操作符是下标引⽤操作符。通过下标引操作符就可访问数组中的每一个元素了。

要访问第七个元素可以使用 arr[7] ,想要访问下标是3的元素,就可以使用 arr[3]。

代码

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

如何输出整个数组的内容呢?
使⽤for循环产生0~9的下标,接下来使用下标访问就行了。目前看来数组的输出只能通过循环实现。

如下代码:

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

如何输入呢?

利用for循环

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d", &arr[i]);
	}
   	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
2.4一维数组在内存中的存储

 我们直接用程序来看每一个元素的地址,代码如下

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

运行的结果

可以发现

1.每个元素相差四个字节(一个普通整型的大小),

2.一维数组在内存中是连续的存放的,随着数组下标的增长,地址是由低到高变化的,值小的叫做低地址 ,大的叫做高地址。

图解

2.5 sizeof计算数组元素个数

        使用sizeof ,是可以计算类型或者变量⼤⼩的,其实 sizeof 也可以计算数组的大小。

sizeof()所求得的结果单位是字节。

        需要注意的是sizeof(arr)中的arr所代表的整个整型数组,arr[0]可以代表一个 一个元素。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", sizeof(arr));
    printf("%d\n", sizeof(arr)/sizeof (arr[0]));
	return 0;
}

运行结果如下

计算得出整个数组有10个元素的整型数组,多占空间是40个字节

3.二维数组

        如果我们把⼀维数组做为数组的元素,这时候就是⼆维数组,⼆维数组作为数组元素的数组被称为三维数组,⼆维数组以上的数组统称为多维数组。
 

3.1 ⼆维数组的创建
type arr_name[常量值1][常量值2];

int arr[3][5];
double data[2][8];

二维数组的创建行可以省略但是列一定不可以省略,如果列省略后,就无法确定数组有几行了。

3.2⼆维数组的初始化
	int arr1[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};//完全初始化三行四列
	int arr2[3][4] = { 1,2,3,4,5 };//不完全初始化  没有赋值的默认是0 
	int arr1[3][4] = { {1,2},{3,4},{5,6} };//按照什么方式初始化  一维数组
	//行是可以省略的,列不可以
	int arr1[3][4] = { 1,2,3,4,5,6,7,8,9 };
	int arr1[][4] = { 1,2,3,4,5,6,7,8,9 };//二者相同

二维数组完全初始化和不完全初始化和一位数组类似。

(1)完全初始化

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

(2)不完全初始化

int arr2[3][4] = { 1,2,3,4,5 };//不完全初始化  没有赋值的默认是0 

(3)按照行初始化

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

说明,在{ }中使用的{ }表示是给数组其中的一行所赋的值。

3.3⼆维数组的使用

⼆维数组的行是从0开始的,列也是从0开始的

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

(1)数组的访问

按照列号和行号访问指定元素,例如 11是第四列,第三行。

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

输入和输出

  类似于一维数组,不过我们需要的是两个变量,采用的for循环的嵌套。

int main()
{
	int arr[3][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", &arr[i][j]); //输⼊数据
		}
	}
	//输出
	for (i = 0; i < 3; i++) //产⽣⾏号
	{
		int j = 0;
		for (j = 0; j < 5; j++) //产⽣列号
		{
			printf("%d ", arr[i][j]); //输出数据
		}
		printf("\n");
	}
	return 0;
}

运行结果如下

3.4 ⼆维数组在内存中的存储

像⼀维数组⼀样,我们如果想研究⼆维数组在内存中的存储⽅式,我们也是可以打印出数组所有元素的地址的。代码如下:

int main()
{
	int arr[3][5] = { 0 };
	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]);
		}
	}
	return 0;
}

运行结果

        可以看出二维数组之间也是连续的每个元素的起始地址相差4个字节,并且二维数组在内存中也是连续存在的。下图会帮助你更好的理解

        如图可以明确的二维数组在内存中的方式和一维数组是一样的,因此我们可以将二维数组看做成一维数组的数组

4.数组应用

4.1数组名字应用

数组名字是首元素的地址

但是有两个例外

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

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

查看实例

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
//代码1
	printf("%p\n",arr);             
	printf("%p\n", &arr[0]);
//代码2
	printf("%p\n", &arr);
	return 0;
}

        运行结果如下

        三者的结果相同,但是代码1两种的表示方式一样,和代码2表示的不一样如下面的程序

 举出例子证明一下

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

运行结果如下

总结

        函数名字就是函数的首个元素的地址和去首个元素地址相同,但是和取数组地址是不相同的。理解好整个有助于下面数组指针的学习。二维数组类似。

        指针+1到底跳过几个字节和指针的类型有关。

4.2数组作为函数参数

写代码的时候,会将数组作为参数,比如:实现冒泡排序函数

什么是冒泡排序

思想:两两相邻元素进行比较,有可能的话需要交换,目标是实现一组数字的升序。

程序如下

void Sort(int arr[], int sz)
{
	//趟数
	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,6,8,5,2,3,1,4,7,0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	Sort(arr, sz);				
	for (int i = 0; i < 10; i++)
	{
		printf("%d  ", arr[i]);
	}
	return 0;
}

解释

        程序执行过程中传递过去的是数组,因此接受的也是形式数组。

        但是本质上,传递过去的是数组的首个元素的地址,因为数组在内存中的排序是连续的,因此可以沿着顺序找到各个元素。函数可以访问同一份数据,效率比较高所以子函数的程序也可以写成

void Sort(int* arr, int sz)

为什么传递的是地址呢

        数组传递 假设值传递形式(10000个元素),拷贝数据浪费时间和空间 ;仅仅传递是地址效率很高,同时访问同一组数据。

再举一个例子加深理解

void Sort(int arr[], int*pa)
{
	printf("%d  ", arr[1]);
	printf("%d  ", *(pa+1));

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

输出的结果

        两者的输出结果是相同,在指针的基础上+1就是下一个元素,和arr[1]的值相同,证明了本质上传递的是数组的首个元素的地址,是指针。*(arr+i)与arr[i]相同,&arr[i]和arr+i相同。

5.数组越界

        数组的访问是有边界的,比如数组中有10个元素,所以下标是0~9的范围,这就是边界。数组下标的范围就是数组的边界。所以数组的下标小于0,或者大于n-1,就是数组越界访问了。

        但是越界了并不代标着编译器报错,这个时候可能不会报错,所以我们自己要做好越界检查。如下图,数组访问的已经越界了,程序没有警告,依然出现了随机值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值