03 | 判断与循环:给你的程序加上处理逻辑

你好,我是胡光,咱们又见面了。不知道上一讲的内容,你自己做练习了么?你是否还觉得 C 语言枯燥无味呢?不管你有没有练习,我都还要啰嗦下,学习编程,就像是学骑自行车,你只看别人怎么骑,你只看自行车的原理,那永远也不可能学会骑自行车,对于你来说,唯一的捷径就是多练习,多思考。在上一讲小试牛刀之后,今天我将带你领略一下算法和逻辑层面的小惊喜。

今日任务

先来看看今天这 10 分钟我们要完成的任务。日期这个概念你肯定不陌生,生日对你我来说都很重要,如果你身边有2月29号过生日的小伙伴,恐怕最少4年,才能为他/她办一次生日宴。今天我们的这个任务,就和日期有关系。如果我给你一个由年月日组成的日期,再给你一个数字 X,你能否准确地让程序输出 X 天后的日期呢?

例如下面这个数据:

1989 11 20
20
1989 12 10

数据中给出了1989年11月20日这个日期,然后问你20天后的日期是多少,你的程序应该输出1989年12月10日。特别需要注意的是,在这个任务中,你需要考虑到闰年中2月份的特殊性,闰年的2月有29天。今天我们就学习,如何用计算机解决这类任务吧。

必知必会,查缺补漏

根据对任务的理解,我们可以分成两步来思考这个问题:

  • 第一步:我们来思考如何求解1天后的日期,在求解1天后日期的过程中,我们涉及到的核心问题就是判断日子是否跨月,月份是否跨年,即判断;
  • 第二步:是要让计算机重复 X 次1天后日期的计算过程,即重复循环做这件事。

要解决这两个难题,我们需要讲讲 C 语言中的一些基础知识,其中包括了程序中用于逻辑分支判断的分支结构,以及可以重复做大量事情的循环结构。听着这些专业词汇,你可能有点懵,别怕,等我下面讲了它们是什么意思,你就会感觉这些其实很简单。

1.给代码添加判断能力:“if…else”语法结构

我们一起来读下这句话:如果隔壁商店有酱油,就买酱油,否则就买点儿醋回来。可以看到,这句话用了“如果……就”的假设关系关联词,“如果”后面接的是假设条件,“就”后面接的是条件成立后的结果,“否则”接的是条件不成立后的结果。

现在我们想把计算机变成我们的小帮手,就必须要有一种语法,能够表达 “如果……就……否则……”的逻辑,这种语法,就是接下来我要介绍给你的 “if…else”语法结构。

在这里,我将简单介绍“if…else”语法结构,主要目的是让你看懂今天我们这个任务的代码,后续在课程逐步展开的过程中,我还会逐步的引入这个语法结构的一些其他知识点。

我们先来看 “if…else”最基本的语法结构:

if (条件表达式) 一条语句1;
else 一条语句2;

简单来说,if 和 else 都是关键字,代表分支逻辑中的 “如果”和 “否则”。if 后面跟着的括号里面,需要放一个条件表达式,条件表达式如果成立,程序会执行 “语句1”,否则就会执行 “语句2”。下面我来举个例子,你就明白了:

#include <stdio.h>

int main() {
    int a, b;
    scanf("%d%d", &a, &b);
    
    if (a == b) printf("a is equal to b!\n");
    else printf("a is not equal to b!\n");
    
    return 0; 
}

这段程序中,首先定义了两个变量 a 和 b,然后通过输入函数(scanf)给变量 a、b 赋值。之后就是重点部分了,根据我们上面所说的,如果 if 后面的条件表达式成立,那么就会输出 “a is equal to b!\n”, 否则就会输出 “a is not equal to b!\n”。

最后,我就再带你理解两个概念,一是条件表达式是什么,二是怎样理解 if 后面跟一条语句,所谓一条语句的概念范围是什么。

回到上面的程序中,你会看到程序中的 if 后面跟着一个括号,括号里面放着一个表达式,这个就是我们所谓的条件表达式,而这个括号,是必不可少的。我们发现,这个条件表达式用两个等号连接 a 和 b,作用是判断 a 和 b 里面存储的值是否相等。可千万别跟赋值表达式的一个等号弄混了。

说到这里,我要告诉你一个重要的事实,变量有变量对应的值,表达式也有表达式对应的值。那么例如上面代码中的条件表达式“a == b”所对应的值是什么呢?其实就是数字 1 或者 0,分别表示“条件成立”(a与b的值相等)和“不成立”(a与b的值不相等)。

延伸内容:
那么除了条件判等以外,还有哪些条件运算符呢?有判断不等于的“a != b”,大于的 “a > b”,小于的 “a < b”,大于等于的 “a >= b”,小于等于的 “a <= b”,逻辑非 “!(a > b)”,等价于 “a <= b”。同时多个条件表达式,还可以用逻辑 && 和 || 进行连接,这个后面我再跟你细说。

事实上,if 的括号里面,不仅可以放条件表达式,类似于 “a - b”这种的表达式,也是可以当做 if 的条件的。

当一般表达式作为条件的时候,if 是怎么执行的呢?很简单,记住:表达式的值,非 0 即为真。例如,下面两行代码,效果等价:

if (a != b) printf("a is not equal to b!\n");
if (a - b) printf("a is not equal to b!\n");

你会看到,第二行代码中,用 “a - b”代替 “a != b”,取得了同样的程序运行效果。因此,你只需要重点思考,表达式 “a - b” 什么时候结果非 0 即可,是不是当且仅当 “a != b”时,“a - b”的结果非 0,根据之前所说的非 0 即为真,那么 if 条件也就算是成立了。

最后,我们来讲一下怎么理解“if 后面跟一条语句”这个概念,其实指的是 if后面的条件成立时所执行的代码。这里,我们的重点是要理解一条语句都包含什么形式,大致可以分为如下几类。

第一种,空语句,就是什么都没有,单纯以一个分号结尾,例如下面这行代码,即使条件成立,也不会有任何实质上的操作。

if (a == 3) ;

第二种,单一语句,比空语句多了语句内容,以分号结尾,例如下面这行代码,当条件成立的时候,会输出 “hello geek!”。

if (a == 3) printf("hello geek!\n");

第三种,复合语句,被大括号包裹,中间是若干条语句,例如下面这段代码:

if (a == 3) {
    printf("hello geek1!\n");
    printf("hello geek2!\n");
    printf("hello geek3!\n");
}

当条件成立以后,程序会依次执行大括号里面的三条语句:

hello geek1!
hello geek2!
hello geek3!

第四种,结构语句,以 if,for,while 等开头的分支语句或循环语句,例如下面这段代码,首先会先判断 a==3,如果条件成立,才会执行下面第二条 if 分支语句,当第二条 if 分支语句的条件也成立的时候,才会输出 “hello geek!”。

if (a == 3) 
    if (b == 4) {
        printf("hello geek!\n");
    }

由此可以看到,if 后面所谓跟着的一条语句,还真是丰富多彩,你可以在后面跟上像上面代码中所写的 printf 函数调用的单一语句,也可以用一个大括号,里面放上若干条语句,亦或是 if 后面跟着另一个 if 也是可以的!你看这种组合能力,有没有点儿像乐高玩具?

至此,你就已经掌握了基础的将 “如果……就……否则……”这种逻辑结构转换成代码的能力了。你的计算机,终于有了“判断力”。

2.给程序添加重复执行功能:for和while语句

想想小的时候,你最讨厌什么事情?我最讨厌的就是被老师罚写汉字,错一个字,罚写100遍那种的,在我看来真的是在浪费时间。可当我学了程序以后,我发现,程序真的是特别擅长做这种重复的工作,而实现这种功能的语法结构就是 for 语句和 while 语句。

我们先来看语法结构较简单的 while 语句:

while (循环条件) 一条语句;

以 while 关键字开头,后面跟着循环条件,也就是一个条件表达式,然后是一条语句。while 循环,顾名思义,当循环条件成立时,就会执行一次后面的语句,之后就是再判断循环语句是否成立,如果成立就再执行,一直到循环条件不成立为止。

下面呢,我们就用最简单的形式,利用 while 循环,输出前100个正整数:

int i = 0;
while (i++ < 100) printf("%d\n", i);

这段代码里面,出现了一个你之前没有见过的语法,就是 i++,这也是表达式,这个表达式的值等于 i 之前的值,当这条表达式执行完以后,i 会变成 i + 1 的值。例如,起初i = 2,i++ 表达式的值就等于 2,可表达式执行以后,你要是输出 i 的值,这时 i 实际等于 3。

上面代码中,我们是用 i++ 表达式的值和 100 进行比较,表达式的值会遍历 0 到 99所有的值,由于 printf 在 i++之后输出 i 的值,所以实际上每次输出的都是 i + 1之后的值,也就是说 printf 会输出 1~100 所有值。具体的你可以参考下面的这个程序流程图。

另外,顺便再问你个问题,你还记得上一节课里,我们学到的\n和%d分别代表什么意思嘛?如果不记得,记得回去再复习下。

有了 while 循环语句的加持之后,是不是重复做某件事,变得很方便了呢?不急,下面我要给你介绍的是功能更为强大的 for 语句。还是先来看一下 for 语句的结构吧:

for (初始化①;循环条件②;循环后操作③) 一条语句④;

正如你看到的,我把 for 语句的四部分已经给你标出来了,for 语句会按照 ①②④③②④③…循环,直到某一次循环条件②不成立了为止。

你会发现,①这一部分只在循环开始时执行了一次,真正所谓的循环,是以循环条件②,一条语句④以及循环后操作③组成的。

如果要是用 for 循环输出 1~100 所有值,会显得代码更清晰一些:

for (int i = 1; i <= 100; i++) printf("%d\n", i); 

上面这段代码,就是用 for 循环实现了和之前 while 循环相同的功能。

看了 for 循环和 while 循环以后,你可能会问,实际中哪种循环用的比较多,我个人经验来说,for 循环用的比较多,因为 for 循环每一部分都非常明确,对于比较复杂的循环控制过程,for 循环写出来以后,一般都会比 while 循环可读性强。

为了让你感受到 for 循环真正的威力,写一段代码,让你感受一下:

for (int i = 1, k = 0; i <= 48; i++, k += 2) printf("%d\n", k);

上面这段程序中,我们用到了两个同步信息变量,i 和 k,i 从 1 到 48,保证循环了48次;代码中“k+=2”表示k每次增加 2 ,也就是说,在这个过程中,i 遍历了 1 到 48 这 48 个整型值,而 k 同步地遍历了从 0 开始的前 48 个偶数。这段代码的意思其实就是打印出从0开始后的共48个偶数,即0、2、4……92、94。

如果用while来实现这个目的,知道怎么写吗?你可以自己在计算机上试一下。

一起动手,搞事情

思考题:打印乘法表

使用循环和条件判断,打印一个格式优美的66乘法表
要求1:输出内容及样式参照下面给出的样例
要求2:每两列之间用 \t 字符进行分隔,行尾无多余 \t 字符

1*1=1
1*2=2	2*2=4
1*3=3	2*3=6	3*3=9
1*4=4	2*4=8	3*4=12	4*4=16
1*5=5	2*5=10	3*5=15	4*5=20	5*5=25
1*6=6	2*6=12	3*6=18	4*6=24	5*6=30	6*6=36

“日期计算器”程序完成

准备完了所有的基础技能后,就让我们来完成开始说的那个任务吧,我们来思考一下哈,首先我们需要有一个循环,循环每一次,让计算机帮我们计算一次下一天的日期。每次在计算下一天日期的过程中,先让日子加1,判断是否跨月,如果跨过了一个月份,就让日子从1开始,让月份加1,再判断是否跨年,如果跨年了,就让月份从1开始,年份加1。

如上的过程中,有一个关键问题需要你注意,就是2月份的月份天数的计算方法,咱们来简单回顾一下闰年的规则,年份满足以下其中一条即为闰年:

  • 能被4整除,但不能被 100 整除;
  • 能被 400 整除。

如果把闰年的规则翻译成逻辑判断,应该是下面这个样子:

if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ...

下面就让我们把思路过程转换成程序过程:

#include <stdio.h>

int main() {
    int y, m, d, X; // 定义存储 年月日 和 X 的变量
    scanf("%d%d%d", &y, &m, &d); // 读入年月日
    scanf("%d", &X); // 读入 X 值
    for (int i = 0; i < X; i++) { // 循环 X 次,每次向后推一天
        d += 1;
        switch (m) {
            case 1:
            case 3:
            case 5: { // 第一部分逻辑
                if (d > 31) d = 1, m += 1;
                if (m == 13) m = 1, y += 1;
            }; break;
            case 4:
            case 6: { // 第二部分逻辑 
                if (d > 30) d = 1, m += 1;
            } break;
            case 2: { // 第三部分逻辑
                if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) {
                    if (d > 29) d = 1, m += 1;
                } else if (d > 28) {
                    d = 1, m += 1;
                }
            } break;
        }
    }
    printf("%d %d %d\n", y, m, d);
    return 0;
} 

上面这段程序是个半成品,只处理了前6个月的情况,并且用到了switch…case 的分支结构,与 if 结构类似,都是用于做逻辑分支判断的。关于这部分的内容,给你留个小作业,自学一下 switch…case 分支结构,然后按照自己的理解,补全上述程序,使得上述程序可以处理一年中12个月的全部情况。

虽然这个程序中有一部分内容需要你进行自学,可你也不要担心,我还是会跟你详细解释上述程序设计的思路。读入部分的代码,相信你现在已经可以很好的掌握了,这一部分就不展开解释了。程序整体设计中,是用 for 循环包裹了 switch…case 结构,for 循环负责循环 X 次,每次在循环内部,都将对日子变量 d 进行加 1 操作,而在 switch…case 结构内部,主要是处理跨月和跨年的问题。

你会看到 switch…case 结构中,主要分成三部分逻辑,第一部分逻辑,主要处理天数为31天的月份,由于12月也是31天,所以当本月是12月,并且发生了跨月,变成了13月,说明是到了下一年的 1 月,需要将年份 +1,月份置为 1 月。第二部分逻辑,主要处理天数为30天的月份。第三部分逻辑,主要处理 2 月份的情况,在这里,程序中分成两种情况来讨论,闰年和非闰年,闰年的时候,判断日子是否超过29天,非闰年,判断日子是否超过28天。

我保证,在你尝试补全上述程序的过程中,你会发现,上述程序易于修改和补全,你要是能试着将上述程序修改成 if 分支结构,那就更好了。这样你将对上述程序结构的美,会感受的更深刻。

课程小结

最后呢,来总结一下今天所学的重点。今天呢,我们主要学习了两种程序流程控制结构,一种分支结构,主要以 if 语句为代表,另一种循环结构,以 for 循环和 while 循环为代表。如果说你只想记住几点的话,那么应该是以下几点:

  1. 熟练掌握分支和循环结构的执行顺序,这一点很重要。
  2. if 语句,首先判断条件表达式的真假,如果为真,则执行 if 里面的语句。
  3. for 循环,分成四部分,其中②④③部分,构成了一个循环,第①部分是用做初始化的。
  4. 所谓一条语句的概念,包括了空语句,单一语句,复合语句和结构语句。

以上这 4 点要牢记哦,尤其是其中的分支和循环结构的执行顺序,因为掌握和理解了程序的执行顺序,才是分析程序,理解程序的第一步。

好了,今天就到这里了,下期我将带你来做一个小总结,我将带你学习一个有趣的圆周率的计算方法,我是胡光,我们下期见。

精选留言

  • Paradise

    2020-01-13 14:11:24

    思路清晰,语言干练,通俗易懂,做了几年前端看老师的文章一样觉得很有收获!
    作者回复

    d(^_^o)

    2020-01-13 18:16:12

  • Noya

    2020-04-26 12:08:39

    #include <iostream>

    using namespace std;

    int year, month, day;
    int x;

    bool isLeap()
    {
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
    return true;
    return false;
    }

    int calcMonth()
    {
    switch (month)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
    return 31;
    case 2:
    {
    if (isLeap())
    return 29;
    else
    return 28;
    }
    default:
    return 30;
    }
    }

    void calc(int x)
    {
    int surplus = calcMonth() - day;
    if (surplus >= x)
    {
    printf("%d %d %d", year, month, day + x);
    return;
    }
    else
    {
    x -= surplus;
    day = 0;
    month++;
    if (month > 12)
    {
    year++;
    month = 1;
    }
    calc(x);
    }
    return;
    }

    int main(void)
    {
    printf("Please enter the start date!\n");
    scanf("%d%d%d", &year, &month, &day);
    printf("Please enter the number of days!\n");
    scanf("%d", &x);
    calc(x);
    return 0;
    }
    作者回复

    赞,你这种解答,倒是挺新鲜的。Y(^_^)Y

    2020-04-29 09:37:30

  • 研发

    2020-03-03 16:37:41

    1.乘法表
    #include <stdio.h>

    int main() {
    for(int i= 1;i<=6;i++){
    for(int j=1;j<=i;j++){
    if(j==i){
    printf("%d*%d=%d",j,i,i*j);
    }else{
    printf("%d*%d=%d\t",j,i,i*j);
    }
    }
    printf("\n");
    }
    return 0;
    }
    2.日期计算
    #include <stdio.h>

    int main(){
    int y,m,d;
    int x;
    scanf("%d%d%d",&y,&m,&d);
    scanf("%d",&x);
    for(int i=0;i<x;i++){
    d+=1;
    switch(m) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    if(d>31){
    d=1;
    m+=1;
    }
    break;
    case 12:{
    if(d>31){
    d=1;
    m+=1;
    }
    if(m==13){
    m=1;
    y+=1;
    }
    break;
    }
    case 6:
    case 9:
    case 11:
    if(d>30){
    d=1;
    m+=1;
    }
    break;
    case 2:
    if(y%4==0&&y%100!=0||y%400==0){
    if(d>29){
    d=1;
    m+=1;
    }
    }else{
    if(d>28){
    d=1;
    m+=1;
    }
    }
    }
    }
    printf("%d-%d-%d\n",y,m,d);
    return 0;
    }
    作者回复

    perfect!!完全正确!

    2020-03-05 20:23:43

  • 罗耀龙@坐忘

    2020-04-24 11:12:26

    茶艺师学编程

    花了好几天才把这节课给消化完······

    在补全课文的例子时卡了很久,到了后来才发现,switch···case···语句的case是要把switch的情况给穷举了,对应与课文中的例子,switch(m),月的情况,对应就要有case1、case2、case3、······、case9、case10、case11、case12。

    这里是补全的代码:

    #include <stdio.h>

    int main() {
    int y, m, d, X; // 定义存储 年月日 和 X 的变量
    scanf("%d%d%d", &y, &m, &d); // 读入年月日
    scanf("%d", &X); // 读入 X 值
    for (int i = 0; i < X; i++) { // 循环 X 次,每次向后推一天
    d += 1;
    switch (m) { //这里是以月(m)的情况做处理 ,一年有12个月,因此下面的Case要有12个(这里卡了我好久啊)
    case 1:
    case 3:
    case 8:
    case 7:
    case 10:
    case 12:
    case 5: { // 第一部分逻辑 31天的月份处理
    if (d > 31) d = 1, m += 1;
    if (m == 13) m = 1, y += 1;
    }; break;
    case 4:
    case 11:
    case 9:
    case 6: { // 第二部分逻辑 30天的月份处理
    if (d > 30) d = 1, m += 1;
    } break;
    case 2: { // 第三部分逻辑 2月的处理
    if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) {
    if (d > 29) d = 1, m += 1;
    } else if (d > 28) {
    d = 1, m += 1;
    }
    } break;
    }
    }
    printf("%d %d %d\n", y, m, d);
    return 0;
    }

    作业题,按照格式打印1-6的乘法表。我分阶段的遇到一下问题:
    不知道如何生成乘法表
    生成的乘法表有有重复的部分
    生成的乘法表不懂得换行
    每一道坎我都花了一天·······但还是出来了。
    尤其是我想到用变量c来控制乘法表的换行,并真的实现的时候,真的很有成就感。
    但不过1秒,我觉得这代码还得该:行的间距太大了·····

    这是代码:

    #include <stdio.h>

    int main() {
    int a,b,c; //三个变量,第三个是用来控制格式的
    for (b = 1 ; b <= 6 ; b++ ){ //这里处理乘法表(1-6)的部分
    for (a=1;a<=b;a++)printf(" %d * %d = %d\t",a,b,a*b);
    for (c=1; c<=6;c++)printf("\n"); //这里处理展示格式的
    }
    return 0;
    }
    作者回复

    很不错了!c循环内部,输出6个换行,着实没有必要。其实输出1和换行即可。

    2020-04-26 20:19:35

  • Jinlee

    2020-01-13 22:27:27

    老师您好,我的没有输出。编译没有错误,也和老师的代码对比了,不知道哪里出错了,麻烦老师有时间帮忙看下,谢谢 代码如下:
    #include <stdio.h>

    int main() {
    int y, m, d, X;
    scanf("%d%d%d", y, m, d);
    scanf("%d", X);
    int i;
    for(i = 0; i < X; i++) {
    d += 1;
    switch (m) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12: {
    if (d > 31) d = 1, m += 1;
    if (m == 13) m = 1, y += 1;
    }; break;
    case 2: {
    if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) {
    if (d > 29) d = 1, m += 1;
    if (m == 13) m = 1, y += 1;
    }
    else if (d > 28) {
    d = 1, m += 1;
    }
    } break;
    case 4:
    case 6:
    case 9:
    case 11: {
    if (d > 30) d = 1, m += 1;
    if (m == 13) m = 1, y += 1;
    } break;
    }
    }
    printf("%d%d%d", y, m, d);
    return 0;
    }
    作者回复

    读入的时候,变量前面需要加&符号

    2020-01-14 07:56:50

  • 徐洲更

    2020-01-09 12:48:15

    作业地址: https://github.com/xuzhougeng/learn-c-in-JKSJ/blob/master/jksj-c-03.c

    我今天写作业的时候,明明觉得自己的逻辑好像是对的,但是结果错了,应该如何debug呢? 特别是写了那么长的代码的时候。
    作者回复

    看到你的代码了,对于 switch...case 的整体结构理解很准确。debug 的话,可以尝试如下三种方式:
    1. 心算推理法,通过看代码找 bug,这个通常适用于经验丰富的老手。
    2. 关键步骤输出信息,在程序中,找到关键的位置,输出相关变量的值,通过程序输出找错误。
    3. 借助调试工具,IDE 一般都带有自己的单步调试工具,Linux 下面可以借助 gdb 进行调试。

    2020-01-12 12:19:22

  • Leolee

    2021-03-07 16:17:37

    非常烧脑,读完这一节马上去啃啊哈C,啃完再回来看这一节。
  • Never give up

    2020-03-27 20:31:22

    今天学习了if–else 分支语句,while 以及for 循环,虽然作业自己没有独立完成,在借鉴其他同学的答案后顺利运行了程序,一直觉得自己没有什么编程逻辑,尽管可能天赋不如别人,但努力学,敲代码,加理解,一步步来。
    作者回复

    加油!d(^_^o)

    2020-03-28 00:13:10

  • 小昭

    2021-10-07 19:08:14

    重新开始学编程,思考题目前还没想到解决办法。日期题根据老师给的模板,加上后半年的月份已经完成,并且可以正常运行了。
    基础太弱,先把《啊哈c》看一遍再继续学吧~
  • 黄矢

    2021-06-20 00:25:15

    scanf(%[^:]%*[:]%[^\n], color, content)

    为啥color老是无法正常输出,而后面的content能正常输出呢?

    如: red:hello world
    world→color
    hello world→content
  • sam

    2020-02-02 11:26:27

    #include <stdio.h>

    int main()
    {
    int y,m,d,x;
    scanf("%d%d%d", &y, &m, &d);
    scanf("%d", &x);

    for (int i = 0; i < x; i++)
    {
    d++;
    if ( 1 == m || 3 == m || 5 == m || 7 == m || 8 == m || 10 == m || 12 == m )
    {
    if ( 31 < d )
    {
    m++;
    d = 1;
    }

    if ( 13 == m )
    {
    m = 1;
    y++;
    }
    }
    else if ( 2 == m )
    {
    if ( ( y % 4 == 0 && y % 100 != 0 ) || y % 400 == 0 )
    {
    if ( 29 < d )
    {
    m++;
    d = 1;
    }
    }
    else
    {
    if ( 28 < d )
    {
    m++;
    d = 1;
    }
    }
    }
    else
    {
    if ( 30 < d )
    {
    m++;
    d = 1;
    }
    }
    }

    printf("%d%02d%02d\n", y, m, d);
    return 0;
    }
    作者回复

    正确,能改写成switch...case结构的程序就更好了!

    2020-02-02 18:21:28

  • rocedu

    2020-02-02 08:44:58

    还是从语法角度讲循环。我觉得可以从应用模式讲一下更好。循环应用有两类:
    1. 固定次数的
    2. 事件驱动的,会使用到条件语句

    这更体现编程不仅仅是语言。

    作者回复

    嗯,你说的这些内容,可以放到讲完循环基本语法以后的总结部分。d(^_^o)

    2020-02-02 11:01:50

  • 行问

    2020-01-09 06:51:34

    请教下,C 语言的编码规范有什么推荐的,专栏中的代码代码规范是如何进行的?
    作者回复

    C语言的专门的编码规范在国内不太多,基本都是参考一些流行的C++的编码规范。

    2020-01-09 10:18:02

  • MarkG

    2023-11-25 09:33:49

    打印乘法表(6*6)


    #include <stdio.h>
    #include <string.h>

    int main() {

    for (int i = 1; i < 7; i++) {
    char str1[200];
    char str2[50];
    for (int j = 1; j < i + 1; j++) {
    sprintf(str2, "%d * %d = %d\t", j, i, i * j);
    size_t space_left = sizeof(str1) - strlen(str1) - 1; // 计算str1剩余空间
    strncat(str1, str2, space_left);
    }
    printf("%s\n", str1);
    memset(str1, '\0', sizeof(str1));
    }

    return 0;
    }
  • 思考你的思考

    2023-07-30 10:22:54

    打印乘法表
    #include <stdio.h>
    int main()
    {
    int i,j;
    for(i=1;i<7;i++)//行递增
    {
    for(j=1;j<=i;j++)//每行中每个等式的计算
    {
    int d=i*j;
    printf("%d*%d=%d ",i,j,d);
    }
    printf("\t\n");
    }
    return 0;
    }
  • 西口径

    2023-07-21 16:42:06

    计算日期程序:
    #include <stdio.h>
    #include <stdbool.h>

    bool leapYear(int year)
    {
    return (0 == year % 4 && year % 100 != 0) || (0 == year % 400);
    }

    void whichDay()
    {
    int year, month, day;
    scanf("%d%d%d", &year, &month, &day);
    int afterDay;
    scanf("%d", &afterDay);
    printf("%d年%d月%d日\n", year, month, day);
    printf("%d天后是:", afterDay);

    int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    bool leap = leapYear(year);
    days[2] = leap ? 29 : 28;
    int newYear = year, newMonth = month, newDay = day;
    for(; afterDay > 0; afterDay--)
    {
    if (newDay < days[newMonth])
    {
    newDay++;
    }
    else
    {
    newMonth++;
    newDay = 1;
    if (newMonth > 12)
    {
    newYear++;
    leap = leapYear(newYear);
    days[2] = leap ? 29: 28;
    newMonth = 1;
    }
    }
    }

    printf("%d年%d月%d日\n", newYear, newMonth, newDay);
    }

    int main(int argc, char *argv[])
    {
    whichDay();
    return 0;
    }
  • 奕晨

    2023-04-10 15:00:59

    输出X天后的日期:
    #include <stdio.h>

    int main() {
    int y,m,d,X,i;
    scanf("%d%d%d", &y,&m,&d);
    scanf("%d", &X);

    for(i = 0; i < X; i++) {
    d += 1;
    switch(m) {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12: {
    if (d > 31) {
    d = 1;
    m += 1;
    }
    if (m == 13) {
    y += 1;
    m = 1;
    }
    break;
    }
    case 4:
    case 6:
    case 9:
    case 11: {
    if (d > 30) {
    d = 1;
    m += 1;
    }
    break;
    }
    case 2: {
    if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) {
    if (d > 29) {
    d = 1;
    m += 1;
    }
    } else {
    if (d > 28) {
    d = 1;
    m += 1;
    }
    }
    break;
    }
    }
    }
    printf("%d / %d / %d", y, m, d);
    return 0;
    }
  • JT

    2022-07-08 15:57:16

    发现一个有趣的现象,那就是越简单的东西背后的逻辑越复杂。

    这段用C写的代码:
    #include <stdio.h>

    int main(int argc, char *argv[]) {
    for(int i= 1;i<=6;i++){
    for(int j=1;j<=i;j++){
    if(j==i){
    // printf("I'm in If\n");
    printf("%d*%d=%d",j,i,i*j);
    // printf("I printed if\n");
    }else{
    // printf("I'm in else\n");
    printf("%d*%d=%d\t",j,i,i*j);
    // printf("\n");
    // printf("I printed else\n");
    }
    }
    printf("\n");
    }
    return 0;
    }
    在结果上面和下面的这段Python的代码出来的内容都是老师的第一个问题;

    for i in range(1,10):
    for j in range(1, i+1):
    if (j==i):
    print (f'{j}x{i}={j*i}', end="")
    else:
    print (f'{j}x{i}={j*i}\t', end="")
    print()

    程序运行后出来的结果是一致的。
    但是只要放到debugger里面去看,那就是另外一副景象。特别是用vscode,或者code runner去看变量i和j的变化的时候,C经常出现j的值是: expression failed to parse:
    还有就是出值的时候,都是积攒了整体后,再发出的。这个过程和人类的思考逻辑很不一致。
    在试了好几种语言之后,发现,只有python是把人类思考过程表达一致,老师,这个问题是我儿子问的,请问是不是就理解逻辑就行,过程不需要在意?
  • 奕晨

    2022-06-24 11:05:45

    打印乘法表
    使用循环和条件判断,打印一个格式优美的 66 乘法表
    要求 1:输出内容及样式参照下面给出的样例要求
    2:每两列之间用 \t 字符进行分隔,行尾无多余 \t 字符

    #include <stdio.h>

    int main() {

    for (int i = 1; i <= 6; i++) {
    for (int j = 1; j <= i; j++) {
    printf("%d * % d = %d \t", j, i, j * i);
    if (j == i) {
    printf("\n");
    }
    }
    }
    return 0;
    }
  • 王兆民

    2022-06-13 22:50:32

    #include <stdio.h>

    int main()
    {
    int y, m, d, X ,i; // 定义存储 年月日 和 X 的变量
    scanf("%d%d%d", &y, &m, &d); // 读入年月日
    scanf("%d", &X); // 读入 X 值
    for (i = 0; i < X; i++) //将for()内部int i修改为i =0,修改后才可以编译成功的。//i代表循环的次数
    { // 循环 X 次,每次向后推一天
    d += 1;
    switch (m)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10: //第1部分的逻辑是处理每月有31天的月份1357810
    { // 第一部分逻辑
    if (d > 31) d = 1, m += 1; //modify d>=31
    }; break;

    case 12://第2部分的逻辑是12月,也是有31天的月份
    { // 第一部分逻辑
    if (d > 31) d = 1, m += 1;
    if (m == 13) m = 1, y += 1;
    }; break;

    case 4:
    case 6: //第2部分的逻辑是处理每月有30天的月份46911
    case 9:
    case 11:
    { // 第二部分逻辑
    if (d > 30) d = 1, m += 1;
    } break;

    case 2: //第3部分的逻辑是处理2月这个月份的逻辑
    { // 第三部分逻辑
    if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) {
    if (d > 29) d = 1, m += 1; //modify d>=29
    } else if (d >= 28) //modify d>=29 2022.06.13 by jerry0
    {
    d = 1, m += 1;
    }
    } break;
    }
    }
    printf("%d %d %d\n", y, m, d);
    return 0;
    }
    我补全完成代码后,感觉自己学习C语言已经算是入门了,感谢指导!!!