C语言学习之旅
本系列文章注重于让读者快速熟练如何使用C语言以及了解C语言的特性,而不做更深的探讨。
第一章 初识C语言
文章目录
前言
本章将讲述以下内容:
- 什么是C语言?
- 编写C语言程序的步骤
- C语言的编译器与链接器
提示:以下是本篇文章正文内容
一、C语言是什么?
欢迎来到C语言的世界。C是一门功能强大的专业化编程语言,深受业余编程爱好者和专业程序员的喜爱。
C语言是一门面向过程的计算机编程语言。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
对于初学者而言,你可能现在并不知道什么是“面向过程”或是“机器码”、“存储器”。但是没有关系,这并不影响你使用它编写简单的程序。
二、编写C语言的步骤

为了让读者对编程有大概的了解,我们在理想状态下,把编写C语言分解为7个步骤:
- 定义程序目标
- 设计程序
- 编写代码
- 编译
- 运行程序
- 测试和调试程序
- 维护和修改程序
1.定义程序的目标
在动手写程序之前,要在脑中有清晰的思路。想要程序去做什么之前,首先你要明确自己的目标是什么,思考你的程序需要哪些信息(或输入数据),要进行哪些计算和控制流程,以及程序应该要报告(输出)什么信息。
在这一步骤中,不涉及具体的计算机语言,应该用一般的术语来描述你需要如何解决问题。
例如对于下面的问题:
对于给定的n,求n的阶乘n!
样例输入:
5
样例输出:
120
目标很明确:求得n!。为了达成此目标,程序需要接受给定的值n,输出最后的结果n!。
2.设计程序
对程序应该完成什么任务有概念性的认识后,就应该考虑如何用程序来完成它。
除此之外,还要决定在程序(还可能是辅助文件)中如何表示数据,以及用什么方法处理数据。学习C语言之初,遇到的问题都很简单,没什么可选的。但是,随着要处理的情况越来越复杂,需要决策和考虑的方面也越来越多。通常,选择一个合适的方式表示信息可以更容易地设计程序和处理数据。
对于步骤1中所提到的题目,我们对于程序的设计应该是这样的:
读取输入的数据n ;
计算n的阶乘n! ;
输出n! ;
3.编写代码
设计好程序后,就可以编写代码来实现它。也就是说,把你设计的程序翻译成 C语言。这里是真正需要使用C语言的地方。可以把思路写在纸上,但是最终还是要把代码输入计算机。
对于上述求n!的题目,这里是其代码:
程序清单1.1
#include <stdio.h>
int main()
{
int n ;
int ans = 1 ;
scanf("%d",&n) ; // 这里从输入流中读取 n
for( int i=1 ; i<=n ; i++ )
{
ans = ans * i ; // 这里计算 n 的阶乘并将其储存在ans中
}
printf("%d",ans) ; // 这里输出最终答案 ans
rerturn 0 ;
}
作为初学者,你目前一定对这段代码充满疑惑:“for是什么”,“scanf和printf是什么”······诸如此类。但是没有关系,目前你并不需要理解这段程序每一行语句的含义。
程序清单1.1只是向你展示了一个C语言程序如何从程序设计转换到代码——即从流程逻辑转换到程序实现。
4.编译
接下来的这一步是编译源代码。
在介绍编译前,我们不得不先提到编译器的概念:编译器是把源代码转换成可执行代码的程序。
可执行代码是用计算机的机器语言表示的代码。这种语言由数字码表示的指令组成。C 编译器负责把C代码翻译成特定的机器语言。
像目前你所熟知的程序设计语言,例如C/C++、Java、Python等都是高级语言。这里你可以简单的理解为便于人类理解、编写的语言。而计算机所识别的语言则是机器语言,它并不能直接理解你用C语言或者是其他的高级语言写出来的程序,就像是你没有办法用英语和一个听不懂英语的野兽交流一样。
而编译器的作用就是把你所编写的高级语言翻译成机器能够看懂的机器语言,充当了“翻译官”的职能。
不仅仅如此,编译器还会检查C语言程序是否有效(是否出现语法错误或是逻辑错误、算数错误等)。如果C编译器发现错误,就不生成可执行文件并报错。
但是编译器可能并不会告诉你具体的错误原因,因此通过错误信息检查出程序的bug也是一个程序设计者的基本功。
5.运行程序
这一步没什么好说的。运行你的程序源代码在编译后得到的.exe格式的文件,并且进行操作。
6.测试和调试程序
程序能运行是个好迹象,但有时也可能会出现运行错误。接下来,应该检查程序是否按照你所设计的思路运行。你会发现你的程序中有一些错误,也就是bug。查找并修复程序错误的过程叫调试。
你可能会犯基本的设计错误,可能错误地实现了一个好想法,可能忽视了输入检查导致程序瘫痪,可能会把圆括号放错地方,可能误用 C语言或打错字,等等。
也可能是输入的数据超过了你的程序的承受能力。例如对于求n!的程序而言,如果你把n设定为1010,其结果远远超过了类型为int的ans变量能够储存的上限(发生了算术溢出),因此程序最后会输出错误的结果。
一个程序可能会出的错误远不止这些,但也不必为自己出错苦恼——一个程序本身就是在不断出错和调试的过程中完善的。即便是最老练的程序员也不敢保证自己写的每一个程序都没有任何错误。
7.维护和修改代码
创建完程序后,你发现程序有错,或者想扩展程序的用途,这时就要修改程序。例如,用户输入某个数据时程序的运行结果出现错误、或者你想到了一个更好的解决方案、想添加一个更好的新特性,或者要修改程序使其能在不同的计算机系统中运行,等等。
三、编程机制
虽然我们不必详细了解计算机内部如何运行C程序,但是,了解C语言的编程机制不仅能丰富编程相关的背景知识,还有助于理解为何要经过一些特殊的步骤才能得到C程序。
用C语言编写程序时,编写的内容被储存在文本文件中,该文件被称为源代码文件(source code file)。大部分C系统,包括之前提到的,都要求文件名以.c结尾。
在文件名中,点号(.)前面的部分称为基本名(basename),点号后面的部分称为扩展名(extension)。基本名可以是任何系统允许的名称,它并不会对文件本身产生任何影响。而拓展名决定了这个文件的类型。
接下来,我们来看一下具体的应用,假设有一个名为concrete.c的源文件,其中的C源代码如程序清单1.2所示。
程序清单1.2
#include <stdio.h>
int main()
{
printf("Concrete contains gravel and cement.\n") ;
return 0;
}
目标代码、可执行文件与库
C编程的基本策略是,用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码)。
典型的C实现通过编译和链接两个步骤来完成这一过程。
编译器把源代码转换成中间代码,链接器把中间代码和其他代码合并,生成可执行文件。C 使用这种分而治之的方法方便对程序进行模块化,可以独立编译单独的模块,稍后再用链接器合并已编译的模块。
库:可以将其理解为一种可装卸的工具箱,不同的库里储存着不同的工具(库函数),当你使用预处理指令#include调用了某个库时,你就可以使用库中的函数。
几乎所有的C程序都要使用C标准库中的函数。例如,concrete.c中就使用了 printf()函数。目标代码文件并不包含该函数的代码,它只包含了使用 printf()函数的指令。printf()函数真正的代码储存在另一个被称为库的文件中。库文件中有许多函数的目标代码。
链接器的作用是,把你编写的目标代码、系统的标准启动代码和库代码这 3 部分合并成一个文件,即可执行文件。对于库代码,链接器只会把程序中要用到的库函数代码提取出来。

四、总结
本章总结
- C是强大而简洁的编程语言。它之所以流行,在于自身提供大量的实用编程工具,能很好地控制硬件。而且,与大多数其他程序相比,C程序更容易从一个系统移植到另一个系统。
- C是编译型语言。C编译器和链接器是把C语言源代码转换成可执行代码的程序。
五、复习题
- 编程的7个主要步骤是什么?
- 编译器和链接器的作用是什么?
- 解释源代码文件、目标代码文件和可执行文件有什么区别?
答案:
定义程序目的 -> 设计程序 -> 编写代码 -> 编译 -> 运行程序 -> 测试与调试程序 -> 维护与修改程序
编译器把源代码转换成中间代码;链接器把中间代码和其他代码合并,生成可执行文件
源代码: 编码文件源码, C++中是 .cpp 扩展名文件、 C 中则是 .c 文件。
目标文件: 通过编译器编译命令生成的中间文件,通常是 .o 文件。
可执行文件: 通过目标文件直接生成的可被计算机直接运行的文件
参考资料:
- [美]史蒂芬·普拉达.《C primer Plus 第六版》 [M].北京:人民邮电出版社.北京,2019