一篇让你理解数组

目录

数组的创建

数组的初始化

数组的完全初始化

一维数组的使用

 数组的下标

数组的大小

sizeof和strlen的区别

一位数组在内存中的存储

二维数组的创建

 二维数组的初始化

二维数组的完全初始化

 二维数组的不完全初始化

二维数组在内存中的存储

二维数组的行和列省略问题

数组越界问题

数组作为函数参数

易错提醒

数组名


数组的概念 :数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式,是用于储存多个相同类型数据的集合。

数组的创建

type_t (元素的数据类型)  arr_name(数组名)   [const_n](数组的元数个数(数组的大小))。

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

数组创建注意事项 

1 ,数组创建arr[ ]中方括号能否是变量?

 在一般编译器是不能是使用变长数组(数组方括号可以为变量)的,但是在c99标准下支持变长数组。(不用做过多了解)

2.有没有其他类型的数组呢?

 是可以的,任何数据类型的都有数组;

总结1.数组的创建方括号[ ]不能是变量只能为常量。

2.数组有很多类型,比如char,int,double等等。

数组的初始化

数组的完全初始化

所谓完全初始化:是在创建所有的元素中都将每个元素初始化满

 数组的不完全初始化

不完全初始化就是在创建所有元素中没有将元素初始化满

 那不完全初始化剩下的元素都没有了么?

 根据调试结果可知剩下的都初始化为0,也就是当你没有完全初始化的时候都会将为初始化的值初始化为0。

 字符与字符串的初始化

 我既然没有初始化元素个数那这字符和字符串的元素都是3么?

 显然不是千万记住字符串呢有个\0,(\0也占内存的一个空间)。

注意

我们尽量写数组时都给他初始化一下,防止造成数组越界

总结:数组有初始化和不初始化,未初始化的值默认为0

且字符串结尾有个\0

一维数组的使用

我们首先创建一个一维数组

 数组的下标

既然是数组,就应该有数组的下标,还记得[ ]这个是什么符号么,它就是下表引用操作符就是来指出数组的下标的,那数组的下标从哪开始呢?

 数组的下标是从0开始访问的

数组的大小

数组有大小那如何计算数组的大小呢?

 我们枚举数组的内容

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

所以数组的大小可以被我们计算出来。

sizeof和strlen的区别

sizeof是一个运算符用来计算变量在内存中所占空间的大小只关心空间大小不在乎\0

strlen是一个库函数用来求字符串长度,只能针对字符串,关注的是字符串中的\0

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abc";
	//int len = strlen(arr);
	printf("%d\n", strlen(arr));
	printf("%d\n", sizeof(arr));
	return 0;
}

 从这里看到strlen只关心求字符串长度,而sizeof求的是arr所占空间大小。

一位数组在内存中的存储

既然看内存那我们就应该看看一位数组的地址是如何变化的

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

	return 0;
}

 我们应该能观察到相邻的元素间差4个字节,为什么是4呢?由于这里存放的是int类型的所以每个差4个字节。

所以我们这里就可以知道一维数组在内存中是连续存储的,并且随着下标的增长地址是由高到低变化的。

那既然知道数组在内存中是连续存储的又有什么用呢?

由于地址是连续存放的,那我只要知道第一个地址后面的地址也就全知道了,我们都用&arr[ ]来访问每一个元素的地址,那既然讲到地址我们就可以联想到指针,我们只要用指针变量存放第一个地址就可以找到后面所有元素的地址

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

那我们只知道元素的地址又有什么用呢?

如果我们知道首元素的地址,那以后的元素也可以被我们找到。

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

这样我们以后就多了一个访问元素的方法,,只要知道首元素的地址都可以访问到元素

二维数组的创建

既然有了一维数组那二维数组如何创建呢?

 这里的3就是二维数组的3行,4就是列

那是如何展现的呢?

 二维数组的初始化

二维数组的完全初始化

(与一维数组相似这里就不详细的叙述)

 二维数组的不完全初始化

 那这又是如何展现的呢?

与一维数组一样没有初始化的默认为0 

二维数组在内存中的存储

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

这里与一位数组存储的一样也是连续存储的

在空间上是如何排列的呢?

 是这样排列的么?当然不是

 他在内存中是这样存储的,连续的每个差4个字节,每个元素也差4个字节

二维数组的行和列省略问题

你只要知道有多少列(一行多少元素)就可以省略行,系统就会自动为二维数组分配行,你只要确定每行多少元素就可以。

数组越界问题

在我们写代码的过程中经常出现这个问题我们来演示一下

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

由于我们不小心在for循环中判断部分加了个=号但是由于数组下标为0~9,这时候就会出现数组越界问题,但这个错误编译器一般默认没有错误,有的时候会弹出栈溢出的对话框,有的时候不会出现,这时候我们一定要检查一下数组越界。

数组作为函数参数

我们上一篇文章讲到函数传参,那函数传参可不可以穿一个数组过去呢?当然可以,我们来写一个经典算法冒泡排序

冒泡排序的定义:

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法

它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

#include<stdio.h>
void bubble_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[10] = { 9,8,7,5,3,2,4,1,6};
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr,sz);
	int i = 0;
	for (i = 1; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

让我们分析一下这个冒泡排序的具体是怎样实现的

首先冒泡排序是相邻两个元素进行比较(加入我们是升序排列),如果前面的元素大于后面的元素那我们就进行交换,反之不交换

交换的过程如图

 那九个元素要进行要进行八次冒泡排序

易错提醒

我们在写代码的时候容易将数组计算在冒泡泡函数中,我们来尝试一下会发生什么错呢?

#include<stdio.h>
void bubble_sort(int* arr)
{
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[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[10] = { 9,8,7,5,3,2,4,1,6};
	bubble_sort(arr);
	int i = 0;
	for (i = 1; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

调试一下

 这里的sz=1;而我们要计算的sz应该是40/4=10;如果等于1就会跳出循环不会排序,那这里为什么会等于1呢?首先数组传参传的是数组首元素地址也就是指针,而指针我们在前面讲过都为4个字节,所以4/4=1,所以结果sz就为1,那我们怎么才能让它为10呢,那就在传数组之前就计算sz的大小就能算出10了

总结:在我们计算某一数组大小时候应该就在传数组之前就计算,也就是在main函数里计算

数组名

数组名是什么(这个很重要哦一定要好好记

我们说数组名是首元素的地址

证明

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

	return 0;
}

但有两个例外

第一个例外

&arr(数组名)arr取出的是是整个数组的地址

第二个例外

sizeof内部单独放一个数组名,sizeof计算的是整个数组的大小

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

这里我们分别计算&arr和arr的地址能看出虽然arr和&arr起始地址是一样的但是当看当前地址的下一个地址时我们就可以看到&arr是取出整个数组的地址,arr是数组首元素地址

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值