C 语言零基础入门教程

本 C 语言零基础入门教程是面向 C 语言零基础读者朋友的入门教程。具有其他高级语言或 汇编语言 的编程经验会很有帮助,不过这些经验对于会用计算机的读者来说并不是必需的。此 C 语言零基础入门教程将让你对 C 语言有足够的认识,从而提升您自己的专业知识水平。

一、C 语言概述

什么是 C 语言?它是 20 世纪 70 年代初期在贝尔实验室开发出来的一种广为使用的编程语言。这一简单回答显然没能传达出 C 语言的特别之处。不过别急,在深入学习这门语言之前,让我们先来回顾一下 C 语言的起源、设计目标和这么多年来的发展(C 语言的历史)。我们还将讨论 C 语言的优缺点,以及如何高效地使用 C 语言(C 语言的优缺点)。

二、C 语言基本概念

这个部分的 C 语言教程介绍了 C 语言的一些基本概念,包括预处理指令、函数、变量和语句。即使是编写最简单的 C 程序,也会用到这些基本概念。后续几个部分将会更详细地描述这些概念。

首先,编写一个简单的 C 程序 给出一个简单的 C 程序,并且描述了如何对这个程序进行编译和链接。接着,一个简单的 C 程的序的一般形式 讨论如何使程序通用。C 语言注释简介 介绍如何添加说明性解释,即通常所说的注释。C 语言变量和赋值简介 介绍变量,变量用来存储程序执行过程中可能发生改变的数据。C 语言使用 scanf 读入输入 说明利用 scanf 函数把数据读入变量的方法。就如 C 语言定义常量的名字 介绍的那样,常量是程序执行过程中不会发生改变的数据,用户可以对其进行命名。最后,C 语言标识符简介 解释 C 语言的命名(标识符)规则,C 语言程序的书写规范 给出 C 程序的布局规范。

三、格式化输入/输出

scanf 函数和 printf 函数是 C 语言编程中使用得很频繁的两个函数,它们用来格式化输入和输出。正如这个部分要展示的那样,虽然这两个函数功能强大,用好它们却不容易。C 语言使用 printf 函数格式化输出 描述 printf 函数,C 语言使用 scanf 函数格式化输入 介绍 scanf 函数。

四、表达式

C 语言的一个特点就是它更多地强调表达式而不是语句。表达式是表示如何计算值的公式。最简单的表达式是变量和常量。变量表示程序运行时需要计算的值,常量表示不变的值,更加复杂的表达式把运算符用于操作数(操作数自身就是表达式)。在表达式 a+(b*c) 中,运算符 + 用于操作数 a(b*c),而这两者自身又都是表达式。

运算符是构建表达式的基本工具,C 语言拥有异常丰富的运算符。首先,C 语言提供了基本运算符,这类运算符存在于大多数编程语言中。

  • 算术运算符,包括加、减、乘和除。
  • 关系运算符进行诸如“i0”这样的比较运算。
  • 逻辑运算符实现诸如“i0并且 i10 小”这样的关系运算。

但是 C 语言不只包括这些运算符,还提供了许多其他运算符。事实上,运算符非常多,我们需要在本 C 语言教程的前 20 个部分中逐步进行介绍。虽然掌握如此众多的运算符可能是一件非常烦琐的事,但这对于成为 C 语言专家是特别重要的。

这个部分将涵盖一些 C 语言中最基础的运算符:算术运算符(C 语言算术运算符简介)、赋值运算符(C 语言赋值运算符简介)和自增及自减运算符(C 语言自增运算符(++)和自减运算符(–))。C 语言算术运算符简介 除了讨论算术运算符外,还解释了运算符的优先级和结合性,这两个特性对含有多个运算符的表达式而言非常重要。C 语言表达式求值 描述 C 语言表达式的求值方法。最后,C 语言表达式语句 介绍表达式语句,即一种允许把任何表达式都当作语句来使用的特性。

五、选择语句

尽管 C 语言有许多运算符,但是它所拥有的语句相对较少。到目前为止,我们只见过两种语句:return 语句(一个简单的 C 程的序的一般形式)和表达式语句(C 语言表达式语句)。根据对语句执行顺序的影响,C 语言的其余语句大多属于以下 3 类。

  • 选择语句(selection statement)。if 语句和 switch 语句允许程序在一组可选项中选择一条特定的执行路径。
  • 重复语句(iteration statement)。while 语句、do 语句和 for 语句支持重复(循环)操作。
  • 跳转语句(jump statement)。break 语句、continue 语句和 goto 语句导致无条件地跳转到程序中的某个位置。(return 语句也属于此类。)

C 语言还有其他两类语句,一类是复合语句(把几条语句组合成一条语句),一类是空语句(不执行任何操作)。

这个部分的 C 语言教程讨论选择语句和复合语句。在使用 if 语句之前,我们需要介绍逻辑表达式:if 语句可以测试的条件。C 语言逻辑表达式 说明如何用关系运算符(<<=>>=)、判等运算符(==!=)和逻辑运算符(&&||!)构造逻辑表达式。C 语言 if 语句简介 介绍 if 语句和复合语句,以及可以在一个表达式内测试条件的条件运算符(?:)。C 语言 switch 语句简介 描述 switch 语句。

六、循环

上一个部分介绍了 C 语言的选择语句:if 语句和 switch 语句。这个部分的 C 语言教程将介绍 C 语言的重复语句,这种语句允许用户设置循环。

循环(loop)是重复执行其他语句(循环体)的一种语句。在 C 语言中,每个循环都有一个控制表达式(controlling expression)。每次执行循环体(循环重复一次)时都要对控制表达式求值。如果表达式为真(即值不为零),那么继续执行循环。

C 语言提供了 3 种重复语句,即 while 语句、do 语句和 for 语句,我们将在 C 语言 while 语句简介C 语言 do 语句简介C 语言 for 语句简介 分别介绍。while 循环在循环体执行之前测试控制表达式,do 循环在循环体执行之后测试控制表达式,for 语句则非常适合那些递增或递减计数变量的循环。C 语言 for 语句简介 还介绍了主要用于 for 语句的逗号运算符。

这个部分的最后两篇致力于讨论与循环相关的 C 语言特性。C 语言使用 break、continue 和 goto 退出循环 描述了 break 语句、continue 语句和 goto 语句。break 语句用来跳出循环并把程序控制传递到循环后的下一条语句,continue 语句用来跳过本次循环的剩余部分,而 goto 语句则可以跳到函数内的任何语句上。C 语言空语句简介 介绍空语句,它可以用于构造循环体为空的循环。

七、基本类型

到目前为止,本 C 语言教程只使用了 C 语言的两种基本(内置的)类型intfloat。(我们还见过 _Bool,那是 C99 中的一种基本类型。)这个部分讲述其余的基本类型,并从总体上讨论了与类型有关的重要问题。C 语言整数类型简介 展示整数类型的取值范围,包括长整型、短整型和无符号整型。C 语言浮点类型简介 介绍 double 类型和 long double 类型,这些类型提供了更大的取值范围和比 float 类型更高的精度。C 语言字符类型简介 讨论 char(字符)类型,这种类型用于字符数据的处理。C 语言类型转换简介 解决重要的类型转换问题,即把一种类型的值转换成另外一种类型的等价值。C 语言类型定义简介 展示利用 typedef 定义新类型名的方法。最后,C 语言 sizeof 运算符 描述 sizeof 运算符,这种运算符用来计算一种类型需要的存储空间大小。

八、数组

到目前为止,我们所见的变量都只是标量(scalar):标量具有保存单一数据项的能力。C 语言也支持聚合(aggregate)变量,这类变量可以存储成组的数值。在 C 语言中一共有两种聚合类型:数组(array)和结构(structure)。本 C 语言教程介绍一维数组(C 语言一维数组简介)和多维数组(C 语言多维数组简介)的声明与使用。C 语言变长数组简介 讨论了 C99 中的变长数组。本 C 语言教程主要讨论一维数组,因为与多维数组相比,一维数组在 C 语言中占有更加重要的角色。

九、函数

在第 2 部分的 C 语言教程中我们已经知道,函数简单来说就是一连串语句,这些语句被组合在一起,并被指定了一个名字。虽然“函数”这个术语来自数学,但是 C 语言的函数不完全等同于数学函数。在 C 语言中,函数不一定要有参数,也不一定要计算数值。(在某些编程语言中,“函数”需要返回一个值,而“过程”不返回值。C 语言没有这样的区别。)

函数是 C 程序的构建块。每个函数本质上是一个自带声明和语句的小程序。可以利用函数把程序划分成小块,这样便于人们理解和修改程序。由于不必重复编写要多次使用的代码,函数可以使编程不那么单调乏味。此外,函数可以复用:一个函数最初可能是某个程序的一部分,但可以将其用于其他程序。

到目前为止,我们的程序都是只由一个 main 函数构成的。这个部分的 C 语言教程将学习如何编写除 main 函数以外的其他函数,并更加深入地了解 main 函数本身。C 语言函数的定义和调用 介绍定义和调用函数的方法;C 语言函数声明 讨论函数的声明,以及它和函数定义的差异;接下来,C 语言函数中的形式参数和实际参数 讲述参数是怎么传递给函数的。余下的部分讨论 return 语句(C 语言 return 语句简介)、与程序终止相关的问题(C 语言如何终止程序)和递归(C 语言使用递归实现快速排序算法)。

十、程序结构

第 9 部分已经介绍过函数了,因此这个部分的 C 语言教程就来讨论一个程序包含多个函数时所产生的几个问题。前两篇讨论局部变量(C 语言局部变量简介)和外部变量(C 语言外部变量简介)之间的差异,C 语言中的程序块 考虑程序块(含有声明的复合语句)问题,C 语言中的作用域 解决用于局部名、外部名和在程序块中声明的名字的作用域规则问题,如何构建一个 C 语言程序 介绍用来组织函数原型、函数定义、变量声明和程序其他部分的方法。

十一、指针

指针是 C 语言最重要——也是最常被误解——的特性之一。由于指针的重要性,本 C 语言教程 将用 3 个部分的篇幅对其进行讨论。

这个部分的 C 语言教程将从内存地址及其与指针变量的关系入手(C 语言指针变量简介),然后 C 语言取地址运算符和间接寻址运算符 介绍取地址运算符和间接寻址运算符,C 语言指针赋值 探讨指针赋值的内容,C 语言指针作为参数 说明给函数传递指针的方法,而 C 语言指针作为返回值 则讨论从函数返回指针。

十二、指针和数组

第 11 部分的 C 语言教程介绍了指针,并且说明了如何把指针用作函数的实际参数和函数的返回值。这个部分介绍指针的另一种应用。当指针指向数组元素时,C 语言允许对指针进行算术运算(加法和减法),通过这种运算我们可以用指针代替数组下标对数组进行处理。

正如这个部分的 C 语言教程将介绍的那样,C 语言中指针和数组的关系是非常紧密的。理解指针和数组之间的关系对于熟练掌握 C 语言非常关键:它能使我们深入了解 C 语言的设计过程,并且能够帮助我们理解现有的程序。然而,需要知道的是,用指针处理数组的主要原因是效率,但是这里的效率提升已经不再像当初那么重要了,这主要归功于编译器的改进。

C 语言指针的算术运算 讨论指针的算术运算,并且说明如何使用关系运算符和判等运算符进行指针的比较;C 语言指针用于数组处理 示范如何用指针处理数组元素;C 语言用数组名作为指针 揭示了一个关于数组的重要事实(即可以用数组的名字作为指向数组中第一个元素的指针),并且利用这个事实说明了数组型实际参数的真实工作机制;C 语言指针和多维数组 讲解前 3 篇的主题对于多维数组的应用;最后的 C 语言 C99 中的指针和变长数组 介绍指针和变长数组之间的关系(C99 的特性)。

十三、字符串

前面几个部分的 C 语言教程虽然使用过 char 类型变量和 char 类型数组,但我们始终没有谈到处理字符序列(C 语言的术语是字符串)的便捷方法。这个部分的 C 语言教程就来补上这一方面的内容,并将介绍字符串常量(在 C 标准中称为字面串)和字符串变量。其中,字符串变量可以在程序运行过程中发生改变。

C 语言字符串字面量简介 介绍有关字面串的规则,包括如何在字面串中嵌入转义序列,以及如何分割较长的字面串。C 语言字符串变量简介 讲解声明字符串变量的方法,字符串变量其实就是字符数组,不过末尾要加上一个特殊的空字符来标示字符串的末尾。C 语言字符串的读和写 描述了读/写字符串的方法。C 语言访问字符串中的字符 讨论用来处理字符串的函数的编写方法。如何使用 C 语言的字符串库 涵盖了一些 C 语言函数库中处理字符串的函数。C 语言处理字符串常用的方式 介绍处理字符串时经常会采用的惯用法。C 语言字符串数组简介 描述如何创建这样的数组:其元素是指向不同长度字符串的指针。这一节还会说明 C 语言如何使用这种数组为程序提供命令行支持。

十四、预处理器

前面几个部分的 C 语言教程用到过 #define#include 指令,但没有深入讨论。这些指令(以及我们还没有学到的指令)都是由预处理器处理的。预处理器是一个小软件,它可以在编译前处理 C 程序。C 语言(和 C++ 语言)因为依赖预处理器而不同于其他的编程语言。

预处理器是一种强大的工具,但它同时也可能是许多难以发现的错误的根源。此外,预处理器也可能被错误地用来编写出一些几乎不可能读懂的程序。尽管有些 C 程序员十分依赖于预处理器,我依然建议适度地使用它,就像生活中的其他许多事物一样。

这个部分的 C 语言教程首先描述预处理器的工作原理(C 语言预处理器的工作原理),并且给出一些会影响预处理指令(C 语言预处理指令简介)的通用规则。C 语言宏定义简介C 语言条件编译简介 介绍预处理器最主要的两种能力:宏定义和条件编译。C 语言中一些不常用的指令 讨论较少用到的预处理指令:#error#line#pragma

十五、编写大型程序

虽然某些 C 程序小得足够放入一个单独的文件中,但是大多数程序不是这样的。程序由多个文件构成的原则更容易让人接受。你将在这个部分的 C 语言教程中看到,常见的程序由多个源文件(source file)组成,通常还有一些头文件(header file)。源文件包含函数的定义和外部变量,而头文件包含可以在源文件之间共享的信息。C 语言源文件简介 讨论源文件,C 语言头文件简介 详细地介绍头文件,C 语言把程序划分成多个文件 描述把程序分割成源文件和头文件的方法,C 语言构建多文件程序 说明如何“构建”(即编译和链接)由多个文件组成的程序,以及在改变程序的部分内容后如何“重新构建”。

十六、结构、联合和枚举

这个部分的 C 语言教程介绍 3 种新的类型:结构、联合和枚举。结构是可能具有不同类型的值(成员)的集合。联合和结构很类似,不同之处在于联合的成员共享同一存储空间。这样的结果是,联合可以每次存储一个成员,但是无法同时存储全部成员。枚举是一种整数类型,它的值由程序员来命名。

在这 3 种类型中,结构是到目前为止最重要的一种,所以本章的大部分内容是关于结构的。C 语言结构变量简介 说明了如何声明结构变量,以及如何对其进行基本操作。随后,C 语言结构类型简介 解释了定义结构类型的方法,借助结构类型,我们就可以编写接受结构类型参数或返回结构的函数。C 语言嵌套的数组和结构 探讨如何实现数组和结构的嵌套。这个部分的 C 语言教程的最后两节分别讨论了联合(C 语言联合简介)和枚举(C 语言枚举简介)。

十七、指针的高级应用

前面几部分的 C 语言教程描述了指针的两种重要应用。第 11 部分的 C 语言教程说明了如何利用指向变量的指针作为函数的参数,从而允许函数修改该变量。第 12 部分的 C 语言教程说明了如何对指向数组元素的指针进行算术运算来处理数组。这个部分的 C 语言教程则通过观察另外两种应用来完善指针的内容:动态存储分配和指向函数的指针。

通过使用动态存储分配,程序可以在执行期间获得需要的内存块。C 语言动态存储分配 解释动态存储分配的基本概念。C 语言动态分配字符串 讨论动态分配字符串,这比通常的字符数组更加灵活。C 语言动态分配数组 概括地介绍数组的动态存储分配。C 语言释放存储空间 处理存储分配的问题,即不再需要内存单元时,动态地释放已分配的内存块。

因为动态分配的结构可以链接在一起形成表、树以及其他高度灵活的数据结构,所以它们在 C 语言编程中扮演着重要的角色。C 语言链表简介 重点讲述链表,它是最基础的链式数据结构。这一节中引出的问题(“指向指针的指针”的概念)对引出 C 语言指向指针的指针 非常重要。

C 语言指向函数的指针 介绍指向函数的指针,这是非常有用的内容。C 语言中一些功能最强大的库函数期望把指向函数的指针作为参数。这里将考察其中一个函数 qsort,它可以对任意数组进行排序。

最后两节讨论从 C99 开始新出现的与指针相关的特性:受限指针(C 语言受限指针简介)和弹性数组成员(C 语言弹性数组成员)。这些特性主要面向高级 C 程序员,初学者可以跳过。

十八、声明

声明在 C 语言编程中起着核心的作用。通过声明变量和函数,可以在两方面为编译器提供至关重要的信息:检查程序潜在的错误,以及把程序翻译成目标代码。

前面几部分的 C 语言教程已经提供了声明的示例,但是没有完整地描述,这个部分的 C 语言教程将弥补这个缺憾。这个部分的 C 语言教程会探讨可以用于声明的复杂选项,并且显示变量声明和函数声明之间的几个共同点。此外,这个部分的 C 语言教程还为存储、作用域以及链接这些重要概念提供了坚实的基础。

C 语言声明的语法 介绍声明的一般语法,这是之前我们一直回避的主题。接下来的 4 篇将集中讨论声明中出现的数据项:存储类型(C 语言存储类型简介)、类型限定符(C 语言类型限定符简介)、声明符(C 语言声明符简介)和初始化器(C 语言初始化器简介)。C 语言内联函数简介 讨论了 inline 关键字,它可以用在 C99 函数声明中。

十九、程序设计

实际应用中的程序显然比本 C 语言教程中的例子要大,但你可能还没意识到会大多少。更快的 CPU 和更大的主存已经使我们可以编写一些几年前还完全不可行的程序。图形用户界面的流行大大增加了程序的平均长度。如今,大多数功能完整的程序至少有十万行代码,百万行的程序已经很常见,甚至千万行以上的程序都听说过。

虽然 C 语言不是专门用来编写大型程序的,但许多大型程序的确是用 C 语言编写的。这会很复杂,需要很多的耐心和细心,但确实可以做到。这个部分的 C 语言教程将讨论那些有助于编写大型程序的技术,并且会展示 C 语言的哪些特性(例如 static 存储类)特别有用。

编写大型程序(通常称为“大规模程序设计”)与编写小型程序有很大的不同——就如同写一篇学期论文(当然是双倍行距 10 页)与写一本 1000 页的书之间的差别一样。大型程序需要更加注意编写风格,因为会有许多人一起工作。需要有正规的文档,同时还需要对维护进行规划,因为程序可能会经历多次修改。

相比于小型程序,编写大型程序尤其需要更仔细的设计和更详细的计划。正如 Smalltalk 编程语言的设计者 Alan Kay 所言,“You can build a doghouse out of anything”。建造犬舍不需要任何特别的设计,可以使用任何原材料,但是对于人居住的房屋就不能这么干了,后者要复杂得多。

第 15 部分的 C 语言教程曾经讨论过用 C 语言编写大型程序,但更多地侧重于语言的细节。这个部分的 C 语言教程会再次讨论这个主题,并着重讨论好的程序设计所需要的技术。全面地讨论程序设计问题显然超出了本 C 语言教程的范围,但我会尽量简要地涵盖一些在程序设计中比较重要的观念,并展示如何使用它们来编写出易读、易于维护的 C 程序。

C 语言程序设计:模块 讨论如何将 C 程序看作一组相互提供服务的模块。随后会介绍如何使用信息隐藏(C 语言程序设计:信息隐藏)和抽象数据类型(C 语言程序设计:抽象数据类型)来改进程序模块。C 语言程序设计:栈抽象数据类型 通过一个示例(栈数据类型)展示了如何在 C 语言中定义和实现抽象数据类型。C 语言程序设计:抽象数据类型的设计问题 描述了 C 语言在定义抽象数据类型方面的一些局限,并讨论了解决方案。

(完)

comments powered by Disqus