C语言学习之路7

C语言学习之路7

本节对C语言中数组部分进行学习记录

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

#include<stdio.h>
int main(){
    int x;
    double sum=0;
    int cnt=0;
    scanf("%d",&x);
    while(x!=-1){
        sum+=x;
        cnt++;
        scanf("%d",&x);
    }
    if(cnt>0){
        printf("%f\n",sum/cnt);
    }
}

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

·如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数?

·如何记录很多数?
·num1,num2,num3…?

数组
·int number[100]
·scanf("%d",&number[i]);

#include<stdio.h>
int main(){
    int x;
    double sum=0;
    int cnt=0;
    int number[100];
    scanf("%d",&x);
    while(x!=-1){
        number[cnt]=x;
        sum+=number[cnt];
        cnt++;
        scanf("%d",&x);
    }
    if(cnt>0){
        int i;
        double average=sum/cnt;
        for(i=0;i<cnt;i++){
            if(number[i]>average){
                printf("%d\n",number[i]);
            }
        }
    }
} 

·这个程序是危险的,因为输入的数据可能超过100个。
在这里插入图片描述
定义数组
·<类型>变量名称[元素数量],如:int grades[100]; double weight[20];
·元素数量必须是整数
·C99之前:元素数量必须是编译时刻确定的字面量

数组
·是一种容器(放东西的东西),特点是:
(1)其中所有的元素具有相同的数据类型。
(2)一旦创建,不能改变大小。
(3)*(数组中的元素在内存中是连续依次排列的)

int a[10]
·一个int的数组
·10个单元:a[0],a[1],…,a[9]
在这里插入图片描述
·每个单元就是一个int类型的变量
·可以出现在赋值的左边或右边:a[2]=a[1]+6;
·*在赋值左边的叫做左值

数组的单元
·数组的每个单元就是数组类型的一个变量
·使用数组时放在[]中的数字叫做下标或索引,下标从0开始计数:grades[0]、grades[5]、grades[99]

有效的下标范围
·编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
·一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃。segmentation fault
·但是也可能运气好,没造成严重的后果
·所以这是程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1]

长度为0的数组
·int a[0]
·可以存在,但是无用

·写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束

#include<stdio.h>
int main(){
    const int number=10;
    int x;
    int count[number];
    for(int i=0;i<number;i++){//初始化 
        count[i]=0;
    }
    scanf("%d",&x);
    while(x!=-1){
        if(x>=0&&x<=9){
            count[x]++;
        }
        scanf("%d",&x);
    }
    for(int i=0;i<number;i++){
        printf("%d:%d\n",i,count[i]);
    }
}

在这里插入图片描述
·在一组给定的数据中,如何找出某个数据是否存在?

#include<stdio.h>
/*
找出key在数组a中的位置
@param key 要寻找的数字 
@param a 要寻找的数组
@param length 数组a的长度
@return 如果找到,返回其在a中的位置,如果找不到则返回-1 
*/ 
int search(int key,int a[],int length);
int main(){
    int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
    int x;
    int loc;
    printf("请输入一个数字:");
    scanf("%d",&x);
    loc=search(x,a,sizeof(a)/sizeof(a[0]));
    if(loc!=-1){
        printf("%d在第%d个位置上\n",x,loc);
    }else{
        printf("%d不存在\n");
    }
}
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;
}

说明:
(1)数组的集成初始化
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
·直接用大括号给出数组的所有元素的初始值
·不需要给出数组的大小,编译器替你数数

(2)集成初始化时的定位
在这里插入图片描述等价于a[0]=2,a[2]=3,a[3]=6,其余为0

·用[n]在初始化数据中给出定位
·没有定位的数据接在前面的位置后面
·其他位置的值补零
·也可以不给出数组大小,让编译器算
·特别适合初始数据稀疏的数组

(3)数组的大小
·sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a)/sizeof(a[0])
·sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
·这样的代码,好处就是一旦修改数组中初始的数据,不需要修改遍历的代码

(4)数组的赋值
int a[]={2,4,6,7,1,3,5,9,11,13,23,14,32};
int b[]=a;//×不能做这样的赋值

·数组变量本身不能被赋值
·要把一个数组的所有元素交给另一个数组,必须采用遍历

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

(5)遍历数组
·通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
·常见错误是:循环结束条件是<=数组长度,或离开循环后,继续用i的值来做数组元素的下标

(6)数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
·数组作为函数的参数时:不能在[]中给出数组的大小,不能再利用sizeof来计算数组的元素个数

判断素数

#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); 
    } 
}
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 ret;
}

·对2到x-1测试是否可以整除
·对于n要循环n-1遍,当n很大时就是n遍

另外一种方案

int isPrime(int x){
    int ret=1;
    int i;
    if(x==1||(x%2==0&&x!=2))ret=0;
    for(i=3;i<x;i+=2){
        if(x%i==0){
            ret=0;
            break;
        }
    }
    return ret;
}

·去掉偶数后,从3到x-1,每次加2
·如果x是偶数,立刻
·否则要循环(n-3)/2+1遍,当n很大时就是n/2遍

但是无须走到x-1,到sqrt(x)就够了

int isPrime(int x){
    int ret=1;
    int i;
    if(x==1||(x%2==0&&x!=2))ret=0;
    for(i=3;i<sqrt(x);i+=2){
        if(x%i==0){
            ret=0;
            break;
        }
    }
    return ret;
}

·只需要循环sqrt(x)遍

还有更好的方法:判断是否能被已知的且<x的素数整除

#include<stdio.h>
int isPrime(int x,int knownPrimes[],int numberOfKnownPrimes){
    int ret=1;
    int i;
    for(i=0;i<numberOfKnownPrimes;i++){
        if(x%knownPrimes[i]==0){
            ret=0;
            break;
        }
    }
    return ret;
}
int main(){
    const int number=100;
    int prime[number]={2};//素数表,前100个素数 
    int count=1;
    int i=3;
    while(count<number){
        if(isPrime(i,prime,count)){
            prime[count++]=i;//count是下标,如果i是素数就把他加入到素数表中,然后移到下一个位置 
         }
         i++;
    }
    for(i=0;i<number;i++){//输出素数表 
        printf("%d",prime[i]);
       if((i+1)%5)printf("\t");
       else printf("\n");
    }
}

构造素数表
·预构造n以内的素数表
(1)令x为2
(2)将2x、3x、4x直至ax<n的数标记为非素数
(3)令x为下一个没有被标记为非素数的数,重复(2);直到所有的数都已经尝试完毕
(4)令x++,如果x<n,重复(3),否则结束

#include<stdio.h>
int main(){
    const int maxNumber=25;
    int isPrime[maxNumber];
    int i;
    int x;
    for(i=0;i<maxNumber;i++){
        isPrime[i]=1;
    } 
    for(x=2;x<maxNumber;x++){
        if(isPrime[x]){
            for(i=2;i*x<maxNumber;i++){
                isPrime[i*x]=0;//从2开始,它所有的倍数isPrime都标记为0 
            }
        }
    }
    for(i=2;i<maxNumber;i++){
        if(isPrime[i]){//剩下isPrime为1的数就是素数了 
            printf("%d\t",i);
        }
    }
    printf("\n");
}

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

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

二维数组的遍历

 for(i=0;i<3;i++){
     for(j=0;j<5;j++){
         a[i][j]==i*j;
     }
 } 

·a[i][j]是一个int
·表示第i行第j列上的单元

二维数组的初始化

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

·列数是必须给出的,行数可以由编译器来数
·每行一个{},逗号分隔
·最后的逗号可以存在,有古老的传统
·如果省略,表示补零
·也可以用定位(*C99 ONLY)

tic-tac-toe游戏
·读入一个3X3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示为O
·程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或O,或输出无人获胜

#include<stdio.h>
int main(){
    const int size=3;
    int board[size][size];
    int i,j;
    int num0fx;
    int num0fo;
    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;
        }else if(num0fx==size){
            result=1;
        }
    } 
    //检查列
    if(result==-1){
        for(j=0;j<size&&result==-1;j++){
            num0fo=num0fx=0;
            for(i=0;i<size;i++){
                if(board[i][j]==1){
                    num0fx++;
                }else{
                    num0fo++;
                }
            }
            if(num0fo==size){
                result=0;
            }else if(num0fx==size){
                result=1;
            }
        }
    } 
    //检查对角线 正对角线 
    num0fo=num0fx=0;
    for(i=0;i<size;i++){
        if(board[i][i]==1){
            num0fx++;
        }else{
            num0fo++;
        }
    }
    if(num0fo==size){
        result=0;
    }else if(num0fx==size){
        result=1;
    }
    //反对角线
    num0fo=num0fx=0;
    for(i=0;i<size;i++){
        if(board[i][size-i-1]==1){
            num0fx++;
        }else{
            num0fo++;
        }
    }
    printf("%d",result);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值