C语言_7 数组

简介: 是翁恺老师的132p 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的所有倍数……)

  1. 先开辟数组prime[n],初始化其所有元素为1,prime[x]=1表示x是素数,prime[x]=0表示x不是素数
  2. 令x=2
  3. 如果x是素数,则for(int i=2;x*i<n;i++)prime[i*x]=0
  4. 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]两种情况。

目录
相关文章
|
1月前
|
存储 C语言 C++
【C语言数组】
【C语言数组】
|
5天前
|
存储 编译器 C语言
【C语言基础考研向】09 一维数组
数组是一种有序集合,用于存储相同类型的数据,便于统一操作与管理。例如,将衣柜底层划分为10个格子存放鞋子,便于快速定位。在C语言中,数组定义格式为 `类型说明符数组名[常量表达式];`,如 `int a[10];` 表示定义了一个包含10个整数的数组。数组初始化时可以直接赋值,也可以部分赋值,且数组长度必须固定。数组在内存中连续存储,访问时需注意下标范围,避免越界导致数据异常。数组作为参数传递时,传递的是首地址,修改会影响原数组。
|
5天前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
8天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
1月前
|
算法 C语言
C语言------数组
这篇文章是关于C语言数组的实训,包括一维数组、二维数组和字符数组的定义、赋值、输入、输出方法,并通过实例代码演示了数组的使用和一些基本算法,如冒泡排序。
C语言------数组
|
1月前
|
存储 编译器 程序员
七:《初学C语言》— 数组
【8月更文挑战第2天】本篇文章详细讲解了一维数组和二维数组的创建、使用和初始化及如何使用sizeof()计算数组中的元素个数。并附带了多个教学源码及代码练习
41 1
七:《初学C语言》— 数组
|
26天前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
45 2
|
1月前
|
存储 编译器 C语言
C语言——数组
C语言——数组
|
1月前
|
存储 C语言
C语言(数组)
C语言(数组)
31 6
|
25天前
|
存储 算法 搜索推荐
C语言中数组
C语言中数组
31 0