数组——C语言程序设计(六)

C语言程序设计(六)

数组

  • 如何写一个程序计算用户输入的数字的平均数?

  • 不需要记录输入的每一个数

  • 如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?必须先记录每一个输入的数字,计算平均数之后,再检查记录下来的每一个数字,与平均数比较,决定是否输出

# include <stdio.h>
int main()
{
	int x, i;
	double sum = 0;
	int cnt = 0;
	int number[100];   //定义一个数组的大小,存放整数型值 
	scanf("%d",&x);
	while(x!=-1){  //当输入的x为-1时,结束输入 
		number[cnt] = x;//将输入的数存进数组里 
		// 
		{
			printf("%d\t",cnt);//\t制表位 
			for(i=0; i<=cnt; i++){
				printf("%d\t",number[i]);
			}
			printf("\n");
		 } //调试代码的内容 
		 // 
		sum += x;
		cnt++;
		scanf("%d",&x);
	}
	if (cnt > 0){
		printf("%f\n",sum/cnt);  //输出平均值 
		for(i=0;i<cnt; i++){
			if (number[i] > sum/cnt){
				printf("%d ",number[i]);
			}//将大于平均数的数输出 
		} 
	} 
 }

输出结果:

注: 可以一次输入1 2 3 4 5 6 7 -1, 以空格隔开的输入和回车键输入是一样的效果,scanf("%d",&x); 计算机只会读取int型的值,空格不会读入,就相当于回车键

1 2 3 4 5 6 7 -1
0       1
1       1       2
2       1       2       3
3       1       2       3       4
4       1       2       3       4       5
5       1       2       3       4       5       6
6       1       2       3       4       5       6       7
4.000000
5 6 7

请添加图片描述

数组的定义和使用

定义数组

  • <类型> 变量名称[元素数量];

  • int grades[100]; //整数型的数组

  • double weight[20]; //浮点数类型的数组

  • 元素数量必须是整数

  • C99之前:元素数量必须是编译时刻确定的字面量

  • 是⼀种容器(放东西的东西),特点是:

    • 其中所有的元素具有相同的数据类型
    • ⼀旦创建,不能改变大小(C语言中不能动态改变数组大小; 即创建后就不能改变大小)
    • *(数组中的元素在内存中连续依次排列的; 因为会预先分配数组空间,留着存放数组)

int a[10]

  • 一个int的数组
  • 10个单元: a[0], a[1], …, a[9]

请添加图片描述

  • 每个单元就是一个int类型的变量

  • 可以出现在赋值的左边或右边:

    • a[2] = a[1] + 6; //将数组a[1]的值加6后赋值给a[2]
  • *在赋值左边的叫做左值

数组的单元

  • 数组的每个单元就是数组类型的一个变量

  • 使用数组时放在[]中的数字叫做下标索引,下标从0开始计数:

  • grades[0]

  • grades[99]

  • average[5]

  • 注:位序是从1开始计算的; 下标是从0开始计算的

有效的下标范围

  • 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
  • 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃
    • segmentation fault
  • 但是也可能运气好,没造成严重的后果
  • 所以这是程序员的责任来保证程序只使用有效的下标值:[0, 数组的大小-1]
# include <stdio.h>
int main()
{
	int x; 
	double sum = 0;
	int cnt = 0;
	int number[100];   //定义一个数组的大小,存放整数型值 
	scanf("%d",&x);
	while(x!=-1){  //当输入的x为-1时,结束输入 
		number[cnt] = x;//将输入的数存进数组里 
		sum += x;
		cnt++;
		scanf("%d",&x);
	}
	if (cnt > 0){
		int i; 
		double average = sum / cnt; 
		printf("%f\n",average);  //输出平均值 
		for(i=0;i<cnt; i++){
			if (number[i] > average){
				printf("%d ",number[i]);
			}//将大于平均数的数输出 
		} 
	} 
 } 
  • 这个程序是危险的,因为输入的数据可能超过100个
计算平均数
  • 如果先让用户输入有多少数字要计算,可以用C99的新功能来实现
# include <stdio.h>
int main()
{
	int x, n; 
	double sum = 0;
	int cnt ;
	printf("请输入你要输入的数字的个数:"); 
	scanf("%d",&n);	
	if (n > 0){
		int number[n]; //输入者来决定数组的大小 
		scanf("%d",&x); 
		while(x!=-1){  //当输入的x为-1时,结束输入 
			number[cnt] = x;//将输入的数存进数组里 
			sum += x;
			cnt++;
			scanf("%d",&x);
		}
		int i; 
		double average = sum / cnt; 
		printf("%f\n",average);  //输出平均值 
		for(i=0;i<cnt; i++){
			if (number[i] > average){
				printf("%d ",number[i]);
			}//将大于平均数的数输出 
		} 
	} 
 } 

输出:

请输入你要输入的数字的个数:5
0 1 2 3 4 -1
2.500000
3 4
存在长度为0的数组?
  • int a[0];
  • 可以存在,但是无用,没有意义,不能存放东西, 而且a[0], 数组从0到-1不符合规范

数组的例子:投票统计

  • 写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,
    输入-1表示结束
#include <stdio.h>
int main()
{
	int x, i;
	int count[10];
	
	for(i=0; i<10;i++){
		count[i] = 0; //初始化数组里面每个值都为0;便于计数 
	} //或者直接 int count[10] = {0},这样也可以初始化数组内的所有值都为0.
	scanf("%d", &x);
	while (x!=-1){
		if (x>0 && x<=9){
			count[x] ++;  //数组里的值从0开始计数,  count[x]表达式的结果是数组值 
		}
		scanf("%d", &x);
	}
	for (i=0; i<10;i++){
		printf("%d:%d\n", i, count[i]);
	}	 
	return 0;
	
} 

输出:

1 5 6 8 5 8 9 5 8 5 -1
0:0
1:1
2:0
3:0
4:0
5:4
6:1
7:0
8:3
9:1

或者:用number来代替10

请添加图片描述

数组的运算

  • 在一组给定的数据中,如何找出某个数据是否存在?

数组的集成初始化

  • int a[] = {2, 4, 6, 7, 1, 3, 5, 9, 11, 13, 23}; //集成初始化
  • 直接用大括号给出数组的所有元素的初始值
  • 不需要给出数组的大小,编译器替你数数
  • int b[20] = {2}; //除了数组中的下标为20的值为2;其他的都为0
  • 如果给出了数组的大小,但是后面的初始值数量不足,则其后的元素被初始化为0
#include <stdio.h>
int main()
{
	int i; 
	int a[10] = {0, 3, 4, 3};  
	for(i=0; i<10; i++){
		printf("%d\t", a[i]);
	} //遍历数组,输出数组 
	printf("\n"); 
	int b[5] = {0}; 
	for(i=0; i<5; i++){
		printf("%d ", b[i]);
	} //遍历数组,输出数组 
	
	return 0; 
}

输出:

0       3       4       3       0       0       0       0       0       0
0 0 0 0 0

集成初始化时的定位

int a[10] = {
    [0] = 2, [2] = 3, 6, //指定的位置赋值,第0个赋值2; 第二个赋值3,第二个后面第三个赋值为6
};
  • 只有C99才能用
  • 用[n]在初始化数据中给出定位
  • 没有定位的数据接在前面的位置后面
  • 其他位置的值补零; 每个位置只能存放一个值
  • 也可以不给出数组大小,让编译器算
  • 特别适合初始数据稀疏的数组
#include <stdio.h>
int main()
{
	int i; 
	int a[10] = {[0] = 2, [2] = 3, 6}; 
	{ 
		for(i=0; i<10; i++){
			printf("%d\t", a[i]);
		} //遍历数组,输出数组 
	} 
	return 0; 
}

上述代码仅支持C99

如果不支持就编译不了:sorry, unimplemented: non-trivial designated initializers not supported

数组的大小

  • sizeof给出整个数组所占据的内容的大小,单位是字节(bytes)

  • sizeof(a)/sizeof(a[0]) 可算出数组长度

  • sizeof(a[0])给出数组中单个元素的大小: int类型4字节长,于是相除就得到了数组的单元个数

  • #include <stdio.h>
    int main()
    {
    	int a[] = {1, 3, 4, 4, 5, 6, 3, 2};  //类似集合形式,但元素可以重复 
    
    	printf("a[]数组单个元素占据:%d\n字节", sizeof(a[0]));
    	printf("a[]数组里元素所占据:%d\n字节", sizeof(a)); 
    	int n = sizeof(a)/sizeof(a[0]);
    	printf("a[]数组里元素的个数:%d\n", n); 
    	int i;
    	for(i=0; i<n; i++){
    		printf("%d ", a[i]);
    	} //遍历数组, 输出数组 
    	return 0; 
    }	
    
  • 输出:

  • a[]数组单个元素占据:4
    字节a[]数组里元素所占据:32
    字节a[]数组里元素的个数:8
    1 3 4 4 5 6 3 2
    
  • 这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码

#include <stdio.h>
int search(int key, int a[], int length)
{
	int ret = -1;
	int i;
	for (i=0; i<length; i++){
		if(a[i] == key){
			ret = i;  //返回该查找数所在数组的位置 
			break; //查找到就终止查询, 节约时间 
		}
	}
	return ret; 
}
int main()
{
	int i;
	int a[] = {1, 3, 4, 4, 5, 6, 3, 2};  //类似集合形式,但元素可以重复 
	int x;
	int loc;
	printf("请输入你要查找的数字:");
	scanf("%d",&x);
	loc = search(x, a, sizeof(a)/sizeof(a[0]) ); //将函数search中返回的值赋给loc 
	if (loc != -1){
		printf("%d在第%d个位置上\n", x, loc); 
	}else{
		printf("%d不存在\n", x); 
	} 
		
	return 0; 
}

数组的赋值

  • 数组变量本身不能被赋值

  • 请添加图片描述

  • 要把一个数组的所有元素交给另一个数组,必须采用遍历(没有其他办法)

for (i=0; i<length; i++){
    b[i] = a[i];
}

注: 定义一个数组和数组赋值的区别;一般定义数组是有类型的;而数组中的某个位置赋值是没有写上类型标注:如

int n[10]; //定义一个长度为10的数组。

n[10] = 2;//数组的第11个位置赋值为2。

int n[10] = {1, 2, 3, 4, 5}//一个长度为10的数组集成初始化数值。

遍历数组

  • 通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
  • 常见错误是:
    • 循环结束条件是<=数组长度 (一般都是<数组长度);
    • 离开循环后,继续用i的值来做数组元素的下标!(离开循环后一般i的值为数组长度而非下标. 下标=数组长度-1.)

数组作为函数参数时,往往必须用另一个参数来传入数组的大小

  • 数组作为函数的参数时
    • 不能在[]中给出数组的大小
    • 不能在定义的函数里再利用sizeof来计算数组的元素个数!
int search(int key, int a[], int length)//重新传入数组的长度值length
{
	int ret = -1;
	int i;
	for (i=0; i<length; i++){
		if(a[i] == key){
			ret = i;  //返回该查找数所在数组的位置 
			break; //查找到就终止查询, 节约时间 
		}
	}
	return ret; 
}

数组的例子: 素数

判断素数

三种方法:

  1. 从2到x-1测试是否可以整除
#include <stdio.h>
int	isPrime(int x);
int main()
{
	int x;
	scanf("%d", &x);
	if (isPrime(x)){
		printf("%d是素数\n",x);
	}else{
		printf("%d不是素数\n", x);
	}
	return 0;
 } 
int isPrime(int x)
{
	int ret = 1;
	int i;
	if(x==1) ret = 0;
	for (i=2; i<x; i++){
		if(x%i==0){
			ret = 0;
			break;
		}			
	}
	return 0;
}//从2到x-1测试是否可以整除
  • 对于n要循环n-1遍;当n很大时就是n遍
  1. 去掉偶数后,从3到x-1,每次加2
#include <stdio.h>

int	isPrime(int x);
int main()
{
	int x;
	scanf("%d", &x);
	if (isPrime(x)){
		printf("%d是素数\n",x);
	}else{
		printf("%d不是素数\n", x);
	}
	return 0;
 } 

int isPrime(int x)
{
	int ret = 1;
	int i;
	if(x==1||(x%2==0&&x!=2)){
		ret = 0;//判断是否是偶数(除了2以外的偶数),如果是则不是素数
	}else{
		for(i=3;i<x;i+=2){
			if(x%i==0){    //如果不是偶数则从3开始,奇数进行取余,如果能整除,则不是素数。
				ret = 0;
				break;
			}
		}	
	}
	return ret;
}
  • 如果x是偶数,除2外,其他偶数都不是素数; 否则要循环(n-3)/2+1遍当n很大时就是n/2
  1. 无须到x-1,到sqrt(x)就够了 sqrt()平方根函数
#include <stdio.h>
#include <math.h>
int	isPrime(int x);
int main()
{
	int x;
	scanf("%d", &x);
	if (isPrime(x)){
		printf("%d是素数\n",x);
	}else{
		printf("%d不是素数\n", x);
	}
	return 0;
 } 

int isPrime(int x)
{
	int ret = 1;
	int i;
	if(x==1||(x%2==0&&x!=2)){
		ret = 0;
	}else{
		for(i=3; i< sqrt(x); i+=2){
			if (x%i==0){
				ret = 0;
				break;
			}
		}
	}
	return ret;		
}
  • 只需要循环sqrt(x)遍

拓展:sqrt

请添加图片描述

判断是否能被已知的且<x的素数整除
#include <stdio.h>

int isPrime(int x, int kownPrimesp[], int numberOfKownPrimes);
int main()
{
	const int number = 10;//常量10的数组大小 
	int prime[number] = {2};//集成初始化数组 
	int count = 1;
	int i = 3;
	
	{
		int i;
		printf("\t\t");
		for(i=0;i<number; i++){
			printf("%d\t",i);
		}
		printf("\n");
	} 
	
	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++; 
	}
		 
	for (i=0; i<number; i++){
		printf("%d",prime[i]);
		if((i+1)%5) printf("\t"); //隔5个进行制表 
		else printf("\n");
		
	}
	return 0;	
}

int isPrime(int x, int kownPrimes[], int numberOfKownPrimes)
{
	int ret = 1;
	int i;
	for (i=0; i<numberOfKownPrimes; i++){
		if (x%kownPrimes[i] == 0){
			ret = 0;
			break;
		}
	}
}

输出:

                0       1       2       3       4       5       6       7       8       9
i=3     cnt=2   2       3       0       0       0       0       0       0       0       0
i=4     cnt=2   2       3       0       0       0       0       0       0       0       0
i=5     cnt=3   2       3       5       0       0       0       0       0       0       0
i=6     cnt=3   2       3       5       0       0       0       0       0       0       0
i=7     cnt=4   2       3       5       7       0       0       0       0       0       0
i=8     cnt=4   2       3       5       7       0       0       0       0       0       0
i=9     cnt=4   2       3       5       7       0       0       0       0       0       0
i=10    cnt=4   2       3       5       7       0       0       0       0       0       0
i=11    cnt=5   2       3       5       7       11      0       0       0       0       0
i=12    cnt=5   2       3       5       7       11      0       0       0       0       0
i=13    cnt=6   2       3       5       7       11      13      0       0       0       0
i=14    cnt=6   2       3       5       7       11      13      0       0       0       0
i=15    cnt=6   2       3       5       7       11      13      0       0       0       0
i=16    cnt=6   2       3       5       7       11      13      0       0       0       0
i=17    cnt=7   2       3       5       7       11      13      17      0       0       0
i=18    cnt=7   2       3       5       7       11      13      17      0       0       0
i=19    cnt=8   2       3       5       7       11      13      17      19      0       0
i=20    cnt=8   2       3       5       7       11      13      17      19      0       0
i=21    cnt=8   2       3       5       7       11      13      17      19      0       0
i=22    cnt=8   2       3       5       7       11      13      17      19      0       0
i=23    cnt=9   2       3       5       7       11      13      17      19      23      0
i=24    cnt=9   2       3       5       7       11      13      17      19      23      0
i=25    cnt=9   2       3       5       7       11      13      17      19      23      0
i=26    cnt=9   2       3       5       7       11      13      17      19      23      0
i=27    cnt=9   2       3       5       7       11      13      17      19      23      0
i=28    cnt=9   2       3       5       7       11      13      17      19      23      0
i=29    cnt=10  2       3       5       7       11      13      17      19      23      29
2       3       5       7       11
13      17      19      23      29

用数组的做法

构造素数表
  • 欲构造n以内(不含)的素数表

    1. 开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
    2. 令x=2
    3. 如果x是素数,则对于(i=2; x * i<n; i++)令prime[i * x]=0 //素数的倍数肯定不是素数
    4. 令x++,如果x<n,重复3,否则结束
  • 欲构造n以内的素数表

    1. 令x为2
    2. 将2x、3x、4x直⾄至ax<n的数标记为非素数
    3. 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕

请添加图片描述

  • 算法不一定和人的思考方式相同

二维数组

  • int a[3][5];
  • 通常理解为a是一个3行5列的矩阵
    a[0][0] a[0][1] a[0][2] a[0][3] a[0][4] a[1][0] a[1][1] a[1][2] a[1][3] a[1][4] a[2][0] a[2][1] a[2][2] a[2][3] a[2][4]

二维数组的遍历

请添加图片描述

  • 需要两层for循环,外面一层为行,里面一层为列;才能进行二维数组的遍历
  • 比如: 先进入第一行i=1,然后进行列j= 1, 2, 3, 4进行遍历,遍历完列后,再换一行i = 2,再遍历列j= 1, 2, 3, 4 ; 以此类推…
  • a[i][j]是一个int整数
    • 表示第i行第j列上的单元
  • a[i, j]是什么? 是一个表达式,而不是一个表达二维数组的方式; 逗号是一种运算符. a[i, j] == a[j]; 最终等于后面的值

二维数组的初始化

请添加图片描述

  • 列数是必须给出的, 行数可以由编译器来数 即int a[][5]
  • 每行一个{},逗号分隔; 可以没有大括号{}隔开两个数组;它会自己按照顺序自己填满
  • 最后的逗号可以存在,有古老的传统,
  • 如果省略{}里面的某个数,表示补零 ;与一维数组一样
  • 也可以用定位(*C99 ONLY) ;与一维数组一样

tic-tac-toe游戏

  • 读入一个3X3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示为O
  • 程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或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++){
		numOfO = numOfX = 0;
		for(j=0; j<size; j++){
			if(board[i][j]==1){
				numOfX ++;
			}else if(board[i][j]==0){
				numOfO ++;
			}
		}
		if (numOfO==size){
			result = 0;
		}else if(numOfX==size){
			result = 1;
		}
检查列
if(result == -1){
		for (j=0; i<size&&result==-1; j++){
			numOfO = numOfX = 0;
			for(i=0; i<size; i++){
				if(board[i][j]==1){
					numOfX ++;
				}else if(board[i][j]==0){ 
					numOfO ++;
				}
			}
			if (numOfO==size){
				result = 0;
			}else if(numOfX==size){
				result = 1;
			}
		} 
	}
检查对角线
	if(result == -1){
	
		//检查对角线
		numOfO=numOfX=0;
		for (i=0; i<size; i++){
		 	if (board[i][i]==1){
		 		numOfX ++;
			}else if(board[i][j]==0){ 
				numOfO ++;	
			}
		}
		 if (numOfO==size){
		 	result = 0;
		 }else if(numOfX==size){
		 	result = 1;
		 }
 	}
	

	if(result == -1){
		numOfO = numOfX = 0;
		for(i=0; i<size; i++){
			if(board[i][size-i-1]==1){
				numOfX ++;
			}else if(board[i][j]==0){ 
				numOfO ++;
			}
		}
		if (numOfO==size){
			result = 0;
		}else if(numOfX==size){
			result = 1;
		}	
  • 总代码:
#include <stdio.h>
int main() 
{
	const int size = 3; //固定行数和列数都为3 
	int board[size][size];
	int i, j;
	int numOfX;
	int numOfO;
	int result = -1;   //-1:没人赢; 1:X赢;  0:O赢
	//读入矩阵
	printf("请按列依次输入信息,X输入1; O输入0;其他输入-1,以空格隔开:"); 
	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++){
		numOfO = numOfX = 0;
		for(j=0; j<size; j++){
			if(board[i][j]==1){
				numOfX ++;
			}else if(board[i][j]==0){
				numOfO ++;
			}
		}
		if (numOfO==size){
			result = 0;
		}else if(numOfX==size){
			result = 1;
		}
	} 

	//检查列
	if(result == -1){
		for (j=0; i<size&&result==-1; j++){
			numOfO = numOfX = 0;
			for(i=0; i<size; i++){
				if(board[i][j]==1){
					numOfX ++;
				}else if(board[i][j]==0){ 
					numOfO ++;
				}
			}
			if (numOfO==size){
				result = 0;
			}else if(numOfX==size){
				result = 1;
			}
		} 
	}


	if(result == -1){
	
		//检查对角线
		numOfO=numOfX=0;
		for (i=0; i<size; i++){
		 	if (board[i][i]==1){
		 		numOfX ++;
			}else if(board[i][j]==0){ 
				numOfO ++;	
			}
		}
		 if (numOfO==size){
		 	result = 0;
		 }else if(numOfX==size){
		 	result = 1;
		 }
 	}
	

	if(result == -1){
		numOfO = numOfX = 0;
		for(i=0; i<size; i++){
			if(board[i][size-i-1]==1){
				numOfX ++;
			}else if(board[i][j]==0){ 
				numOfO ++;
			}
		}
		if (numOfO==size){
			result = 0;
		}else if(numOfX==size){
			result = 1;
		}
	}
	
	if(result==1){
		printf("X方获胜\n"); 
	}else if(result==0){
		printf("O方获胜\n");
	}else{
		printf("平局\n");
	} 
	return 0;
	
		
 }

输出

请按列依次输入信息,X输入1; O输入0;其他输入-1,以空格隔开:1 1 1 0 1 -1 -1 0 0
X方获胜

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值