C语言(七)数组

这篇博客介绍了数组在C语言中的使用,包括如何定义、初始化和遍历数组,以及处理数组越界的安全隐患。通过数组统计特定范围内数字出现次数和寻找素数的过程,阐述了动态调整数组大小、优化算法效率的方法,如避免无效计算和利用已知素数表。同时,讨论了二维数组的概念和遍历技巧,并展示了在井字棋游戏中检查输赢的二维数组应用。

8.1.1 初试数组

之前提到过如何计算用户输入的数字的平均数?
之前的做法:每读到一个数(!=-1)加到sum里,cnt++,最后sum/cnt
这样我们不需要记录每一个数
如果题目还要求:输出所有大于平均数的数?这样就必须记录每一个数了,因为我们是最后才计算平均数的,要最后再用每个数和平均数做判断
如何记录很多数?int num1,num2……?不好。这样下去无穷无尽。

使用数组

int number[100];//定义数组,表示数组可以放100个int
scanf(%d”,&x);
while(x!=-1){
	number[cnt]=x;//对数组中的元素赋值
	cnt++;
	scanf(%d”,&x);
}

最后再加个cnt长度的循环,判断每一个数与平均数比较大小

if(cnt>0){
	int i;
	double average=sum/cnt;
	for(i=0;i<cnt;i++){
		if(number[i]>average){//使用数组中的元素
			printf("%d ",number[i]);//遍历数组
		}
	}
}

这个程序的安全隐患在于没有考虑使用的数组下标是否会超过100.定义的时候注意要求。

8.1.2 数组的使用:如何定义和使用数组,数组的下标和下标的范围

定义数组:
<类型>变量名称[元素数量];//方括号表示这是个数组

int grades[100];
double weight[20];

元素数量必须是整数。在c99之前,元素数量必须是编译时确定的字面量。(a[n]不行)vscode中好像就不行,提示variable-sized object may not be initialized
数组是一种容器,特点是:
其中所有元素具有相同的数据类型;
一旦创建,不能改变大小;
其中元素在内存中连续依次排列(从0开始);
如:定义十个单元a[10]→a[0]~a[9]
在这里插入图片描述

每个单元就是一个int类型的变量。像普通变量一样可以出现在赋值的左边或右边。左边的叫左值,右边的叫右值。
数组的每个单元就是数组类型的一个变量。使用数组时[]中的数字/变量叫下标或索引,从0开始计数
(要习惯数数从0开始到n-1)
但编译器和运行环境不会检查数组下标是否越界,无论读、写数组单元。
不过数组越界时可能出问题:segmentation fault,运气好的话不会造成严重的后果。
所以这是程序员的责任来保证程序只适用有效的下标值(范围:[0,数组大小-1])

防止读入数字超过100个的方法:
方法一:cnt=100之后停止读数;
方法二:利用c99数组大小可以是动态的的特性,定义number[cnt];//用户先输入cnt

可不可以int a[0];?
可以,但是没用。

8.1.3 数组的例子:统计个数

不停输入0~9范围内的整数,读到-1停止,统计每种数字出现的次数。
和上一道题不同的是,不用记录每次输入的数字,我们需要记录的是每种数字出现的次数。
学到了定义数组为0的方法:

for(int i=0;i<10;i++)count[i]=0;

和打印方法:

for(int i=0;i<10;i++)printf(%d\n”,count[i]);

该题中出现多次数字10。根据之前学到的方法,我们可以定义const number=10(c99才能用);每一个10用number代替。

通常用到数组的程序都需要的环节:

  1. 确定数组大小;
  2. 定义数组;
  3. 初始化数组;
  4. 数组参与运算;
  5. 遍历数组输出。

8.2.1 数组运算

搜索:在一组给定数据中,怎样找出某个数据是否存在?
(往函数中传数组:int sum(a[]))
数组的集成初始化:

int a[]={2,4,6,7,1,3,5,9}
/*直接用大括号给出数组所有元素的初始值;
不需要给出数组的大小,编译器替你数了。*/

依次初始化数组的每一个单元
如果a[13]={2};只有a[0]是2,后面的单元都是0
所以如果想定义一个数组全为0:a[13]={0};
C99还可以在大括号里给指定的位置赋值。
用[n]在初始化数据中给出定位,没有定位的数据接在前面的位置后面;其他位置的值补0.

int a[0]={[0]=2,[2]=3,6};
这个例子里,a[0]=2,a[2]=3,a[3]=6
我们也可以不给出数组大小,让编译器计算。比如上例可写为:
int a[]={[0]=2,[2]=3,6};
这样会根据大括号里最大的数字下标来算数组的大小。即下标最大为3
这样特别适合初始数据稀疏的数组。

数组的大小

sizeof给出整个数组所占据的内容的大小,单位是字节。(n*4,sizeof(a)/sizeof(a[0])就能得到数组元素个数)
不能直接把一个数组赋给另一个数组b[]=a;
数组变量本身不能被赋值。如果想把一个数组的值全部交给另一个数组,必须遍历。

遍历数组

通常使用for循环,从0开始到<n,这样循环体最大的i正好是数组最大的有效下标。
常见错误:1.循环结束条件是<=数组长度
2.离开循环之后继续使用i作为数组元素下标。

数组作为函数参数时,往往需要另一个参数来传递数组大小。
原因:1、数组传入函数之后我们不能用sizeof来计算数组的元素个数;
2.不能在[]中给出数组的大小。
具体原因后面再说。

8.2.2 数组例子:素数

之前找素数的例子。我们可以定义isPrime()函数来判断一个数是否是素数。
isPrime()函数:我们从2到x-1都拿去除x,循环要走很多遍,重复执行的次数很多(程序很差)。
优化:当x是!=2的偶数,一定不是素数,就直接不用判断。
因为剩下需要判断的书都是奇数,肯定%2=1,这样我们判断接下来的数时for循环除数就可以从3开始的奇数判断。

for(int i=3;i<x;i+=2)

再次优化:我们不需要走到x。我们只要走到sqrt(x)就够了。

for(int i=3;i<=sqrt(x);i+=2)

引入:当我们想了解一个函数时,在编译器中输入man 函数名称(man sqrt)就能得到其相关信息。(man:manual手册)
Windows用户:打开浏览器搜索。
再再次优化:我们不需要拿比x小的数字来测试x是不是素数,我们只需要拿比x小的素数就够了。

int isPrime(int x,int knowsPrimes[],int numberofKnownPrimes)
int main()
{
	const int number=100;
	int prime[number]={2};
	int count=1;
	int i=3;
	while(count<number){
		if(isPrime(i,prime,count)){
			prime[count++]=i;
		}
		i++;
	}//prime数组装着所有素数
	for(i=0;i<number;i++){
		printf("%d",prime[i]);
		if((i+1)%5)printf("\t");
		else printf("\n");
	}
	return 0;
}
 int isPrime(int x,int knowsPrimes[],int numberofKnownPrimes)
 {
 	int ret=1;
 	int i;
 	for(i=0;i<numberofKnownPrimes;i++){
 		if(x%knownPrimes[i]==0){
 			ret=0;
 			break;
 		}
 	}
 	return ret;
 }

一边构造素数表,一边利用表来证明素数。
其中prime[cnt++]=i;一举两得,cnt++的同时还把i的值放到了对应的数组位上。

while(count<number){
	if(isPrime(i,prime,count)){
		prime[count++]=i;
	}
	{
		printf("i=%d \tcnt=%d\t",i,count);
		int i;
		for(i=0;i<number;i++){
			printf("%d\t",prime[i]);
		}
		printf("\n");
	}
	i++;
}

这样加个括号在里面int i之后,我们使用i就不会影响到外面的i的值了(但是我宁愿重新定义个变量j=i。因为太绕了)

同样的方法,我们可以先输出一个表头。

{
	int i;
	printf("\t\t\t\t");
	for(i=0;i<number;i++){
		printf("%d\t",i);
	}
	printf("\n");
}

在这里插入图片描述

换一个思路,使得最后这张表里留下来的数都是素数。
欲构造n以内的素数表:

  1. 令x=2
  2. 将2x,3x……直到ax<n的数标记为非素数
  3. 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经被尝试完毕。
    (从2,3,4,5,6,7,8……删掉2的所有倍数,再删掉3的所有倍数,再删掉5的所有倍数……)
  4. 先开辟数组prime[n],初始化其所有元素为1,prime[x]=1表示x是素数,prime[x]=0表示x不是素数
  5. 令x=2
  6. 如果x是素数,则for(int i=2;x*i<n;i++)prime[i*x]=0
  7. x++,重复3直到x==n,否则结束。
int main()
{
	const int maxNumber=25;
	int isPrime[maxNumber];
	int i,x;
	for(i=0;i<maxNumber;i++)
	{
		isPrime[i]=1;
	}
	for(x=2;x<maxNumber;i++)
	{
		if(isPrime[x])
		{
			for(i=2;i*x<maxNumber;i++) isPrime[i*x]=0;
		}
	}
	printf("\n");
	return 0;
}

如此可见,算法的思考方式不见得与人相同。

8.2.3 二维数组

int a[3][5];通常理解为a是一个3行5列的矩阵
在这里插入图片描述

最好先行号再列号,和线性代数也是相对应的。
二维数组的遍历需要两个for循环

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

a[i][j]表示一个int
a[i,j]是逗号运算符(等于逗号右边的值),表示a[j],不是正确表达二维数组的方式。
二维数组的列数必须给出,行数可以交给编译器来数。

给数的时候每行一个{},用逗号分隔。如果省略表示0。
也可以用定位(注意只能是c99)
二维数组是逐行填满的,所以也可以不加大括号,当做一维数组去初始化
PS: tic-tac-toe 井字棋判断输赢问题:行列对角线分开检查。

const int size = 3;
int board[size][size];
int i,j;
int num0fX;//X是一方
int num0fO;//O是一方
int result=-1;//-1:平局,1:X方赢,0:O方赢

//读入矩阵
for(i=0;i<size;i++){
	for(j=0;j<size;j++){
		scanf("%d",&board[i][j]);
	}
}

//检查行
for(i=0;i<size&&result==-1;i++){
	num0fO=num0fX=0;
	for(j=0;j<size;j++){
		if(board[i][j]==1)num0fX++;
		else num0fO++;
	}
	if(num0fO==size)result=0;//O方赢
	else if(num0fX==size)result=1;//X方赢
}

类似的,遍历j检查列。
其实这样代码是重复的,我们可以想个办法用一个两重循环遍历行与列。
对角线就是board[i][i]board[i][2-i]两种情况。

C语言是古老而长青的编程语言,它具备了现代程序设计的基础要求,它的语法是很多其他编程语言的基础,在系统程序、嵌入式系统等领域依然是无可替代的编程语言,在各类编程语言排行榜上常年占据前两名的位置。 本课程是零基础的编程入门课,是后续的操作系统、编译原理、体系结构等课程的基石。 —— 课程团队 课程概述 程序设计是一门基础课程。对于计算机相关专业而言,程序设计是专业基础知识,是进一步学习其他专业知识的第一步阶梯;对于非计算机专业而言,程序设计的学习有助于理解计算机的能力所在,理解哪些是计算机擅长解决的问题,怎样的方式方法是计算机擅长的手段,从而能更好地利用计算机来解决本专业领域内的问题。 C语言是古老而长青的编程语言,它具备了现代程序设计的基础要求,它的语法是很多其他编程语言的基础,在系统程序、嵌入式系统等领域依然是无可替代的编程语言,在各类编程语言排行榜上常年占据前两名的位置。 对于非计算机专业的学生,学习本课程的主要目的是掌握程序设计的基本方法,C语言是教学媒介。但是对于计算机专业的学生,本课程是向后续的计算机组成、操作系统、编译原理、体系结构等课程前进的基石,对于C语言本身甚至程序设计语言基础原理的深入理解都是应该掌握的。 本课程是零基础的入门课程,完成本课程之后,就能具有初步的运用C语言编写程序的能力。要想完整的学习C语言,还需要进一步学习本课程的后续课程——《C语言程序设计进阶》。 程序设计是实践性很强的课程,该课程的学习有其自身的特点,听不会,也看不会,只能练会。你必须通过大量的编程训练,在实践中掌握编程知识,培养编程能力,并逐步理解和掌握程序设计的思想和方法。在这里所提供的,只是基础的知识讲解,要想学会编程,还需要更多时间的投入和努力。 为了学习编程,你需要有一台计算机,安装必要的编程软件。无论是MS Windows、Mac OS X还是Linux,都有适合C语言编程的软件。如果搞不定自己电脑上的编程软件,我们也会提供在网页中编写、运行C语言程序的方法。 课程大纲 01 程序设计与C语言 课时 1 计算机和编程语言 2 C语言 3 第一个程序 02 计算 课时 1 变量 2 数据类型 3 表达式 4 在线评判系统 03 判断与循环 课时 1 判断 2 循环 04 进一步的判断与循环 课时 1 逻辑类型和运算 2 级联和嵌套的判断 3 多路分支 4 循环的例子 5 判断和循环常见的错误 05 循环控制 课时 1 循环控制 2 多重循环 3 循环应用 06 数组与函数 课时 1 数组 2 函数的定义与使用 3 函数的参数和变量 4 二维数组 07 数组运算 课时 1 数组运算 2 搜索 3 排序初步 08 指针与字符串 课时 1 指针 2 字符类型 3 字符串 4 字符串计算 预备知识 作为第一门编程课,本课程可以零基础学习。大学计算机(大学计算机基础、计算思维导论)等课程对于理解本课程的部分内容有帮助,但这些课程的学习不是必须的。 证书要求 课程的总分达到60分以上,可以获得本课程的合格证书。 课程的总分达到85分以上,可以获得本课程的优秀证书。 参考资料 何钦铭、颜晖,《C语言程序设计(第3版)》,高等教育出版社,2015年,ISBN 978-7-04-043128-3 颜晖、张泳,《C语言程序设计实验与习题指导(第3版)》,高等教育出版社,2015年,ISBN 978-7-04-043563-4 常见问题 Q:我需要特殊版本的计算机吗? A:任何计算机都可以用于C语言编程,包括但不限于各个版本的MS Windows、各个版本的Mac OS X、各种发行版本的Linux。有的手机有app也能实现C语言编程,不过我们不推荐那么小的屏幕。 Q:我需要安装特定的软件才能做这门课的作业吗? A:任何C语言的开发工具都可以。课程演示会使用Dev C++及命令行环境,但是你使用任何其他开发工具都不会影响做作业的正确性。可以使用的其他工具包括但不限于MS Visual Studio、C-Free等。 Q:每周会发布多少时间的视频?我需要花多少时间来学习? A:我们按照90分钟的授课时间来设计课程。但是发布的视频肯定小于90分钟,一般在50到70分钟左右。因为在线下上课时,老师可能会回顾一下上周的内容、某些内容会看学生的反应重复几遍、会当堂问做一些提问,以及在各种软件等教学工具之间切换,这些都要花点时间,而这些在线上课程中都不存在了,所以视频的时间不会正好是90分钟。但是一般我们设计课程需要1:1.5到1:2的课后学习时间,这包括预习、作业、练习和复习的时间,所以每周大约需要花费三到五小时的时间。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

灰海宽松

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

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

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

打赏作者

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

抵扣说明:

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

余额充值