02 | 第一个程序:教你输出彩色的文字

你好,我是胡光。从今天开始,我将带你正式进入 C 语言的编程世界。我们总会听到这样一种说法:兴趣是最好的老师。引起你对编程的兴趣,就是今天这讲的目的。如果你之前对于 C 语言的认知还停留在黑白纯色阶段的话,今天就让我们一起来进入一个彩色的 C 语言世界,来,跟我一起给它点儿颜色!

今日任务

先来看看今天这10分钟,我们要完成的任务。今天你将会学到如何设计一个读入字符串,并且按照字符串信息中规定的颜色,输出相应颜色文本内容的方法程序。

例如当我们:

  • 输入:red:color content, 输出红色的:color content
  • 输入:blue:color content,输出蓝色的:color content
  • 输入:yellow:color content,输出黄色的:color content

下面这张图呢,就是当你完成这个任务时,你程序应该有的一个输出效果:

那么想完成这个任务,我们都需要做哪些准备呢?又有哪些概念需要我们理解和掌握的?请你多点耐心,听我一步步讲。

必知必会,查缺补漏

1.输出函数:让计算机“说出话”

我还记得我当年学习C语言的时候,最兴奋的地方就是计算机可以根据我的“指令”打印出一句我想说的话来。这个功能虽然简单,但它也预示着我们可以初步“控制”计算机了。下面我附了一段代码,代码后面有相应的中文说明,你可以先看看。

#include <stdio.h> // 文件包含,之所以能使用 printf,跟这行代码有关

int main() {       // 主函数部分,整个程序的入口位置
    printf("hello my friend!"); // 打印函数
    return 0;      // 暂不介绍,不是重点
}

不知道你有没有理解这段代码,我多啰嗦两句。上面代码中,// 后面的内容属于注释,它是用来说明代码功能的,不属于程序部分,而且就算写在程序里面,也不会影响程序逻辑的正确性。

如果你在编译器中运行上面这段程序,就会看到如下一段输出:

hello my friend!

恭喜你,现在你已经成功与我们的计算机朋友打招呼了,这是一个好的开始,不是么?

printf 函数就是所谓的“输出函数”,现在你可以尝试在 printf 函数的双引号中间换一些其他内容,来试试效果了。但要注意,printf 后面的括号和双引号(且是英文输入法环境下)是必须要有的,其中双引号里面的内容才是最后程序输出的内容。至于为什么是这样,你也不用想,一开始,先死记住就可以了,或者换句话说,这就是规则。有些规则,本来就没有为什么。

我学习计算机的时候,我的老师就让我把上面的代码敲了N遍,最后都成了肌肉记忆。现在想想,也是这么回事。

上面那段代码,如果你玩够了的话,可以将 printf 函数换成下面的内容,看看输出结果:

printf("Hi, my friend:\n\tthis is the first day I know you.");

你所看到的输出内容,应该与下面这段内容相似:

Hi, my friend:
    this is the first day I know you.

我们看到有了换行效果,且第二行开头有了缩进。

你要是使用过Word的话,应该知道 Tab 键吧,对,\t 的效果就相当于在相应位置敲击了 Tab 键, \n 的效果就相当于敲击了换行。在C语言中,\t、\n都属于转义字符,还是和上面一样,它是C语言定义的规则,你也先不用问为什么,记住它,然后多用几次就可以了。下面这个表里,我还给出了一些更多的转义字符,你可以拿来玩一玩。

2.类型与变量:组织语言让计算机理解你的意思

现在我们来假设一个场景,在一片硝烟弥漫的战场上,你身处其中,需要将战况传回指挥部,以便指挥官做出下一步的战斗指示,你可能会将如下信息回传:

报告指挥官,敌军兵力大致如下:
重步兵:100人
坦克:4辆
火箭炮:6门
报告完毕,请总部做出下一步战斗指示!

这个场景中,100、4、6 都是对抽象的战场环境的客观且具体的数字描述。

现实世界就像这个战场一样,是抽象的,而计算机的世界是具体的、可计算的。从现在开始,你应该注意学习如何将现实世界的“信息”,映射到计算机世界中“数据”的技巧。

下面就来看现实生活中几种常见的信息,以及相应信息在计算机中的表示:

在上表中,金拱门有多少家,是一个整数,因为不可能出现0.5这样的半家,所以在计算机中表示为int的整数类型;巨人的身高则有零有整,所以在计算机中表示为float或double的浮点数类型;而一个人的名字就不能用数字类型来表示了,而是采用字符串类型来存储。

可以看到,我们说到的这几种基础数据类型,用来代表不同种类的信息。

在现实生活中,你可能会把各种信息记录在纸条上,或者本子的某个地方。在程序中,我们把这些信息,记录在一些叫做“变量”的东西里面。注意,类型和变量是两个完全不同的概念。

下面我们来看一个简单的变量示例:

#include <stdio.h>

int main() {
    int a = 167, b = 543;   //定义变量a,b
    printf("%d + %d = %d\n", a, b, a + b);
    return 0;
}

在上面的程序中,a、b就是变量,167、543就是数据。那么167、543这样的数据是什么类型呢?我们看到它们是整数,所以用的是int 。可以看到,我们定义了两个整数型变量a、b,并把数据167、543分别放到了变量a和变量b里,进而实现了程序目的。

所以,这里我划个重点,变量是用来存储数据的。你理解了吧?

上面例子中的 printf 函数虽然复杂一点儿,但其实简单来说就只有如下两部分:

  1. “%d + %d = %d\n”叫做格式控制字符串,其中 %d 是整型的“格式占位符”。
  2. a, b, a + b叫做参数列表,每一项依次对应一个“格式占位符”要输出的内容。

“格式占位符”与参数列表中的项一一对应,按照顺序,第一个%d与a对应,第二个%d与b对应,第三个%d与a+b对应。在输出内容时,会被替换成为对应项的内容。例如,上述程序的输出结果如下图所示:

你会看到,相应的 %d 被按顺序替换成了变量 a、变量 b以及表达式 a + b 的内容。

我们利用 printf 函数输出了二者加法表达式的值。至此,这个程序之于我们而言,已经具备了一个简单计算器的基本功能了。

下表是一些常用的“类型”与其“格式占位符”之间的对应关系,同样,还是不用问为什么,先试着去用,把它当做规则记住就可以了。

3.输入函数:让计算机“捡起”你的话

前面,我们已经看过了printf 函数的输出功能,它可以把程序中数据信息输出到屏幕上,这个屏幕,就是现在我们与程序交互的最基本的场所,以后你还会接触其他交互形式,但不急,慢慢来。

你可以把这个屏幕想象成一个桌子,你和计算机坐在桌子的两侧,当程序执行到 printf 的时候,计算机会把数据放到这个桌子上。那么这个时候,如果你往桌子上放了一些数据,计算机如何把这些数据信息“捡起来”呢?

看到这里,请在你的编译器中,输入如下程序:

#include <stdio.h>

int main() {
    int a, b; //定义变量a,b
    scanf("%d%d", &a, &b); // 输入函数
    printf("%d + %d = %d", a, b, a + b); //输出函数
    return 0;
}

代码的第5行中,有一个 scanf 函数,它会帮计算机把你放在屏幕上的数据“捡起来”,就像上文中说的“变量是用来存放数据的”,计算机会把捡起来的数据存储在 a、b 两个变量中。

注意:a、b 两个变量前面有一个特殊的符号&(取地址符),在这里暂不做过多解释。

当你运行上面这段程序后,可能会得到如下效果:

192 567
192 + 567 = 759

第一行是你放到屏幕上的信息,第二行是计算机放到屏幕上的信息。

有了输入函数以后,面对每次不同的计算需求,就不需要修改程序代码了,我们可以直接在屏幕上输入两个需要计算的数据,计算机就会给我们一个满意的答案。

最后我们来看看输入输出函数的返回值:

  • printf 函数的返回值,代表向屏幕上打印了多少个字符。
  • scanf 函数的返回值,代表成功给多少个变量赋了值(后续再展开讲)。

一起动手,搞事情

下面我给出两道思考题,希望你能尽量自己动手查资料解决。

以后,基本每堂课我都会留一些你抬抬脚就能解决的问题,不太简单,可绝不会难上天。我尽量控制,也欢迎你在专栏下面留下意见和建议,更欢迎你将思考过程留在专栏下面。

思考题(1):位数输出

计算一个输入整数的十进制表示的位数?
条件1:允许有多余输出的情况下,怎么实现?
条件2:只允许输出数字位数的时候,又该怎么实现?
请单纯考虑使用 printf 一族函数方法实现。

思考题(2):读入一行字符串

请大家自行实现一个读入一行字符串,并且输出相关内容的程序,思考如下:
条件1:如果字符串中没有空格,怎么实现?
条件2:如果字符串中有空格,又该怎么实现?
请单纯考虑用 scanf 一族函数方法实现。

“hello world”显示什么颜色,你做主

前面我们讲了如何使用输出函数让计算机显示什么,又讲了如何利用类型与变量等组织一句计算机可以理解的话语,以及如何让计算机接收到你想传达信息的渠道。接下来,就回到我们今天的任务:按照字符串信息中规定的颜色,输出相应颜色文本内容的方法程序。

在我讲输出函数的时候,提到转义字符,其中有一个转义字符就是用来操作颜色的,它就是:\033。下面就让我们具体看一下,它是如何工作的。

设置颜色,以 \033 开始,也以 \033 结束,这种首尾呼应的结构对记忆比较友好。具体格式如下:

格式:\033[属性代码{;属性代码...}m输出内容\033[0m

我们来介绍几个属性代码,并加以使用:

  • 0 代表关闭所有属性
  • 1 代表高亮/粗体
  • 4 代表下划线
  • 33 代表黄色

如果你在你的 Linux 环境中输入如下代码:

#include <stdio.h>

int main() {
    printf("\033[1;4;33mhello color\033[0m");
    return 0;  
}

运行以后,你就会在终端看到一行高亮且带有下划线的“hello color”字符串。如下图所示:

至此,我们就准备好了完成课程任务的所有基本元素了,下面,就请你自行尝试一下本任务吧,即使做不出来,也不用担心,我会上传参考代码。

参考代码中,会涉及一些我们后续才会学到的编码技巧,你暂时看不懂也没关系,只需要欣赏它就好了。毕竟,想要进入一个行业的前提,是要懂得这个行业的审美标准。

课程小结

今天是我们第一次真正接触C语言,所涉及的专业词汇可能有点多,你可能看完后对一些概念也是分不太清楚,但不要担心,当你接触的多了,这些术语渐渐都会清晰明白。下面呢,我来给你总结以下今天的重点内容:

  1. printf 函数是用来输出内容的方法,包含了格式控制字符串和参数列表两部分。
  2. 类型和变量是两个完全不同的概念,变量是用来存储数据的。
  3. 使用格式占位符的时候,需要对应到相关类型,整型对应到 %d,字符型对应到 %c,浮点型对应到 %f,双精度浮点型对应到 %lf。

总之,今天这堂课你已经知道如何和计算机打招呼,以及如何让计算机“听”你说的话了。

我是胡光,这是我带你第一次接触C语言,你还有什么疑惑或其他想知道的,我们留言区见。

精选留言

  • 1043

    2020-03-29 21:58:10

    首先,别管计算结果是什么?再好的程序最终都是要显示结果的,把输出什么先看到,所见即所得,会让初学者有种即时满足感,有种什么都能控制的“全能自恋”感。无法验证和反馈就没法刻意练习,不能刻意练习那不管练多久都不能成为大师。有大师指导不做针对训练和刻意练习那就是对这么好课程的暴遣天物。
  • rocedu

    2020-01-22 08:00:06

    linux下可以用echo测试彩色字符串对不对:echo -e "\033[文字背景颜色;文字颜色m 你要显示的内容 \033[0m"
    作者回复

    d(^_^o)

    2020-01-22 23:02:28

  • 贵州小欧哥

    2020-05-01 00:02:15

    #include <stdio.h>
    int main() {
    int a, b; //定义变量a,b
    scanf("%d%d", &a, &b); // 输入函数
    printf("%d + %d = %d", a, b, a + b); //输出函数
    return 0;
    }

    复制运行这段 怎么都得不到想要的结果呢?

    救助
    作者回复

    你用的什么编译器?DEVCPP 么?不出结果是编译错误么?
    当你运行这个程序以后,会跳出来一个黑色的框框,我们叫做命令行,你在命令行里面输入两个整数,两个整数之间用空格隔开,然后回车,就能看到结果了。

    2020-05-05 14:15:38

  • 罗耀龙@坐忘

    2020-04-12 16:47:50

    茶艺师学编程

    老师喊着“来,我们给电脑一点颜色看”,但现实是电脑给我颜色看······

    1、就是颜色部分,我自己就是跑不出来,是不是我这里是windows环境的原因?

    2、作业,我做了一天······
    ①我的思路一开始是去查找print,和scanf的函数,看没有能实现题目要求的。但是无功而返。
    ②后来才想到,依靠计算函数啊·······,还有就是scanf居然是遇到空格键就收工的家伙······

    第一题作业
    #include<stdio.h>
    #include<math.h> //这里要加上这个,不然下面的Log10()+1就运算不了
    int main()
    {
    int n;
    printf("你准备让电脑算什么数?\n");
    scanf("%d",&n);
    printf("电脑算出来了,%d的位数是%d\n",n,(int)log10(n)+1); //把一个数字取log10的整数值再加1,就是这个数字的位数了,(int)就是取整运算
    } //这里最大只能运算位数为10的数字,超过了就异常。但在这之前,输入的数要小于等于2147483647,不然就算是位数计算正确,但是文本显示会出现异常。虽然这个BUG是能直接改的···

    第二题的作业
    #include <stdio.h>

    int main() {
    char a[80]; //这里我认为一个空格键能把字符串一分为二,换句话说就是一个字符串分为两个。空格键分得越多,字符串越多
    char a2[80]; //而scanf函数捡到空格,就以为捡完了 。得这样让它继续捡
    char a3[80]; // 我在这里假设不会有人输入一段字符串要断它超过3次(除非这家伙故意找茬)
    printf("你想给电脑说什么?\n(空格键请控制在3个以内)\n");
    scanf("%s%s%s",&a,&a2,&a3);
    printf("\n\t%s%s %s %s","电脑看懂了,然后写回给你看:",a,a2,a3); //prinf函数输出是不含空格的,除非是自己再补空格进去。但这又问题,在这里补的空格数和在输入字符串断一次连打的空格键数有可能不一样
    return 0;
    }
    作者回复

    1、对的,是环境问题。windows的cmd下,想输出颜色,需要调用windows相关系统函数。这就是另外一个美妙的故事了。

    2、你能积极的思考,着实不错!d(^_^o),可以在后面看看我给的参考答案,⁄(⁄ ⁄ ⁄ω⁄ ⁄ ⁄)⁄

    2020-04-13 09:10:15

  • 黎鲤

    2022-03-05 10:41:44

    唉,终究还是吃了数学的亏,我居然不知道求一个数的位数是用取对数值再加一
  • Geek_58e91d

    2020-06-30 18:10:40

    这我这纯新手来说第二节课的思考题也太难了,题目都理解不了,小学的数学概念都分不清的水平。评论区的一些答案就算照抄也一头雾水,老师能推荐个数学入门书吗?建议前几节课程别搞这种思考题,学习顺畅点好。🙄
    作者回复

    -_-||| 数学入门书,真的不知道怎么推荐。

    2020-07-11 00:43:40

  • 郑煜丰

    2020-04-01 20:07:46

    分享一下我今天的经历:作为小白,完全菜鸟的我都是百度出来才把哈喽朋友做好并且运行成功,中间失败了好几次,疑惑很久,百度几十次,模仿别人,试验,再检查,模仿,最后才成功,开心快乐
    作者回复

    d(^_^o)牛!

    2020-04-02 10:10:11

  • 落曦

    2020-03-25 19:55:02


    //第一个思考题
    #include <stdio.h>

    int main() {
    int a;
    printf("请输入一个十进制整数");
    scanf("%d",&a);
    printf("请输出这个十进制的位数");
    int count=0;
    while(a!=0)
    {
    a/=10;
    count++;
    }
    printf ("%d",count);
    return 0;
    }

    //第二个思考题
    #include <stdio.h>

    int main() {
    char a[10];
    printf("请输入一行字符串");
    int i;
    for(i=0;i<10;i++)
    scanf("%d",&a[i]);//字符串可以有空格
    scanf("%s",&a);//字符串无空格
    return 0;
    }

    老师讲的很基础,值得学习
    作者回复

    第二个思考题的代码中:
    1. 试试不使用 for 循环
    2. 读入时使用了错误的格式占位符%d,应该是%c
    3. 不能假定读入的一行字符串,字符数量就是 10 个

    后面有参考答案,你可以先试着自己解决一下这三个问题,然后再看看参考答案。

    ^_^ 加油!!!

    2020-03-26 02:21:45

  • 2020-01-11 11:52:45

    老师…什么是多余输出啊…第一题两个条件不太看得懂
    作者回复

    哈哈哈,你看文章的留言区中已经有人给出了自己的答案,并且已经很接近标准答案了。你先看一看。

    2020-01-12 01:49:47

  • 潮汐

    2020-01-10 22:37:18

    现实世界就像这个战场一样,是抽象的,而计算机的世界是具体的、可计算的。从现在开始,你应该注意学习如何将现实世界的“信息”,映射到计算机世界中“数据”的技巧。
    点赞!
  • 徐洲更

    2020-01-07 12:13:07

    \033这类打印颜色的方式是shell终端的解读格式,而非windows系统下。
    作者回复

    对的,windows的话可以调用API实现颜色功能,这个要是有一定基础的话,可以自行查阅资料试一下。

    2020-01-07 15:08:27

  • Yaohong

    2022-03-16 14:26:00

    请问老师:思考题计算一个输入整数的十进制表示的位数?条件 1:允许有多余输出的情况下,怎么实现? 多余输出是什么意思
  • 信念

    2020-01-15 22:38:38

    $ vim test.cpp
    #include <stdio.h>

    int main() {
    int a=192, b=567; // dingyi bianliang a,b
    scanf("%d%d", &a, &b);//shuruhanshu
    printf("%d + %d = %d",a,b,a+b);// shuchuhanshu
    return 0;
    $ gcc test.cpp
    $ ./a.out
    想请问一下,为什么我在Linux下进行完上面的操作后,并没有在屏幕上打出
    192 567
    192 + 567 = 759
    而仅仅是换了一行,让我不得不用CTRL+c来停止进程如下:
    $ ./a.out
    ^C
    作者回复

    192 567是你的输入,然后按下回车即可。

    2020-01-16 22:45:29

  • 学写代码的猪

    2020-01-08 00:16:11

    通过百度完成二道思考题,对格式化的输入输出,又有了新的认识。
    思考2:
    条件 1:如果字符串中没有空格,怎么实现?:%s
    条件 2:如果字符串中有空格,又该怎么实现?:%[^\n] ,原来scanf 还支持正则表达式。
    思考1:
    条件 1:允许有多余输出的情况下,实现方式:printf("=%d\n", printf("len(%lld)", a)-5);
    条件 2:只允许输出数字位数的时候,又该怎么实现?重定向?还没想到。
    作者回复

    d(^_^o),非常棒!
    只输出数字位数的话 fprintf想一想。

    2020-01-08 06:31:42

  • 2020-01-07 23:53:43

    不能回复老师的评论,我就重新写了
    Mac打印字符串使用\a 没有声音
    作者回复: 你用的什么环境?

    环境为: 编译环境 gcc: Apple LLVM version 10.0.1 (clang-1001.0.46.4)
    系统:Mac OS 10.15.1
    作者回复

    没啥问题

    2020-01-08 07:54:46

  • 2020-01-07 21:46:29

    为什么用printf打印字符串后面会多一个%号呢?
    作者回复

    我看看你的代码?

    2020-01-07 23:15:16

  • 2020-01-07 20:51:19

    Mac打印字符串使用\a 没有声音
    作者回复

    你用的什么环境?

    2020-01-07 23:15:40

  • 行问

    2020-01-07 08:16:44

    #include <stdio.h>

    int main() {
    printf("\033[1;4;33mhello color\033[0m");
    return 0;
    }

    (Windows 系统,没有钱买 Mac ……)这段代码的输出:hello color
    作者回复

    装个虚拟机Ubuntu、CentOS或者DeepIn系统,或者使用windows 10里面的bash Ubuntu都行。^_^

    2020-01-07 13:36:28

  • Geek_2f2fd5

    2024-08-31 17:20:05

    思考题会有答案吗
  • Geek_a20b8d

    2022-12-18 15:01:57


    老师:格式:\033[属性代码{;属性代码...}m输出内容\033[0m,这个也适用于window吗?