Bootstrap

十天速通C语言基础(day04)

写在前面:

        昨天有事休息了一天,今天给大家补上,今天着重说一下循环结构、循环控制以及数组。话不多说开启今日的C语言基础。

目录

1.循环结构

for语句

while语句

do_while语句

2. 循环控制

break语句

3.数组

 3.1一维数组

 3.2数组名

 3.3一维数组的引用

3.4数组的初始化 

3.4.1全部初始化

3.4.2部分初始化

3.4.3未初始化

3.5数组遍历

3.5.1声明数组

 3.5.2数组遍历

4.内存分配

5.数组大小


1.循环结构

for语句

  一般形式

for(expression1;expression2;expression3)
{statements}

 执行过程

  • 先求解表达式1;
  • 求解表达式2,若为真,则执行循环体,然后执行步骤3;若为假,则执行步骤5;
  • 求解表达式3;
  • 转回执行步骤2;
  • 执行for下面的语句

for语句构成循环

如:

for(i = 1;i <= 100;i++)

{

        sun = sum + i;

}

 for语句构成循环

  • 表达式1可省略,单循环之前应给循环变量赋值
  • 表达式2可省略,但将陷入死循环
  • 表达式3可省略,但在循环体中增加使循环变量值改变的语句

首先看一下下面这个程序

// 外层循环,控制变量i从0到5
for(int i=0; i<6; i++)
{
    // 内层循环,控制变量j从0到2
    for(int j=0; j<3; j++)
    {
        // 打印当前的i和j的值
        printf("i:%d j:%d
", i, j);
    }
}

        这是一个C语言程序,它使用了两个嵌套的for循环来打印出一系列的i和j的值。外层循环控制变量i从0到5,内层循环控制变量j从0到2。每次内层循环结束后,外层循环的i值会增加1,然后内层循环重新开始。在内层循环中,程序使用printf函数打印出当前的i和j的值。

 外层循环执行一次,内层循环执行一轮。

可以根据这个来输出一个九九乘法表:

思路:

        1.使用两层嵌套循环来实现九九乘法表的输出。

        2.外层循环控制行数,从1到9,每次循环递增1。

        3.内层循环控制列数,从1到当前行数i,每次循环递增1。

        4.在内层循环中,使用printf函数打印乘法表的每个元素,格式为:j * i = 结果。

        5.每行结束后,使用printf函数输出一个换行符,以便在下一行开始新的一行。

#include <stdio.h>

int main() {
    // 外层循环控制行数,从1到9
    for (int i = 1; i <= 9; i++) {
        // 内层循环控制列数,从1到i
        for (int j = 1; j <= i; j++) {
            // 打印乘法表的每个元素,格式为:j * i = 结果
            printf("%d*%d=%d\t", j, i, j * i);
        }
        // 每行结束后换行
        printf("
");
    }
    return 0;
}

 练习

/*
以下程序执行后的输出结果是()
A.自然数1~9的累加和
B.自然数1~10的累加和
C.自然数1~9中的奇数和
D.自然数1~10中的偶数和
*/

int i,s = 0;
for(i = 1;i < 10;i += 2)
{
    s += i + 1;
}
printf("%d\n",s);

这段代码是一个C语言程序,用于计算一个累加和。让我们逐步分析代码的逻辑:

  1. 定义变量 i 和 s,其中 s 初始化为0,用于存储累加和。
  2. 使用 for 循环,循环变量 i 从1开始,到9结束(因为 i < 10),每次循环 i 增加2(i += 2)。这意味着循环只会遍历奇数(1, 3, 5, 7, 9)。
  3. 在循环体中,执行 s += i + 1;,这实际上是将 i + 1 的值加到 s 上。
  4. 最后,使用 printf 函数打印变量 s 的值。

现在,我们可以计算累加和的具体值:

  • 当 i = 1 时,s += 1 + 1,即 s = 0 + 2
  • 当 i = 3 时,s += 3 + 1,即 s = 2 + 4
  • 当 i = 5 时,s += 5 + 1,即 s = 6 + 6
  • 当 i = 7 时,s += 7 + 1,即 s = 12 + 8
  • 当 i = 9 时,s += 9 + 1,即 s = 20 + 10

最终,s = 30

因此,程序的输出结果是30,这是自然数1到9中所有奇数加1后的和。所以正确答案是:D

下面再进行一个练习:

终端输入行数,实现以下功能:

输入5

*

**

***

****

*****

我们来分析一下:

首先,我们需要从终端获取用户输入的行数,它的格式可以理解为

行数 每一行*个数 嵌套

1 1

2 2

3 3

然后,我们需要使用嵌套循环来实现打印星号的功能。外层循环控制行数,内层循环控制每行的星号数量。并且在内层循环中,我们需要打印相应数量的星号,并在每行星号后换行。

代码如下:

#include <stdio.h>

int main() {
    int n, i, j;

    // 从终端获取用户输入的行数
    printf("请输入行数:");
    scanf("%d", &n);

    // 使用嵌套循环打印星号
    for (i = 1; i <= n; i++) { // 外层循环控制行数
        for (j = 1; j <= i; j++) { // 内层循环控制每行的星号数量
            printf("*");
        }
        printf("
"); // 每行星号后换行
    }

    return 0;
}
  1. 首先,程序包含了头文件stdio.h,这是因为我们需要使用标准输入输出函数(如printfscanf)。

  2. main()函数是程序的入口点。在C语言中,每个程序必须有一个名为main的函数作为程序执行的起点。

  3. main()函数内部,我们声明了三个整型变量n, i, j。其中n用于存储用户输入的行数,ij分别用作循环计数器。

  4. 接下来,程序通过printf函数提示用户输入行数,并使用scanf函数读取用户的输入并将其存储到变量n中。

  5. 然后,程序使用两个嵌套的for循环来实现打印星号的功能。外层循环控制行数,内层循环控制每行的星号数量。

  6. 在内层循环中,程序使用printf函数打印一个星号,然后继续下一次迭代。当内层循环结束时,即一行的星号打印完毕,程序使用printf(" ")换行,以便开始打印下一行。

  7. 最后,程序返回0,表示程序正常结束。

总结起来,这个程序的主要功能是根据用户输入的行数,打印出相应数量的星号,每行星号的数量逐行递增。

进阶以下:

 终端输入行数,实现以下功能:

输入5

*

**

***

****

*****

***** 

 **** 

  *** 

   ** 

    * 

分析一下:

其格式我们可以由上面的程序延伸一下

 行数        空格        *

    1            0           5

    2            1           4

    3            2           3

    4            3           2

    5            4           1

 解题思路:

        1.首先,我们需要从终端获取用户输入的行数。

        2.然后,我们使用两个嵌套的for循环来实现打印星号的功能。第一个循环用于打印上               半部分的星号图案,第二个循环用于打印下半部分的星号图案。

        3.在第一个循环中,外层循环控制行数,内层循环控制每行的星号数量。每次迭代时,             我们打印一个星号,并在每行星号后换行。

        4.在第二个循环中,外层循环同样控制行数,但这次是从n-1开始递减到1。内层循环则             控制每行的星号数量,从1开始递增到i。这样可以实现下半部分的对称性。

        5.最后,程序返回0,表示程序正常结束。

代码如下:

#include <stdio.h>

int main() {
    int n, i, j;

    // 从终端获取用户输入的行数
    printf("请输入行数:");
    scanf("%d", &n);

    // 打印上半部分星号图案
    for (i = 1; i <= n; i++) { // 外层循环控制行数
        for (j = 1; j <= i; j++) { // 内层循环控制每行的星号数量
            printf("*");
        }
        printf("
"); // 每行星号后换行
    }

    // 打印下半部分星号图案
    for (i = n - 1; i >= 1; i--) { // 外层循环控制行数,从n-1开始递减到1
        for (j = 1; j <= i; j++) { // 内层循环控制每行的星号数量,从1开始递增到i
            printf("*");
        }
        printf("
"); // 每行星号后换行
    }

    return 0;
}

while语句

while语句构成循环

基本形式

while(表达式)

{

        statatments;

}

例:求1到10的和

1.先获取10个数

2.把10个数累加

int i=1,sum=0;

while(i<=10)

{

sum+=i;

i++;

}

printf("%d\n",sum);

do_while语句

do_while语句构成循环

基本形式

do

{

        statatments;

}while(表达式);

注:while和do_while的区别

        while先判断在执行

        do_while先执行一次,在判断

死循环:while(1);for(;;);

2. 循环控制

break语句

用于从循环体内跳出循环体,即提前结束循环

  • break只能用在循环语句和switch语句中

for(r = 1;r <= 10;r++)

{

        area = pi * r * r;

        if(area > 100) break;

        printf("%f",area);

}

素数

在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。

换句话说,只有两个正因数(1和自己)的自然数即为素数。比1打但不是素数的数成为合数。1和0既非素数也非合数。

例:

        对一个大于或等于3的正整数,判断它是不是一个素数

  • S1:输入整数n的值
  • S2:i=2
  • S3:n被i除,得余数r
  • S4:如果r=0,则打印n“不是素数”,算法结束;否则执行S5
  • S5:i+1→i
  • S6:如果i<=根号n,返回S3;否则打印n“是素数”;然后算法结束

程序如下:

#include <stdio.h>
#include <math.h>

int main()
{
    int n, i;      // 定义整数变量n和i
    double sqrt_n; // 定义浮点数变量sqrt_n,用于存储n的平方根

    printf("请输入一个大于或等于3的正整数:"); // 提示用户输入一个大于或等于3的正整数
    scanf("%d", &n);                           // 读取用户输入的整数并赋值给n

    if (n < 3)
    { // 如果输入的数字小于3,则输出错误信息并退出程序
        printf("输入的数字必须大于或等于3。\n");
        return 1;
    }

    i = 2;            // 初始化i为2,因为从2开始检查是否为素数
    sqrt_n = sqrt(n); // 计算n的平方根并赋值给sqrt_n

    while (i <= sqrt_n)
    { // 当i小于等于n的平方根时,执行循环
        if (n % i == 0)
        {                                // 如果n能被i整除,说明n不是素数
            printf("%d不是素数。\n", n); // 输出n不是素数的信息
            return 0;                    // 结束程序
        }
        i++; // i递增,继续检查下一个可能的因子
    }

    printf("%d是素数。\n", n); // 如果循环结束后没有找到任何因子,说明n是素数,输出n是素数的信息
    return 0;                  // 程序正常结束
}

这个程序的主要目的是判断一个大于或等于3的正整数是否为素数。以下是程序的思路:

  1. 首先,程序提示用户输入一个大于或等于3的正整数,并将其存储在变量n中。
  2. 如果输入的数字小于3,则输出错误信息并退出程序。
  3. 初始化变量i为2,因为从2开始检查是否为素数。同时计算n的平方根并存储在sqrt_n中。
  4. 进入循环,当i小于等于n的平方根时执行以下步骤:
    • 如果n能被i整除(即余数为0),说明n不是素数,输出n不是素数的信息并结束程序。
    • 如果n不能被i整除,将i递增1,继续检查下一个可能的因子。
  5. 如果循环结束后没有找到任何因子,说明n是素数,输出n是素数的信息。
  6. 程序正常结束。

通过以上思路,程序可以有效地判断一个给定的正整数是否为素数。

3.数组

  • 构造数据类型之一
  • 数组是具有一定顺序关系的若干个变量的集合,组成数组的各个变量成为数组的元素
  • 数组中各元素的数据类型要求相同,用数组名和下表确定。数组可以是一维的,也可以是多维的。
由一个或多个相同类型数据组成的集合

特点:

        数据类型相同

        内存连续

 3.1一维数组

一维数组的定义

  • 所谓一维数组是指只有一个下标的数组。它在计算机的内存中是连续存储的。
格式:

存储类型        数据类型        数组名[元素个数]

                        int                   arr[5];

                定义了一个int 类型的数组,可以存储5个int类型的数据

例: 只有在定义时,5表示元素个数,其他任何情况下5表示数组的第6个元素

 例:

int a[5]={5,6,7,8,9};

printf("%d %d %d\n",a[0],a[2],a[4]);

访问元素: 索引从0开始

a[0]----->5

a[1]----->6

a[2]----->7

a[3]----->8

a[4]----->9

注意: 

  • C语言对数组不做越界检查,使用时要注意
  • int a[5]; a[5]=10
  • 关于用变量定义数组维数
  • int i = 15; int a[i]

 3.2数组名

也表示数组的首地址,是地址常量,不能为左值(=左边)

 3.3一维数组的引用

  • 数组必须先定义,后使用
  • 只能逐个引用数组元素,不能一次引用整个数组
  • 数组元素表示形式:数组名[下标]
  • 其中:下标可以是常量或整形表达式
  • int a[10];

       printf(“%d”, a); (×) 

         

       for(j=0;j<10;j++)

           printf(“%d\t”, a[j]); (√)          

3.4数组的初始化 

  • 初始化方式:在定义数组时,为数组元素赋初值
  • int a[5]={1,2,3,4,5};
  • 说明
  • 数组不初始化,其元素值为随机数
  • 对static数组元素不赋初值,系统会自动赋以0值
  • 只给部分数组元素赋初值

 static int a[5];

等价于:a[0]=0;  a[1]=0; a[2]=0; a[3]=0; a[4]=0;


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

等价于:a[0]=6; a[1]=2;a[2]=3; a[3]=0; a[4]=0;

               int a[3]={6,2,3,5,1};     (×)


 int a[]={1,2,3,4,5,6};//编译系统根据初值个数确定数组维数


3.4.1全部初始化

int a[5]={5,6,7,8,9}

3.4.2部分初始化

int a[5]={8,9};

未初始化部分值为0

3.4.3未初始化

int a[5];

值为随机值

定义数组元素值都为0:

int a[40]={0};

int a[40]={ };

3.5数组遍历

         C语言数组遍历是指通过循环结构逐个访问数组中的元素。在C语言中,数组是一种数据结构,用于存储相同类型的多个元素。数组的遍历可以通过下标来访问每个元素。

3.5.1声明数组

在C语言中,可以使用以下语法声明一个数组:

dataType arrayName[arraySize];
其中,dataType是数组元素的类型,arrayName是数组的名称,arraySize是数组的大小(即元素个数)。

 3.5.2数组遍历

        使用循环结构遍历数组中的每个元素。常见的遍历方式有for循环和while循环。以下是两种遍历方法的示例:

  • for循环遍历数组
for (int i = 0; i < size; i++) 
{
    printf("%d ", numbers[i]);
}
  • while循环遍历数组
int i = 0;
while (i < size) 
{
    printf("%d ", numbers[i]);
    i++;
}

4.内存分配

  •         在C语言中,内存分配是指在程序运行时为变量、数组、结构体等数据对象分配内存空间。C语言提供了多种内存分配方式,包括静态内存分配、栈上分配和堆上分配。

        1.静态内存分配:在编译时进行,用于全局变量、静态变量和常量。这些变量的内存空间在程序运行期间保持固定大小,并且在程序的整个生命周期内都存在。例如:

int globalVar; // 全局变量,静态内存分配
static int staticVar; // 静态变量,静态内存分配
const int constantVar = 10; // 常量,静态内存分配

        2.栈上分配:在函数调用时进行,用于局部变量、函数参数和返回地址。栈上分配的内存空间在函数调用结束后自动释放。例如:

void exampleFunction() 
{
    int localVar; // 局部变量,栈上分配
    // ...函数体...
}

        3.堆上分配:在程序运行时进行,用于动态分配的内存空间。堆上分配的内存空间需要手动管理,包括申请、使用和释放。C语言提供了以下几种堆上内存分配的函数:

  • malloc:用于分配指定字节数的内存空间,并返回一个指向该内存空间的指针。如果分配失败,返回NULL。
int *ptr = (int*) malloc(sizeof(int) * arraySize); // 分配整数数组的内存空间
  • calloc:与malloc类似,但还会将分配的内存空间初始化为0。
int *ptr = (int*) calloc(arraySize, sizeof(int)); // 分配并初始化整数数组的内存空间
  • realloc:用于调整已分配内存空间的大小。可以增加或减少内存空间,并可能改变内存空间的位置。
ptr = (int*) realloc(ptr, sizeof(int) * newArraySize); // 调整整数数组的内存空间大小
  • free:用于释放已分配的内存空间,将其归还给操作系统。
free(ptr); // 释放整数数组的内存空间

在使用堆上分配的内存空间时,需要注意以下几点:

  • 确保正确计算所需的字节数,以避免分配过小或过大的内存空间。
  • 在使用分配的内存空间之前,确保检查指针是否为NULL,以避免空指针错误。
  • 及时释放不再使用的内存空间,以避免内存泄漏。

总之,C语言中的内存分配是一个重要的概念,需要根据具体需求选择合适的内存分配方式,并合理管理内存资源。

5.数组大小

在C语言中,数组的大小是指数组所能容纳的元素个数。数组的大小由声明时指定的元素个数决定,并且一旦声明后,其大小就固定不变。

要确定一个数组的大小,可以使用以下方法:

1.使用sizeof运算符:sizeof运算符可以返回一个对象或类型所占用的字节数。对于数组来说,它返回整个数组所占用的字节数。通过将数组的总字节数除以单个元素的字节数,可以得到数组的大小。例如:

int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]); // 计算数组大小
printf("The size of the array is: %d\n", size);

2.直接查看声明时的数组大小:如果在声明数组时指定了数组的大小,可以直接从声明语句中获取数组的大小。例如:

int numbers[5]; // 声明了一个大小为5的整数数组
int size = sizeof(numbers) / sizeof(numbers[0]); // 计算数组大小
printf("The size of the array is: %d\n", size);

需要注意的是,数组的大小是固定的,不能在运行时改变。如果需要动态调整数组的大小,可以考虑使用动态内存分配函数(如mallocrealloc)来创建和重新分配内存空间。

留一下提供大家练习的题目,详情解释会放在Day05

1.输入任意两个数,输出两数之间(包括这两个数)偶数之和。

思路:将输入的两个数a,b中小的数a,依次加1,加到b的值,每次循环判断这个数a是否为偶数,是则累加到sum中

2.循环输入一个5位数,判断它是不是回文数。当输入0时循环结束。即12321是回文数,个位与万位相同,十位与千位相同。

3.一同学上课违纪看视频,罚他表演才艺,现场抽取10名学生现场打分赋值在数组中,为了 公平起见,去掉其中最高分和最低分,求最终平均值。

;