C语言-------数组(知识点+例题)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本节看点:

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

2.一维数组的使用

3.一维数组在内存中的存储

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

5.二维数组的使用

6.二维数组在内存中的存储

7.数组越界

8.数组作为函数参数

一、一维数组的创建和初始化

1.1数组的创建

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

type_    arr_name  [const_n];

//type_t  是指数组的元素类型

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

数组的创建实例

//代码1

int  arr1[10];

//代码2

int  count;

int arr2[count];

//代码3

char  arr3[10];

float arr4[1];

double  arr5[20];

注:数组创建,在C99标准前,【】中要给一个常量才可以,不能使用变量,在C99标准支持了变长数组的概念。但在VS编译器环境下不支持变长数组概念,在其他编译环境中,使用变长数组时,变长数组不能初始化。

1.2 数组的初始化

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

看代码:

int  arr[10]={1,2,3};//不完全初始化,未初始化部分默认为0

int arr2[]={1,2,3,4};//数组中有4个元素

int  arr[5]={1,2,3,4,5};//数组中有5个元素

char  arr4[3]={'a',98,'c'};

char  arr5[]={'a','b','c'};

char  arr6[]="abcdef";

数组在创建时如果不想指定数组的大小就得初始化,数组的元素个数根据初始化的内容来确定

但是对于下面的代码要进行区分,内存中如何分配

char  arr1="abc";

char arr2[3]={'a','b','c'};

观察上面的代码,我们发现arr1中存有3个元素,而arr2中存有4个元素.

1.3一维数组的使用

【】,下标引用操作符,他其实就是数组访问的操作符

下面我们来看代码

总结:数组时使用下标来访问的,下标是从0开始。数组的大小可以通过计算得到

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

接下来我们来探讨数组在内存中的存储

看代码:

通过观察,我们发现,每个元素的下标(地址)之间相差4个字节 ,随着数组下标的增长,元素的地址,也在有规律的递增。由此可以得出结论:数组在内存中是连续存放的

二、有关一维数组的几个经典练习

2.1删除指定数字

有一个整数序列(可能有重复的整数),现删除指定的某一个整数,输出删除指定数字之后的序列,序列中未被删除数字的前后位置没有发生改变.

思路分析:首先使用一个 for 循环遍历数组 a。如果找到一个等于 k 的元素,则使用另一个内部 for 循环将其后面的所有元素向前移动一个位置,覆盖掉等于 k 的元素。然后我们将数组的长度减少 1,并将外层循环的循环变量减少 1,以便再次检查当前位置

重难点:要考虑删除多个相同数字的情况

#include <stdio.h>
int main()
{
    int a[51]={0};
    int i=0;
    int k=0;
    int n=0;
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&a[i]);//数组的输入
    }
    scanf("%d",&k);//输入想要删除的数字k
    for(i=0;i<n;i++)
    {
        int j=0;
        if(a[i]==k)
        {
            for(j=i;j<n-1;j++)//查看后面是否有相同的要删除的元素
            {
                a[j]=a[j+1];
            }
            n--;//删除一个,个数少一
            i--;
        }

    }
    for(i=0;i<n;i++)
    {
        printf("%d ",a[i]);
    }
}

2.2有序序列判断

输入一个整数序列,判断是否是有序序列,有序,指序列中的整数从小到大排序或者从大到小排序(相同元素也视为有序)。

数据范围: 3 \le n \le 50 \3≤n≤50  序列中的值都满足 1 \le val \le 100 \1≤val≤100 

第一行输入一个整数N(3≤N≤50)。

第二行输入N个整数,用空格分隔N个整数。

输出为一行,如果序列有序输出sorted,否则输出unsorted。

思路分析:

题目中可以设置2个标记变量,flag1,flag2,满足相邻2个元素升序把flag1置为1,满足相邻的两个元素降序则把flag2置为1,如果flag1和flag2都是1,那就是乱序,否则,就是有顺序的。

​
#include<stdio.h>
int main()
{
	int flag1 = 0;//升序
	int flag2 = 0;//降序
	int i = 0;
	int n = 0;
	int a[101] = { 0 };
	scanf("%d", &n);
	for (i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
	}
	for (i = 0; i < n; i++)
	{
		if (i > 0)//输入一个后开始比较
		{
			if (a[i] > a[i-1])
			{
				flag1 = 1;//flag2=0
			}
			else {
				flag2 = 1;//flag1=0
			}
		}
	}
	if (flag1 + flag2 > 1)
	{
		printf("unsorted");
	}
	else
	{
		printf("sorted");
	}
	return 0;

}

​

2.3有序序列插入一个数

有一个有序数字序列,从小到大排序,将一个新输入的数插入到序列中,保证插入新数后,序列仍然是升序。

输入描述:
第一行输入一个整数(0≤N≤50)。

第二行输入N个升序排列的整数,输入用空格分隔的N个整数。

第三行输入想要进行插入的一个整数。

思路分析:从后往前遍历数组,如果数组中的元素大于要插入的数,那么应该一次后移,如果数组中的某个元素小于需要插入的数,那么就可以插入了,但是如果要插入的元素比其他元素都小,那就插到第一个元素的位置上。

易错点提示:本题思路较简单,但需要考虑边界情况

#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);//输入元素个数
	int i = 0;
	int a[51] = { 0 };
	for (i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);//数组的输入
	}
	int k = 0;//输入要插入的数
	scanf("%d", &k);
	for (i = n-1; i >= 0; i--)//从后往前遍历
	{
		if (a[i] > k)
		{
			a[i + 1] = a[i];
		}
		else
		{
			a[i + 1] = k;//数据的插入
			break;
		}
	}
	if (i < 0)//边界情况
	{
		a[0] = k;
	}
	for (i = 0; i < n + 1; i++)//插入之后,元素个数加一
	{
		printf("%d ", a[i]);
	}
}

2.4有序数列的合并

输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。

数据范围: 1 \le n, m \le 1000 \1≤n,m≤1000  , 序列中的值满足 0 \le val \le 30000 \0≤val≤30000 
输入描述:
输入包含三行,

第一行包含两个正整数n, m,用空格分隔。n表示第二行第一个升序序列中数字的个数,m表示第三行第二个升序序列中数字的个数。

第二行包含n个整数,用空格分隔。

第三行包含m个整数,用空格分隔。
输出描述:
输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重新进行升序序列排列合并。

思路分析:由于只是要打印出合并后的结果,所以不需要储存,现在2个数组中找较小的一次打印,等一个数组中没有元素了,打印另一个数组中的元素

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
    int n, m, a, b;
    scanf("%d%d", &n, &m);//n,m为两个数组中整数的个数
    int num1[1000] = { 0 };
    int num2[1000] = { 0 };
    int i = 0, j = 0;
    for (i = 0; i < n; i++) 
        scanf("%d", &num1[i]);//输入第一个数组的元素
    for (i = 0; i < m; i++) 
        scanf("%d", &num2[i]);//输入第二个数组的元素
    i = 0;
    while (i < n && j < m)
    {
        if (num1[i] < num2[j] && i < n && j < m) 
            printf("%d ", num1[i++]);//如果第一个数组中的第一个元素小于第二个数组中的第一个元素,就输出第一个数组中的第一个元素,
        else                         //并将i+1,用第一个数组中的第二个元素与第二个数字中的第一个元素进行比较
            printf("%d ", num2[j++]);//否则,输出第二个数组中的第一个元素,并将第二个数组中的第二个元素与第一个数组中的第一个元素进行比较
    }
                                       //最后输出的是从小到大的两个数组中的经过比较的元素
    while (i < n) 
        printf("%d ", num1[i++]);//输出没有比较的元素
    while (j < m) 
        printf("%d ", num2[j++]);//输出没有比较的元素
    return 0;
}

2.5有序序列去重后按从小到大排序

老师给了小乐乐一个正整数序列,要求小乐乐把这个序列去重后按从小到大排序。但是老师给出的序列太长了,小乐乐没办法耐心的去重并排序,请你帮助他。


输入描述:
第一行包含一个正整数n,表示老师给出的序列有n个数。接下来有n行,每行一个正整数k,为序列中每一个元素的值。(1 ≤ n ≤ 105,1 ≤ k ≤ n)

输出描述:
输出一行,为去重排序后的序列,每个数后面有一个空格。

思路分析:这题如果用传统排序、去重方法也可以很好但解出来,大概思路就是先把无序的数组用冒泡排序的方法整理成有序数组,然后进行去重就搞定了,大家如果有兴趣的话可以去尝试,在这里给大家介绍一个简单的方法

举个例子:

#include <stdio.h>
int main()
{
    int arr[1000001]={0};
    int i=0;
    int n=0;
    int k=0;
    scanf("%d",&n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&k);
        arr[k]=k;//元素的存放
    }
    for(i=0;i<n;i++)
    {
        if(arr[i]!=0)
        printf("%d ",arr[i]);//输出
    }
}

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

3.1二维数组的创建以及初始化

类比一维数组的相关知识,我们来浅浅谈一下二维数组的创建以及初始化

数组创建:int  arr[3][4];

int   arr[3][5];

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

数组初始化:

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

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

int arr[][4]={{2,3},{4,5}};//二维数组初始化,行可以省略,列不能省略

3.2二维数组的使用

推论:二维数组的访问也是通过下标的方式

分析如下代码,验证推论

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

像一维数组一样,这里我们尝试打印二维数组的每个元素的地址:

通过结果我们可以分析到,其实二维数组在内存中也是连续存放的

可以把二维数组看成特殊的一维数组,arr[0]、arr[1]、arr2[2]作为每一行的数组名

 也正是因为上诉原因,所以在创建二维数组时列不能省略

3.4数组进阶练习题

找鞍点

一个矩阵的鞍点指的是该位置上的元素值在该行最大,该列最小。本题要求编写程序,求给定的n阶方阵的鞍点

输入格式:输入第一行给出一个整数n,随后n行,每行给出n个整数,随后n行,每行给出n个整数,其间以空格分隔

输出格式:输出在一行中按照“行下标 列下标(下标从0开始)“”的格式输出鞍点的位置,如果不存在鞍点,则输出“None”,题目保证给出的矩阵最多存在一个鞍点

下面我们来一起看看代码实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	int n = 0;
	int arr[10][10] = { 0 };
	scanf("%d", &n);//输入行数
	int i = 0;
	int j = 0;
	int m = 0;
	int flag = 0;
	int r = 0;
	int c = 0;
	int r1 = 0;
	for (i = 0; i < n; i++)//
	{
		for (j = 0; j < n; j++)
		{
			scanf("%d", &arr[i][j]);//二维数组的输入
		}
	}
	for (i = 0; i < n; i++)//以行为单位遍历数组
	{
		int k = 0;
		m = arr[i][0]; //假设一开始第i行的第1个元素为最大值
		r = i;//标记最大值的位置
		c = 0;
		for (j = 0; j < n; j++)
		{
			if (arr[i][j] > m)
			{
				m = arr[i][j];//更新最大值
				c = j;
			}
		}
		r1 = r;
		for (k = 0; k < n; k++)
		{
			if (arr[k][c] < m)
			{
				r1 = k;//找列中的最小值,记录它的位置
			}
		}
		if (r1 == r)//如果二者相等,满足条件
		{
			printf("%d,%d", r, c);
			flag = 1;
		}
	}
	if (flag == 0)
	{
		printf("None");
	}
	return 0;

}

四.数组越界

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

数组的下标是从0开始的,如果有n个元素,最后一个元素的下标是n-1.

如果数组的下标小于0,或者大于n-1,就是数组越界访问了,超出了数组的访问空间

C语言本身是不做数组下标越界的检查,编译器也不一定报错,但是编译器不报错,并不意味着程序本身是正确的。

所以程序员在写代码时,最好自己做越界的检查

 二维数组的行和列也可能存在越界。

五.数组做为函数参数

5.1关于数组名的那些事儿

往往我们在写代码的时候,会将数组作为参数传给函数,这时,我们传的通常是数组名,那么,数组名到底是什么呢?接下来,让我们一起研究研究。

通常来说,数组名就是首元素地址

But,如果只是单纯地把数组名当成首元素地址看待,那就入坑了!

 总结:数组名就是地址,通常来说,数组名是首元素地址

但是有两个例外

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

(2)&数组名,这里的数组名表示整个数组取出的是整个数组的大小

除此之外,遇到的数组名都是首元素地址

5.2案例分析

构造一个函数,对整型数组进行排序

思路分析:两两相邻的元素进行比较

有了大致的思路,相信大家都已经迫不及待地想写代码了!于是……

出现了问题☹☹☹ ,那就找一下问题吧!调试之后可以看到函数内部的sz是1.结合上文所学知识,我们知道,数组传参传的是首地址,而对于整型元素而言,一个元素的大小是4个字节,在32位机器下,地址的大小也是4字节,于是,计算sz的表达式就被化为了4/4,这就不难理解为什么sz=1了,这也是上面代码出现的问题!

下面,我们一起修改这段代码:


总结

掌握了数组的基本知识,本文也就结束了,希望本篇文章能够帮助大家更好的了解数组!若有不足之处,也希望大家在评论区指出,本人一定及时改正!

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值