C语言快速入门 - 控制语句

目录

什么是控制语句

回顾我们之前的程序,你会发现,我们的程序一直都是从上到下,一行接着一行执行的,并不能根据我们的意愿改变执行顺序,但是控制语句却有着这个功能。本文,就将介绍控制语句

if...else

我们把if翻译成中文,那他的意思便是如果,也就是根据我们的条件来执行相应的语句。

例子:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    if (a != 0)
    {
        printf("You get something!\n");
    } else {
        printf("nothing!\n");
    }
    return 0;
}

之前,大家在学运算符的时候学到过==的运算符,所以在这里这个整个语句的意思就是:假如a的值不是0,那么我们就输出You get something!

那么下面那个else又是什么意思呢?把它翻译成中文,那就是其他的意思,也就是说:假如a的值10,那么我们就输出nothing!

所以if的格式就是这样的:

if ([逻辑表达式])
{
    // 逻辑表达式为 真 的时候做的一些事
} else {
    // 逻辑表达式为 假 的时候做的一些事
}

if后面括号里跟的是一个逻辑表达式,根据上一节我们对它的理解来说(忘了就看一下),除了0是以外,其他任何一个数字都是,也就是说,只要这个逻辑表达式不等于0,那它就会执行下面大括号里的值。

所以我们就可以这样来简写:

if (a)
{
    printf("You get something!\n");
} else {
    printf("nothing!\n");
}

这样的效果是一样的。

其实,我们还能再简写。

我们观察一下main函数,你会发现它也有一个大括号,我们写的东西都在这个里面,其实if的大括号也是这样的作用,他就是把一堆要执行的语句囊括在其中的。但是我们这个例子就只有一句话,而不是一大堆,所以其实我们可以把它去掉,就像下面这样:

if (a) printf("You get something!\n");
else printf("nothing!\n");

当然,else也不是必要的。所以下面那么写也是可以的:

if (a) printf("You get something!\n");

或者:

if (a)
{
    printf("You get something!\n");
}

其实,组合起来也是有的:

if (a == 1)
{
    printf("a = 1\n");
} else
{
    if (a == 2) printf("a = 2\n");
} 

也可以外面:

if (a == 1)
{
    printf("a = 1\n");
} else if (a == 2)
{
    printf("a = 2\n");
} 

反正挺自由的。

最后,我们再来谈谈这个大括号。

作用域

大括号也不能直接叫它大括号吧,它也有名字的,那就是我们的标题——作用域

我们还是先来看一段例子:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    if (a)
    {
        int b = 1;
        printf("a = %d, b = %d\n", a, b);
    } else {
        int b = 2;
        printf("a = %d, b = %d\n", a, b);
    }
    // printf("%d", b); // 这句话错误!!
    return 0;
}

我们可以观察到,每个大括号里都定义了一个名叫b的变量,照道理来说是不能定义名字一样的变量的,但是这边为啥又可以了呢?
原因就在于,他们是属于不同的作用域的,他们之间没有联系的。所以,上面的b归上面,下面的归下面。

那a为什么能用呢?因为a所在的这个作用域是属于main函数的整个作用域的,自作用域可以使用父作用域里定义的变量。但是父作用域用不了子作用域里的变量,也就是例子中最后一句话错误的原因。

这个概念和数学里的集合有点像,理解起来也不是很复杂的。

goto

我们先翻译一下goto是什么意思吧。这其实是两个词的组合,goto,所以他的意思就是去一个地方,所以我们在程序中就可以运用goto来进行跳转从一个语句跳转到另一个语句,而不是规规矩矩按顺序从上到下执行。

但是具体跳转到哪里呢?跳转总要有目的地吧,这里就有与一个叫标签的概念了。

我们先来看一段例子。

#include <stdio.h>

int main()
{
    printf("Hello Control!\n");
    int i = 0;
mLabel:
    printf("Times: %d.\n", i);
    i ++;
    if (i < 5) goto mLabel;
    return 0;
}
  • mLabel就是那个标签,一个标签由一个标签名和一个:组成,其命名规则和变量命名规则一样。

  • 当程序从上到下运行时,遇到标签之后并不会做什么,而是跳过它,继续执行下面的语句。

  • 当程序执行的时候遇到了goto,那就会跳转到goto后面的标签所在的位置,然后继续执行标签下面的语句们,也就是例子中的printf("Times: %d.\n", i);

测试下这个程序:

输出

Hello Control!
Times: 0.
Times: 1.
Times: 2.
Times: 3.
Times: 4.

switch...case

我们设想一下,假如说一个变量有很多种可能性,而你的被要求把这么多种可能性都判断一遍,那么我们可以用if语句来实现:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    if (a == 1)
    {
        printf("%d\n", a);
        a = 2;
    } else if (a == 2)
    {
        printf("%d\n", a);
        a = 3;
    } else if (a == 3)
    {
        printf("%d\n", a);
        a = 4;
    } else if (a == 4)
    {
        printf("%d\n", a);
        a = 5;
    } else if (a == 5)
    {
        printf("%d\n", a);
        a = 6;
    } else if (a == 6)
    {
        printf("%d\n", a);
        a = 7;
    } else if (a == 7)
    {
        printf("%d\n", a);
        a = 8;
    }
    return 0;
}

这样,就可以针对不同的情况来实现不同的功能了(上文例子的这个功能其实完全可以不用这么实现,但是我是为了更直观的展示使用if判断多种情况)。

这个程序里,只要我们输入[1,7]内的数字,就可以输出它,并让a的值等于输入值+1。

这里我还将展示一种错误的写法:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    if (a == 1)
    {
        printf("%d\n", a);
        a = 2;
    }
    if (a == 2)
    {
        printf("%d\n", a);
        a = 3;
    }
    if (a == 3)
    {
        printf("%d\n", a);
        a = 4;
    }
    if (a == 4)
    {
        printf("%d\n", a);
        a = 5;
    }
    if (a == 5)
    {
        printf("%d\n", a);
        a = 6;
    }
    if (a == 6)
    {
        printf("%d\n", a);
        a = 7;
    }
    if (a == 7)
    {
        printf("%d\n", a);
        a = 8;
    }
    return 0;
}

注意观察,这边我把所有的else都去掉了,由于程序是按顺序执行下去的,所以,如果我们如果输了一个1,那它会把后面所有的234567全都输出出来的,和我们要的功能就相背了。

但不管怎么说,如果你写对了,这代码也太麻烦了吧,所以switch...case语句应运而生!

现在我们就把这个正确的例子改写为switch...case版本吧:

#include <stdio.h>

int main()
{
    int a = 0;
    scanf("%d",&a);
    switch (a) {
        case 1:
            printf("%d\n", a);
            a = 2;
            break;
        case 2:
            printf("%d\n", a);
            a = 3;
            break;
        case 3:
            printf("%d\n", a);
            a = 4;
            break;
        case 4:
            printf("%d\n", a);
            a = 5;
            break;
        case 5:
            printf("%d\n", a);
            a = 6;
            break;
        case 6:
            printf("%d\n", a);
            a = 7;
            break;
        case 7:
            printf("%d\n", a);
            a = 8;
            break;
        default:
            break;
    }
    return 0;
}

虽然说也挺长的,但是,这样写会更严谨些。

接下来我们就来说一下这个的格式吧。

switch ([待判断的变量])
{
    case [值]:
        // 当[待判断的变量] = [值]的时候做的一些事
        break; // 跳出switch语句
    default:
        // 当[待判断的变量]的值不符合上面的任何一个情况时做的一些事
}
  • 最上面的switch后面的括号里填的是一个待判断的变量
  • switch下面的是一对大括号,里面写的就是可能的情况。
  • case的意思就是情况,所以在case后面写的就是待判断的变量可能的值,在这个值后面再紧跟一个:用来指示这个情况要执行的代码都在后面。
  • default的意思是默认,也就是如果这个变量实际的值不满足上面的任何一个情况时,将会执行的代码。
  • break这个语句将会跳出switch语句,具体是什么意思呢,我们先来看下不加break的情况:
#include <stdio.h>

int main()
{
    int a;
    scanf("%d", &a);
    switch (a) {
        case 0:
            printf("0");
        case 1:
            printf("1");
        case 2:
            printf("2");
        case 3:
            printf("3");
        case 4:
            printf("4");
        default:
            printf("de\n");
            break;
    }
    return 0;
}

测试下这个程序:

输入

0

输出

01234de

输入

2

输出

234de

输入

10

输出

de

观察规律,我们发现,一旦满足这个case的条件之后,他就会一直往下执行,把之后的casedefault全部执行掉。总感觉每个case咋这么像goto的标签哈,处理方式和长相(都有:)都这么像所以说,还是加一个break来跳出switch语句,以免搞出些奇怪的东西。

循环

循环,其实也就是反复执行一段一样的代码罢了。在前面,我其实已经用goto实现了一个类似循环的结构了:

#include <stdio.h>

int main()
{
    printf("Hello Control!\n");
    int i = 0;
mLabel:
    printf("Times: %d.\n", i);
    i ++;
    if (i < 5) goto mLabel;
    return 0;
}

其中的

printf("Times: %d.\n", i);
i ++;

被我们重复地执行了5次,这其实就是我们称的循环体,也就是要反复执行的那部分代码。

但是这样goto来goto去的实现循环,总觉得有点麻烦,还容易出错,所以用专门的循环语句会更好。

在C语言中,总共有3种循环语句,下面我将我给大家一一介绍。

while

while语句是一中比较简单的语句,格式如下:

while ([逻辑表达式])
{
    // 循环体
}

只要[逻辑表达式]的值为(再回顾一下,只要[逻辑表达式]的值不是0,它就是只有值是0的时候,才为),那就不停的执行循环体里的代码。

那么我们先看下一个简单的数字复读机的例子吧:

#include <stdio.h>

int main()
{
    int num;
    scanf("%d", &num);
    while (num != -1)
    {
        printf("%d\n", num);
        scanf("%d", &num);
    }
    return 0;
}

观察代码,只要num的值不为-1,那就一直输入数字,并输出这个数字。这样,我们就完成了这个数字复读机了。

当然,我们还可以这么写:

#include <stdio.h>

int main()
{
    int num;
    while (1)
    {
        scanf("%d", &num);
        if (num == -1) break;
        printf("%d\n", num);
        
    }
    return 0;
}

switch...case语句中,我们学过了break语句,它是用于跳出switch语句的,同样的,break语句同样适用于循环语句。在这个例子中,当读入当数字等于-1的时候,我们就用break语句跳出循环,并且break之后的语句都将不会被执行

同时,在这个例子中,假如没有这个break,那这个程序将会一直被执行下去,一直到你强制把它关闭为止,因为你在上面的while后面的值为1,也就是真,那么这样的结构就是死循环

while (1)
{
    // 需要反复执行的代码
}

上面的这个就是一个死循环,除了break语句以外都不能使这个循环结束。

do...while

do...while和while循环十分相似,只是有一点点不相同,那就是执行顺序。

由于程序是从上到下执行的,所以,while语句它是先执行那个条件判断语句的。

#include <stdio.h>

int main()
{
    int num;
    scanf("%d", &num);
    while (num != -1)
    {
        printf("%d\n", num);
        scanf("%d", &num);
    }
    return 0;
}

这是之前的那个程序,那么程序从上到下依次执行的是int num;定义变量,然后再是scanf("%d", &num);读入数据,并赋值给num。接下去便是我们的循环,他先碰到的是while括号里的内容,也就是num != 1,于是它先做一遍判断,如果num不等于-1的时候,那就执行循环体里的东西,反之便跳过大括号,执行下面的return 0;去了。

但是这边写了两个scanf("%d", &num);,略显麻烦,所以do...while正是来解决这个问题的。

格式:

do
{
    // 循环体
} while ([逻辑表达式]);

将刚刚那个程序修改成do...while的格式就是这样的:

#include <stdio.h>

int main()
{
    int num;
    do
    {
        scanf("%d", &num);
        printf("%d\n", num);
    } while (num != -1);
    return 0;
}

当然,在功能上与之前的那个程序还是有一点差别的,不知道大家有没有看出来。

唯一的区别就是,我这个程序他在输入-1的时候也会复读出-1。

do...while语句它是先执行完循环体里的内容,再去判断while后面跟的条件的。而while语句是先判断一边条件再去执行循环体的。这就是他们区别,根据不同的编程场合,应合理运用。

这张GIF可以看出他们的不同,布尔值其实就是一种只有真/假两个状态的值

for

现在有一个很简单的问题:

输入一个数字n,求1 + 2 + 3 + ... + n的和。

测试样例:
输入

10

输出

55

输入

100

输出

5050

我们可以尝试先用while语句实现下:

#include <stdio.h>

int main()
{
    int n;
    scanf("%d",&n);
    int i = 1;
    int sum = 0;
    while (i <= n) {
        sum += i;
        i ++;
    }
    printf("%d\n",sum);
    return 0;
}

我们要先定一个变量i,用于代表当前加到的数字,变量sum则用于记录总和。
接下来就是判断,只要i ≤ n就对sum进行累加。而i ++;则是让要加的数字+1。

其实对于这种要循环制定次数的循环结构,那么我们则可以使用for循环来达到我们的目的。

先来看下格式:

for ([执行循环体之前的表达式,一般用于循环中要用到的变量的初始化];[逻辑表达式];[循环体尾部的表达式])
{
    // 循环体
}

说了那么多,估计大家都晕了还是看张图比较明白:
生动的ppt动画。。。

这样就直观多了。

那么我们再来看看之前那个例子用for循环实现是怎么样的:

#include <stdio.h>

int main()
{
    int n;
    scanf("%d", &n);
    int sum = 0;
    for (int i = 1;i <= n;i ++)
    {
        sum += i;
    }
    printf("%d\n", sum);
    return 0;
}

其中,int i = 1与之前定义的变量一样,是用来记录当前要加的数,i <= n和之前的while后面的逻辑表达式作用一样,i ++也和之前的没区别,也就是在循环尾部对要加的数字进行自加。

但是这样写有一个好处是,程序更容易阅读,并且变量i也只能在for下面的那个作用域起作用,离开了这个作用域这边定义的变量i就无法再次使用了。

if语句一样,for循环也是可以简写的(while也行),只要作用域里只有一句话,那就可以使用简写,也就是行语句

上面的那个for语句可以写成下面的这个形式:

for (int i = 1;i <= n;i ++) sum += i;

break 和 continue

之前在讲switch语句的时候提到过break的用法,然后在while里也简单提过,现在我就来详细地说明一下。

在switch里面我们讲过,break是跳出整个switch语句对作用域,也就是说不再往后执行下去,而在while和for里面也是这样的。break语句可用于各种循环语句以及switch语句,就是跳出循环语句,使作用域内之后的语句不再起作用,然后继续执行循环体后面的语句。

continue也十分类似,它也会使它后面的语句失效,并跳转,但是跳转的位置和break不同,break跳转到循环体末尾,而continue则是跳转到整个循环的最前面,并再判断一遍那个条件,之后继续执行循环体里面的语句。

还是图比较明了

简单的例子:

#include <stdio.h>

int main()
{
    int i = 0;
    while (i < 3)
    {
        i ++;
        if (i == 2) continue;
        printf("%d\n",i);
    }
    return 0;
}

输出

1
3

利用continue就把2给避开了,但3还是能正常输出。

但是假如用break,那么3就不会被输出了:

#include <stdio.h>

int main()
{
    int i = 0;
    while (i < 3)
    {
        i ++;
        if (i == 2) break;
        printf("%d\n",i);
    }
    return 0;
}

输出

1

当i = 2的时候,循环就被退出了。

结语

到这里,大家已经掌握了基本的编程技巧了,可以适当的做一些编程题练练手了。这里我推荐个网站可以练习一些基础编程题:PTA注册个账号就能开始练习了,这样就可以巩固一些编程知识了。同时也欢迎大家阅读下一篇文章《C语言快速入门 - 函数》。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容