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;
}
数组的例子: 素数
判断素数
三种方法:
- 从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遍
- 去掉偶数后,从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
遍
- 无须到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以内(不含)的素数表
- 开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
- 令x=2
- 如果x是素数,则对于(i=2; x * i<n; i++)令prime[i * x]=0 //素数的倍数肯定不是素数
- 令x++,如果x<n,重复3,否则结束
-
欲构造n以内的素数表
- 令x为2
- 将2x、3x、4x直⾄至ax<n的数标记为非素数
- 令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方获胜