avatar
/home/robomico
33 分钟带你速通C语言

33 分钟带你速通C语言

不信请看阅读时长

33 分钟带你速通C语言

注意:本篇教程处于 Beta 测试阶段。如果你遇到了看不懂、不会做、有错别字、程序报错等问题,请在评论区留言,作者不一定保证会及时赶来谢罪,但一定会认真做修订,谢谢各位的贡献。

感慨于大学本科课程教的 C 语言普遍拉完了,故抽出摸鱼时间撰写本文,供各位同学自救使用。

本篇教程不讲废话,不扯定义,实用第一,以实例代替习题,帮你快速掌握 C 语言编程的基本操作,也可以用作知识速查表。

必备品:一台电脑。对英语和数学水平零要求。

安装 IDE

IDE 的正式中文名叫做“集成开发环境”。在很久以前,IDE 还没有发明的时候,人们编写程序需要用到很多工具软件,包括编辑器(编写代码)、编译器(把源代码编译成可执行的程序)、调试器(给程序排除问题)等等。IDE 则是集各类工具于一体的编程工具箱,可以很方便地编写、运行程序。

小熊猫 C++ 是一款非常适合新手入门的 IDE,开源免费,简单好用。安装步骤如下:

  1. 在电脑上打开右边的链接,转到官方下载页面:https://royqh.net/redpandacpp/download/

  2. 从“蓝奏云网盘”和“百度云网盘”两种下载方式中二选一,记住链接右边的分享码,然后点击对应的蓝色链接。输入对应的分享码后就能看到文件列表。 redpanda-download-link.png

  3. 下载文件名带有 MinGW64 字样,且以 .exe 结尾的文件下载。 redpanda-download-file.png

  4. 下载完成后运行,即可打开安装包。按照指引,一直点击“下一步”即可完成安装。

  5. 初次启动小熊猫 C++ 时,会弹出配置向导。可以在深色和浅色外观之中根据自己的喜好进行选择,记得将默认语言切换成 C,然后点击右下角的确定redpanda-setup.png

  6. 把小熊猫的编辑器想象成一个带有多标签页功能的记事本,可以对源代码文件进行打开、编辑、保存等操作。点击上排按钮中的三角形(或者按下键盘上的 F11)可以编译并运行正在编辑的源代码。 redpanda-gui.png

编写第一个程序

把下面这段代码复制粘贴到小熊猫的编辑区中(点击代码块右上角的剪贴板符号可以一键复制),点击上排按钮中的保存键(或者按下键盘快捷键 Ctrl+S),在电脑中选择一个喜欢的地方保存你的代码,然后按下运行键(或者键盘上的 F11)。

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
    printf("Hello World!");
    return 0;
}

可以看到弹出了一个黑底白字的窗口(由于笔者的电脑配置问题,实际看到的效果与图片中略有出入),第一行写着 “Hello World!”。这个黑底白字的窗口就是你创造的程序。切换到这个窗口,按下键盘上任意的按键可以关闭这个程序。当然也可以直接点击窗口右上角的按钮来关闭。

hello-world-window.png

你可以随意更改第 6 行双引号中的文本(甚至是输入中文),再次点击运行即可看到不一样的效果。

本文之后出现的代码,都请务必复制粘贴到 IDE 中,自己动手运行一遍。最好还能根据自己的理解,对代码做一些修改后再尝试运行,即使编译出错或者运行结果不对也不要紧,尝试修复错误并了解出错的原因,可以很大程度地提升编程水平。

如果你想打印多行文本的话,在需要换行的地方写 \n 即可。其中 \ 是反斜杠符号,在键盘上的位置通常是回车(Enter)键上面一个。

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
    printf("学了C语言\n手机电脑单片机\n全都能编程\n最有用");
    return 0;
}

C 语言程序的基本结构

第 1 行 #include 开头的代码是编译器预处理命令。目前你不需要了解它有什么用,只需要会默写(或者复制粘贴)即可。

第 3 行的 int main() 定义了程序的主函数。听不懂没关系,你只需要知道任何程序都从这一行开始执行代码

第 5 行的 printf 用处是往屏幕上打印文本。注意这个语句的用法,先有一层圆括号 (),然后再用双引号 "" 把要打印的内容包起来。C 语言代码中所有的符号都是英文输入法下打出的符号,请不要在敲代码时使用中文输入法。

第 6 行的 return 0 表示这里是程序的终点。在主函数中,程序一碰到 return 就会立即结束,忽略之后的任何代码。例如下面的这个程序只会打印 123,而不会打印 123456

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
    printf("123");
    return 0;
    printf("456");
    return 0;
}

所有会被执行的语句都需要有一个分号 ; 结尾。单独的一个分号被视为一个什么都不做的空语句。例如下面的这个程序什么也不会输出:

1
2
3
4
5
6
7
#include <stdio.h>

int main()
{
    ;
    return 0;
}

代码格式与注释

在 C 语言代码中,任何换行和多余的空格都是可以忽略的。仅有两个例外:

  1. 编译器预处理命令(如 #include <...>)必须单独占一行。
  2. 双引号("")包裹的字符串不能换行,而且内部的空格会被认为是字符串的一部分。

例如,下面的这个程序与一开始打印 “Hello World!” 的程序效果一模一样:

1
2
#include <stdio.h>
int main(){printf("Hello World!");return 0;}

甚至也可以写成这样:

1
2
3
4
5
6
7
8
#include <stdio.h>

int
 main (  )
  {
   printf (  "Hello World!"   )  ;
    return      0 ;
     }

只不过,排版整齐的代码更方便阅读,有利于编程的人理解程序的逻辑。小熊猫 C++ 支持一键排版代码,只需点击菜单栏中的 代码 -> 对代码重新排版,或者按下键盘快捷键 Ctrl+Shift+A。

注释是指给人提供参考,而在编译时会被忽略的文本。C 语言支持两种注释,分别为单行和多行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

/*
这是一块多行注释
你可以尝试把这个程序全文复制粘贴到编辑区,然后运行
*/

int main()
{
    // 这是一行单行注释
    printf("Hello everyone!"); // 跟大家打个招呼!
    printf("//"); // 字符串内的符号都是字符串的一部分,不会触发注释

    // printf("I prefer not to speak my name.");
    // 小提示:给一行代码加上注释,可以方便地将其“禁用”。需要恢复时,去掉注释符号即可。

    return 0;
}

C 语言程序的编译流程

如果你足够细心,会发现小熊猫 C++ 在保存代码时会保存一个拓展名为 .c 的文件。这个文件就是你用 C 语言写出来的源程序

你可以尝试用 Windows 自带的记事本打开这个 .c 文件。事实是记事本也可以毫无障碍地浏览、编辑 C 语言源代码,因为用 C 语言写出来的代码本质上就是一堆文本,和 .txt 文本文档是一样的,唯一的区别在于文件拓展名的不同。

问题来了,既然 C 语言代码本质上就是一堆文本,那么电脑要怎么运行这个程序呢?此时需要另一种软件的帮助:编译器

编译器可以读取你写的 C 语言代码,然后将其转换为电脑可以运行的 .exe 程序。这个转换的过程叫做编译

编译的具体过程十分复杂,此处只是简单的介绍。小熊猫 C++ 自带了一份编译器,在小熊猫 C++ 中点击“运行”时,小熊猫 C++ 会自动编译你的程序并运行它,因此十分方便快捷。

变量

计算机中一切信息都是以数字的形式储存的。变量就是储存数字的容器。

C 语言中的变量有很多类型,类似于酒店中的客房分为单间、标间等不同房型,可以满足储存各式各样数字的需求。

可以用这样的语句创建变量:<类型> <变量名>;

键盘上的等于号 = 被叫做赋值符号,用途是把符号右边的数字储存到符号左边的变量中。请看例子:

1
2
3
4
5
6
7
8
#include <stdio.h>

int main()
{
    int a;  // 创建(或者说定义)了一个类型为int、名称为a的变量
    a=5;    // 变量a现在储存了一个整数5
    return 0;
}

但是这个程序什么也没有输出。我们可以用 printf 来输出变量的值:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
    int a;
    a=5;
    printf("The value of a is %d",a);
    return 0;
}

请注意这里 printf 与之前格式的不同。双引号中出现了一个东西 %d,它叫做格式占位符,意思就是说“a 的值应该在这里出现”。

双引号中格式占位符的数量,应该与括号内所附带变量的数量一致。占位符与变量是依次一一对应的。例如下面的程序会输出 3.1.2

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main()
{
    int a,b,c;  // 这样可以一次性定义多个相同类型的变量
    a=1;
    b=2;
    c=3;
    printf("%d.%d.%d",c,a,b);
    return 0;
}

C 语言中常见的变量类型如下:

类型格式占位符用途
int%d储存整数,支持的范围为 $-2^{31}$ ~ $2^{31}-1$(约为 21 亿)
long long%lld储存整数,支持的范围为 $-2^{63}$ ~ $2^{63}-1$(约为 922 亿亿)
short%hd储存整数,支持的范围为 $-2^{15}$ ~ $2^{15}-1$(约为 3 万)
char%c储存一个字符,C 语言中字符的本质是一个取值范围为 0 ~ 255 的整数,详见下文
float%f储存实数
double%lf储存实数,支持比 float 更好的精确度

注意,不同类型的变量,在输入和输出时使用的格式占位符是不同的。如果在编程时没有注意匹配,尽管编译时不会报错,但在运行时会出现无法预料的错误。

变量的命名

并不是任何文本都可以成为变量的名称。变量名需要满足以下要求:

  1. 只能是大小写英文字母(a-zA-Z)、数字(0-9)和下划线(_)的组合。
  2. 第一个字符不能是数字。
  3. 不能和 C 语言关键字相同。

以下是一些合规和无效的变量名的实例:

1
2
Good: a, xyz, Num, my_var, var123, _i, NAME
Bad:  123num(第一个字符不能是数字), 变量一号(不能是中文), $x(出现了无效的符号), int(与关键字相同)

从键盘输入

使用 scanf 可以实现从键盘向程序输入信息。它的用法与 printf 很像,请看例子:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
    int x;
    scanf("%d",&x);
    printf("你输入的数是%d",x);
    return 0;
}

运行程序,发现弹出的窗口里什么也没有,实际上程序正在等待用户输入数字。随便输入一个整数(比如 123),然后按下回车(Enter)键,即可完成输入,程序继续运行。

scanf 的用法与 printf 很像,括号内都是一个带有格式占位符的字符串,后带若干个变量。不同之处在于,scanf 中的变量名前面需要带有 & 符号,它的真实名字是取地址符。目前你无需理解它的用途,只需要记住在 scanf 中需要用到即可。

修改 scanf 中的格式字符串,可以让程序在读入文本时智能提取其中的数字。例如以下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main()
{
    int a,b,c;
    scanf("a=%d b=%d c=%d",&a,&b,&c);
    printf("a is %d, b is %d and c is %d",a,b,c);
    return 0;
}
/*
输入: a=4 b=88 c=205
输出: a is 4, b is 88 and c is 205
*/

scanf 会尽力从输入的文本中匹配格式字符串,直到匹配完毕,或者遇到其“不能理解的字符”后停止。如果在匹配停止时,有格式占位符还没有被处理,那么对应变量的值将不会发生任何改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main()
{
    int a=0;    // 可以在定义变量时快捷地为其指定一个初始值
    scanf("a=%d",&a);
    printf("a is %d",a);
    return 0;
}
/*
输入: a=666
输出: a is 666

输入: a=1.23
输出: a is 1

输入: 123
输出: a is 0

输入: man! what is a?
输出: a is 0
*/

数学运算

可以用变量、数字和运算符组成表达式,从而进行运算。

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int main()
{
    int a,b,c;
    a=16;
    b=9;
    c=a+b;  // 计算a+b的值(16+9=25),然后把这个值赋值给变量c
    printf("%d",c);
    return 0;
}

C 语言支持加减乘除四则运算,其中乘号使用星号 * 代替,除号使用正斜杠 / 代替。注意,整数除以整数,得到的结果是商的整数部分,只有在除号任意一边是实数时,得到的结果才是实数。

用小括号包裹表达式的一部分,可以在运算时将其视作一个整体。运算顺序遵守“先乘除,后加减,从左往右依次算”。多层括号请全部使用小括号,而不要使用中括号(方括号 [])或大括号(花括号 {}),它们有别的用处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main()
{
    int res;
    res=16/5+3*(2-1)+(2*(4-2));
    printf("%d",res);
    return 0;
}
/*
解析:
  16/5 + 3*(2-1) + (2 * (4-2))
= 3 + 3*1 + 2*2
= 3+3+4
= 10
*/

C 语言还有一种特殊的运算符 %,用来求整数除法中的余数。求余运算的优先级与乘法、除法相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main()
{
    int res;
    res=32%6;
    printf("%d\n",res);   // 2
    res=15%2;
    printf("%d\n",res);   // 1
    res=20%5;
    printf("%d\n",res);   // 0
    return 0;
}

结合上文所讲的输入,我们可以编写一些简单的计算器。例如以下程序可以把摄氏度转化为华氏度:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
    double cel,fah;
    scanf("%lf",&cel);
    fah=cel*9/5+32;
    printf("%lf",fah);
    return 0;
}

printf 输出的值不一定是单个独立的变量,也可以直接输出表达式的值。例如以下程序可以计算两个整数的和:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    printf("%d",a+b);
    return 0;
}

赋值符号左边的变量也可以出现在赋值符号右边的表达式中,程序会先用变量原本的值计算右边表达式的值,然后用结果覆盖左边变量所储存的值。

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
    int a;
    scanf("%d",&a);
    a=a*2+1;
    printf("%d",a);
    return 0;
}

C 语言支持一些快捷的赋值语句,用来改变变量所储存的值。请看例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int main()
{
    int a=2;
    a+=5;   // a的新值比原来大5,等效于a=a+5
    a-=3;   // a的新值比原来小3,等效于a=a-3
    a*=2;   // a储存的数值翻倍,等效于a=a*2
    a/=3;   // a的新值是原来的值除以3的商,等效于a=a/3
    a++;    // a储存的数值增加1,等效于a=a+1或a+=1
    a--;    // a储存的数值减少1,等效于a=a-1或a-=1
    // 某些地方还能看到++a和--a,它们分别与a++和a--类似,目前不需要知道两者的区别
    printf("%d",a);
    return 0;
}

条件语句

if-else 语句可以用来选择性地执行一段语句。格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
if(条件)
{
    语句1;
    语句2;
    ...
}
else
{
    语句a;
    语句b;
    ...
}

如果程序准备执行 if 处的语句,会先判断括号内的条件是否成立。如果成立,那么会执行上方大括号内的 语句1;语句2;...,并跳过下方大括号的内容;反之,如果不成立,那么会跳过上方大括号的内容,执行下方大括号的 语句a;语句b;...

特别地,如果在条件不成立时不打算做任何事,那么可以省略 else 和与其连接的大括号,变成如下格式:

1
2
3
4
5
6
if(条件)
{
    语句1;
    语句2;
    ...
}

如果在准备执行 if 处语句时条件成立,那么程序会执行大括号内的 语句1;语句2;...;否则跳过大括号内的语句,继续往后执行。

如果大括号内的语句只有一条,那么大括号可以省略,变成如下格式:

1
2
if(条件) 语句1;
else 语句2;

C 语言支持判断数字之间的大小和相等关系,符号和对应含义如下:

符号含义
==相等
!=不相等
>大于
>=大于等于
<小于
<=小于等于

注意判断相等需要使用两个等号,单个等号的含义是赋值。

例如下面的程序,如果用户输入的整数小于等于 18,那么会输出两句话,否则只输出一句话。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main()
{
    int age;
    printf("What is your age? :");
    scanf("%d",&age);

    if(age<=18)
    {
        printf("You are young!\n");
    }
    printf("Hello there!");

    return 0;
}

条件判断中也可以使用表达式。例如下面的程序会判断用户输入的整数是奇数还是偶数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

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

    if(x%2==1)
    {
        printf("odd");
    }
    else
    {
        printf("even");
    }

    return 0;
}

逻辑运算符

逻辑与逻辑或运算符可以以特定关系连接两个条件,相当于中文里的“并且”和“或者”,其中 || 表示“或者”,&& 表示“并且”。| 可以通过按住键盘上的 Shift 同时按下反斜杠键(\,一般在 Enter 回车键上方)打出来。

&& 的优先级高于 ||。逻辑运算符构成的表达式中也可以使用括号,将表达式的一部分视为一个整体。

例如下面这个程序可以判断用户输入的年龄是否合理(处于 0 到 100 之间):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main()
{
    int age;
    printf("What is your age? :");
    scanf("%d",&age);

    if(age<0 || age>100)
    {
        printf("Not a valid age!");
    }
    else
    {
        printf("OK!");
    }

    return 0;
}

再例如,下面这个程序可以用来判断闰年(四年一闰,百年不闰,四百年又闰):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main()
{
    int year;
    scanf("%d",&year);

    if (year%400==0 || (year%4==0 && year%100!=0))
    {
        printf("leap year");
    }
    else
    {
        printf("common year");
    }

    return 0;
}

逻辑运算的顺序是从左到右,并且遵循“偷懒原则”(科学地说,这两种运算符具有“短路效应”)。如果 || 左侧的条件已经被判定为“成立”,那么程序就不会判断其右边的条件,直接认为表达式是“成立”;如果 && 左侧的条件已经被判定为“不成立”,那么程序就不会判断其右边的条件,直接认为表达式是“不成立”。

以上面判断闰年的程序为例:

  • 如果 year 是 400 的整数倍(year%400==0 成立),那么这一年一定是闰年,程序不会再继续计算 || 右边的式子,直接输出 “leap year”;
  • 否则,程序计算右边括号里的表达式。
    • 如果 year 不是 4 的整数倍(year%4==0 不成立),那么这一年一定不是闰年,程序不会再继续计算 && 右边的式子,直接输出 “common year”;
    • 否则,程序继续计算 && 右边的式子。
      • year 已经是 4 的整数倍,如果它还不是 100 的整数倍(year%100!=0 成立),那么这一年就是闰年,整个表达式被判定为“成立”,程序输出 “leap year”;
      • 否则,这一年不是闰年,整个表达式被判定为“不成立”,程序输出 “common year”。

还有一种逻辑运算符 !,名为逻辑非,可以用来反转条件,即“成立”变为“不成立”,“不成立”变为“成立”。例如,!(year%100==0)year%100!=0 是两个等效的表达式。

注意 != 中的感叹号,它是“不等号”的一部分,而不是逻辑运算符。逻辑非运算符必须要放置在一个完整的表达式之前。

数值与逻辑的关系

除了上述的逻辑表达式以外,一般的变量甚至是数学表达式也可以当作逻辑条件使用。把一个数字当成一个逻辑条件处理时,数值为 0 则认为这个条件不成立,除此之外的任何值都认为这个条件成立。

例如,这一段代码永远会输出 “Yes”:

1
2
3
4
5
6
7
8
if(616)
{
    printf("Yes");
}
else
{
    printf("No");
}

再例如,下面这一段代码,大括号内的语句永远都不会被执行:

1
2
3
4
if(0 && 1)
{
    printf("我是棍木");
}

再例如,上文提到的判断奇偶数的程序,可以简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

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

    if(x%2)
    {
        printf("odd");
    }
    else
    {
        printf("even");
    }

    return 0;
}

反过来,逻辑表达式的结果也可以被当成是一个数字,可以储存在变量中,甚至是参与数学表达式的运算。把逻辑条件当作数字处理时,“成立”的条件数值上等于 1,“不成立”的条件数值上等于 0

例如,下面这个程序会输出 3,因为第 7 行的 4 个条件里只有 3 个是成立的:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
    int x;
    x = (3>2) + (4!=5) + (999==999) + (50>80);
    printf("%d",x);
    return 0;
}

循环语句

循环语句可以用来反复多次地执行一段代码。C 语言的循环语句分为 whilefor

while 循环

while 语句的格式如下:

1
2
3
4
5
6
while(条件)
{
    语句1;
    语句2;
    ...
}

当程序准备执行 while 处的语句时,会先判断括号内的条件是否成立。如果条件成立,那么会执行大括号内的 语句1;语句2;...,之后再次回到条件判断,如果条件成立,那么再执行大括号内的语句,如此循环。如果某一次条件判断为不成立,那么大括号内的语句就会被跳过,程序继续往后执行。

if-else 类似,如果大括号内只有一条语句,那么大括号可以省略,变成如下格式:

1
while(条件) 语句1;

例如,下面的程序可以输出 1 到 10 的整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

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

再例如,下面的程序会无休无止地输出 “ha”:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
    while(233)
    {
        printf("ha ");
    }
    return 0;
}

while 循环还有一种变种 do-while,格式如下:

1
2
3
4
5
6
do
{
    语句1;
    语句2;
    ...
} while(条件);

程序在运行 do 处的语句时,无论如何都会先运行一遍大括号内部的 语句1;语句2;...,之后判断括号内的条件是否成立,如果成立则再次运行大括号内的语句,如此循环,直到某一次括号内的条件判断为不成立,程序再继续往后执行。

例如,下面的程序会从大到小输出 32 到 2 的所有 2 的幂次:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main()
{
    int i=32;
    do
    {
        printf("%d ",i);
        i/=2;
    } while(i>1);
    printf("\n");
    return 0;
}

for 循环

for 语句的格式如下:

1
2
3
4
5
6
for(语句1;条件2;语句3)
{
    语句a;
    语句b;
    ...
}

程序在运行 for 语句时,首先会执行一次括号内的 语句1;然后判断 条件2 是否成立,如果成立,就执行大括号内的 语句a;语句b;...,并且再执行一次括号内的 语句3,之后又回到条件判断,如此循环。如果某一次 条件2 被判定为不成立,那么循环结束,程序继续往后执行。

注意括号内的 语句3 结尾不能带有分号。

if-else 类似,如果大括号内只有一条语句,那么大括号可以省略,变成如下格式:

1
for(语句1;条件2;语句3) 语句a;

for 语句的功能也可以用 while 语句实现,如下:

1
2
3
4
5
6
7
8
9
语句1;
while(条件2)
{
    语句a;
    语句b;
    ...

    语句3;
}

for 循环最常用的场景是“将某一段代码循环执行特定的次数”。例如,下面的程序可以输出 1 到 10 的整数:

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

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

这个程序的功能与上文用 while 实现的程序效果是完全一致的,但是更加清晰易读,只需要看 for 所在的行就能知道变量 i 的取值“从哪开始,到哪结束”。

再例如,下面的程序可以输入一个整数,然后输出这么多个“Hi”:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

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

for语句1条件2语句3 都是可以选择省略的。如果 语句1语句3 被省略,那么相应时刻就不会执行对应的语句;如果 条件2 被省略,那么每一次条件判断都默认会当作“成立”。特别地,如果三者都被省略,那么就会创造一个永远不会结束的循环。例如下面的程序会无休无止地输出 “ha”:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
    for(;;)
    {
        printf("ha ");
    }
    return 0;
}

for 也有一些比较巧妙的用法。比如下面这个程序可以输入一个正整数,然后把这个整数按数位逆序输出(比如输入 123,输出 321):

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>

int main()
{
    int n;
    scanf("%d",&n);
    for(;n>0;n/=10)
    {
        printf("%d",n%10);
    }
    printf("\n");
    return 0;
}

循环控制语句

循环控制语句可以在循环语句中间控制循环的运行。在 whilefor 循环中都可以使用循环控制语句。一般来说,循环控制语句都需要配合 if-else 语句使用。

continue; 语句可以提前跳到下一轮循环。例如,下面的程序可以输出 1 到 50 中不是 7 的倍数的数:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

int main()
{
    for(int i=1;i<=50;i++)
    {
        if(i%7==0) continue;
        printf("%d ",i);
    }
    printf("\n");
    return 0;
}

for 循环中使用 continue; 时,小括号内的 语句3 依然会被执行。

break; 语句可以提前强行终止循环,让程序继续执行循环之后的语句。例如,下面的程序会输出 10 次“Hi”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
    int i=0;
    while(616)
    {
        printf("Hi ");
        i++;
        if(i==10) break;
    }
    printf("\n");
    return 0;
}

for 循环中使用 break; 时,小括号内的 语句3 最后不会被执行。

语句的嵌套

上文所讲的 if-elsewhilefor 语句都可以相互之间进行任意嵌套,从而实现更复杂的逻辑。

### 循环控制语句一节中已经出现了 forwhile 循环内嵌套 if 语句。我们来看看多层 if-else 语句的嵌套。

例如,下面的程序可以根据输入的分数来给出评级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>

int main()
{
    int s;
    scanf("%d",&s);
    if(s>=100)
    {
        printf("S");
    }
    else
    {
        if(s>=80)
        {
            printf("A");
        }
        else
        {
            if(s>=60)
            {
                printf("B");
            }
            else
            {
                printf("C");
            }
        }
    }

    return 0;
}
  • 如果输入的分数大于等于 100,那么输出 S,后面的所有语句都被跳过,程序结束。
  • 否则,进入第二层判断。如果分数大于等于 80,那么输出 A,跳过第二层 else,程序结束。
  • 否则,进入第三层判断。如果分数大于等于 60,那么输出 B,跳过第三层 else,程序结束。
  • 否则,进入第三层的 else,输出 C,程序结束。

注意,完整的一套 if-else 整体可以被视为一条语句。因此,上面的程序可以简化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main()
{
    int s;
    scanf("%d", &s);

    if (s >= 100)
        printf("S");
    else
        if (s >= 80)
            printf("A");
        else
            if (s >= 60)
                printf("B");
            else
                printf("C");

    return 0;
}

多层嵌套中 ifelse 的对应关系通常难以直观地看出。ifelse 配对的规则可以类比于左括号与右括号的配对。事实上,因为 if-else 的配对实在难以通过肉眼判定,在实际开发中,有很多人会有意识地避免 else 的使用。

现在再来看循环的嵌套。例如下面的程序会输出 54 个 “Ciallo~ (^.^)”,分为 9 行,每行 6 个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=6;j++)
        {
            printf("Ciallo~ (^.^)  ");
        }
        printf("\n");
    }
    return 0;
}
  • 最外层由变量 i 控制的循环会执行 9 次。每一次循环会执行如下操作:
    • 进入内层的一个循环,由变量 j 控制,执行 6 次,每次都输出一个 Ciallo~ (^.^)
    • 内层循环结束后,输出一个换行符号 \n

在嵌套的循环内使用循环控制语句时,只对目前最内层的循环生效。例如,以下程序会输出 10 到 99 中个位数不是 3、6、9 的整数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        {
            if(j%3==0) continue;
            printf("%d ",i*10+j);
        }
    }
    return 0;
}

更多学习资料

  • cppreference.net - 权威 C/C++ 参考文档网站,相当于 C/C++ 语言的电子词典。另有中文版可供参阅。
  • OI Wiki - 为竞赛选手打造的编程语言与算法百科,内容比较硬核。
  • 菜鸟教程 - C 语言篇 - 通俗易懂、深入浅出、章节式的系统 C 语言教程。比我写得好多了。
  • 洛谷 - “入门与面试”题库 - 一些适合新手熟悉语言特性的练习题。
  • 必应国内版 - 有啥不懂记得多找搜索引擎。比百度好用得多得多。
  • AI?建议初学者慎用 AI 聊天机器人,因为 AI 写的程序通常不易于理解、修改。

致谢

  • 感谢 doublewestie 指出少打了个分号;
  • 感谢两位不愿透露姓名的朋友指出部分段落看不懂的问题。

次回予告

本教程可能会有下篇,计划讲解如下内容:

  • 函数
  • 变量的作用域
  • 数组
  • 字符和字符串

因为只有摸鱼时间才有动力写这种东西,所以不保证能够在有生之年发布。如果你想早点看到下篇,请和我一起希望 DUT 能够多一些水课(逃

本文由作者按照 CC BY 4.0 进行授权