
前言" p2 R# @" ?( `1 T, g9 [- w C 语言是一门抽象的、面向过程的语言,C 语言广泛应用于底层开发,C 语言在计算机体系中占据着不可替代的作用,可以说 C 语言是编程的基础,也就是说,不管你学习任何语言,都应该把 C 语言放在首先要学的位置上。下面这张图更好的说明 C 语言的重要性 ![]() 可以看到,C 语言是一种底层语言,是一种系统层级的语言,操作系统就是使用 C 语言来编写的,比如 Windows、Linux、UNIX 。如果说其他语言是光鲜亮丽的外表,那么 C 语言就是灵魂,永远那么朴实无华。 C 语言特性那么,既然 C 语言这么重要,它有什么值得我们去学的地方呢?我们不应该只因为它重要而去学,我们更在意的是学完我们能学会什么,能让我们获得什么。 C 语言的设计C 语言是 1972 年,由贝尔实验室的丹尼斯·里奇(Dennis Ritch)和肯·汤普逊(Ken Thompson)在开发 UNIX 操作系统时设计了C语言。C 语言是一门流行的语言,它把计算机科学理论和工程实践理论完美的融合在一起,使用户能够完成模块化的编程和设计。 C 语言具有高效性 C 语言是一门高效性语言,它被设计用来充分发挥计算机的优势,因此 C 语言程序运行速度很快,C 语言能够合理了使用内存来获得最大的运行速度 C 语言具有可移植性C 语言是一门具有可移植性的语言,这就意味着,对于在一台计算机上编写的 C 语言程序可以在另一台计算机上轻松地运行,从而极大的减少了程序移植的工作量。 C 语言特点
下面让我们通过一个简单的示例来说明一下 C 语言 入门级 C 语言程序下面我们来看一个很简单的 C 语言程序,我是 mac 电脑,所以我使用的是 xcode 进行开发,我觉得工具无所谓大家用着顺手就行。 第一个 C 语言程序 #include <stdio.h>int main(int argc, const char * argv[]) {: }* B. a1 \3 w. z5 c' r+ n, D/ L; o printf("Hello, World!\n");2 l5 I4 t# j; e: ~4 y7 O6 B* l printf("My Name is cxuan \n");0 c) Q+ z$ t8 u7 Y8 | return 0;0 d/ @% M7 q. m: X: M } 你可能不知道这段代码是什么意思,不过别着急,我们先运行一下看看结果。 ![]() 这段程序输出了 Hello,World! 和 My Name is cxuan,最后一行是程序的执行结果,表示这段程序是否有错误。下面我们解释一下各行代码的含义。 首先,第一行的 #include <stdio.h>, 这行代码包含另一个文件,这一行告诉编译器把 stdio.h 的内容包含在当前程序中。stdio.h 是 C 编译器软件包的标准部分,它能够提供键盘输入和显示器输出。
我们后面会介绍 stdio.h ,现在你知道它是什么就好。 在 stdio.h 下面一行代码就是 main 函数。 C 程序能够包含一个或多个函数,函数是 C 语言的根本,就和方法是 Java 的基本构成一样。main() 表示一个函数名,int 表示的是 main 函数返回一个整数。void 表明 main() 不带任何参数。这些我们后面也会详细说明,只需要记住 int 和 void 是标准 ANSI C 定义 main() 的一部分(如果使用 ANSI C 之前的编译器,请忽略 void)。 然后是 /*一个简单的 C 语言程序*/ 表示的是注释,注释使用 /**/ 来表示,注释的内容在两个符号之间。这些符号能够提高程序的可读性。
下面就是 { ,这是左花括号,它表示的是函数体的开始,而最后的右花括号 } 表示函数体的结束。{ } 中间是书写代码的地方,也叫做代码块。 int number 表示的是将会使用一个名为 number 的变量,而且 number 是 int 整数类型。 number = 11 表示的是把值 11 赋值给 number 的变量。 printf(Hello,world!\n); 表示调用一个函数,这个语句使用 printf() 函数,在屏幕上显示 Hello,world , printf() 函数是 C 标准库函数中的一种,它能够把程序运行的结果输出到显示器上。而代码 \n 表示的是 换行,也就是另起一行,把光标移到下一行。 然后接下来的一行 printf() 和上面一行是一样的,我们就不多说了。最后一行 printf() 有点意思,你会发现有一个 %d 的语法,它的意思表示的是使用整形输出字符串。 代码块的最后一行是 return 0,它可以看成是 main 函数的结束,最后一行是代码块 } ,它表示的是程序的结束。 好了,我们现在写完了第一个 C 语言程序,有没有对 C 有了更深的认识呢?肯定没有。。。这才哪到哪,继续学习吧。 现在,我们可以归纳为 C 语言程序的几个组成要素,如下图所示 ![]() C 语言程序成为高级语言的原因是它能够读取并理解人们的思想。然而,为了能够在系统中运行 hello.c 程序,则各个 C 语句必须由其他程序转换为一系列低级机器语言指令。这些指令被打包作为可执行对象程序,存储在二进制磁盘文件中。目标程序也称为可执行目标文件。 在 UNIX 系统中,从源文件到对象文件的转换是由编译器执行完成的。 gcc -o hello hello.cgcc 编译器驱动从源文件读取 hello.c ,并把它翻译成一个可执行文件 hello。这个翻译过程可用如下图来表示 ![]() 这就是一个完整的 hello world 程序执行过程,会涉及几个核心组件:预处理器、编译器、汇编器、连接器,下面我们逐个击破。
对于上面这种简单的 hello 程序来说,我们可以依赖编译系统(compilation system)来提供一个正确和有效的机器代码。然而,对于我们上面讲的程序员来说,编译器有几大特征你需要知道
为了理解 hello 程序在运行时发生了什么,我们需要首先对系统的硬件有一个认识。下面这是一张 Intel 系统产品的模型,我们来对其进行解释 ![]()
![]()
前面我们简单的介绍了一下计算机的硬件的组成和操作,现在我们正式介绍运行示例程序时发生了什么,我们会从宏观的角度进行描述,不会涉及到所有的技术细节 刚开始时,shell 程序执行它的指令,等待用户键入一个命令。当我们在键盘上输入了 ./hello 这几个字符时,shell 程序将字符逐一读入寄存器,再把它放到内存中,如下图所示 ![]() 当我们在键盘上敲击回车键的时候,shell 程序就知道我们已经结束了命令的输入。然后 shell 执行一系列指令来加载可执行的 hello 文件,这些指令将目标文件中的代码和数据从磁盘复制到主存。 利用 DMA(Direct Memory Access) 技术可以直接将磁盘中的数据复制到内存中,如下 ![]() 一旦目标文件中 hello 中的代码和数据被加载到主存,处理器就开始执行 hello 程序的 main 程序中的机器语言指令。这些指令将 hello,world\n 字符串中的字节从主存复制到寄存器文件,再从寄存器中复制到显示设备,最终显示在屏幕上。如下所示 ![]() 上面我们介绍完了一个 hello 程序的执行过程,系统花费了大量时间把信息从一个地方搬运到另外一个地方。hello 程序的机器指令最初存储在磁盘上。当程序加载后,它们会拷贝到主存中。当 CPU 开始运行时,指令又从内存复制到 CPU 中。同样的,字符串数据 hello,world \n 最初也是在磁盘上,它被复制到内存中,然后再到显示器设备输出。从程序员的角度来看,这种复制大部分是开销,这减慢了程序的工作效率。因此,对于系统设计来说,最主要的一个工作是让程序运行的越来越快。 由于物理定律,较大的存储设备要比较小的存储设备慢。而由于寄存器和内存的处理效率在越来越大,所以针对这种差异,系统设计者采用了更小更快的存储设备,称为高速缓存存储器(cache memory, 简称为 cache 高速缓存),作为暂时的集结区域,存放近期可能会需要的信息。如下图所示 ![]() 图中我们标出了高速缓存的位置,位于高速缓存中的 L1高速缓存容量可以达到数万字节,访问速度几乎和访问寄存器文件一样快。容量更大的 L2 高速缓存通过一条特殊的总线链接 CPU,虽然 L2 缓存比 L1 缓存慢 5 倍,但是仍比内存要快 5 - 10 倍。L1 和 L2 是使用一种静态随机访问存储器(SRAM) 的硬件技术实现的。最新的、处理器更强大的系统甚至有三级缓存:L1、L2 和 L3。系统可以获得一个很大的存储器,同时访问速度也更快,原因是利用了高速缓存的 局部性原理。 Again:入门程序细节现在,我们来探讨一下入门级程序的细节,由浅入深的来了解一下 C 语言的特性。 #include<stdio.h>我们上面说到,#include<stdio.h> 是程序编译之前要处理的内容,称为编译预处理命令。 预处理命令是在编译之前进行处理。预处理程序一般以 # 号开头。 所有的 C 编译器软件包都提供 stdio.h 文件。该文件包含了给编译器使用的输入和输出函数,比如 println() 信息。该文件名的含义是标准输入/输出 头文件。通常,在 C 程序顶部的信息集合被称为 头文件(header)。 C 标准库 除了 <sdtio.h> 外,C 标准库还包括下面这些头文件 ![]() <assert.h> 提供了一个名为 assert 的关键字,它用于验证程序作出的假设,并在假设为假输出诊断消息。 <ctype.h> C 标准库的 ctype.h 头文件提供了一些函数,可以用于测试和映射字符。 这些字符接受 int 作为参数,它的值必须是 EOF 或者是一个无符号字符
C 标准库的 errno.h 头文件定义了整数变量 errno,它是通过系统调用设置的,这些库函数表明了什么发生了错误。 C 标准库的 float.h 头文件包含了一组与浮点值相关的依赖于平台的常量。 limits.h 头文件决定了各种变量类型的各种属性。定义在该头文件中的宏限制了各种变量类型(比如 char、int 和 long)的值。 locale.h 头文件定义了特定地域的设置,比如日期格式和货币符号 math.h 头文件定义了各种数学函数和一个宏。在这个库中所有可用的功能都带有一个 double 类型的参数,且都返回 double 类型的结果。 setjmp.h 头文件定义了宏 setjmp()、函数 longjmp() 和变量类型 jmp_buf,该变量类型会绕过正常的函数调用和返回规则。 signal.h 头文件定义了一个变量类型 sig_atomic_t、两个函数调用和一些宏来处理程序执行期间报告的不同信号。 stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。 stddef .h 头文件定义了各种变量类型和宏。这些定义中的大部分也出现在其它头文件中。 stdlib .h 头文件定义了四个变量类型、一些宏和各种通用工具函数。 string .h 头文件定义了一个变量类型、一个宏和各种操作字符数组的函数。 time.h 头文件定义了四个变量类型、两个宏和各种操作日期和时间的函数。 main() 函数main 函数听起来像是调皮捣蛋的孩子故意给方法名起一个 主要的 方法,来告诉他人他才是这个世界的中心。但事实却不是这样,而 main() 方法确实是世界的中心。 C 语言程序一定从 main() 函数开始执行,除了 main() 函数外,你可以随意命名其他函数。通常,main 后面的 () 中表示一些传入信息,我们上面的那个例子中没有传递信息,因为圆括号中的输入是 void 。 除了上面那种写法外,还有两种 main 方法的表示方式,一种是 void main(){} ,一种是 int main(int argc, char* argv[]) {}
在程序中,使用 /**/ 的表示注释,注释对于程序来说没有什么实际用处,但是对程序员来说却非常有用,它能够帮助我们理解程序,也能够让他人看懂你写的程序,我们在开发工作中,都非常反感不写注释的人,由此可见注释非常重要。 ![]() C 语言注释的好处是,它可以放在任意地方,甚至代码在同一行也没关系。较长的注释可以多行表示,我们使用 /**/ 表示多行注释,而 // 只表示的是单行注释。下面是几种注释的表示形式 // 这是一个单行注释9 ?5 I2 A F9 T: [/* 多行注释用一行表示 */7 ~: g* d i. H# d' Y8 K2 u n9 L: H$ o4 t& n /*! o* I* {, K$ f; r b 多行注释用多行表示2 S* s: \$ _, `+ Z 多行注释用多行表示% I- |2 R( |$ z( I7 |) N) d. D 多行注释用多行表示 多行注释用多行表示 ' h& j" Z( a" _ */ 函数体 在头文件、main 方法后面的就是函数体(注释一般不算),函数体就是函数的执行体,是你编写大量代码的地方。 变量声明在我们入门级的代码中,我们声明了一个名为 number 的变量,它的类型是 int,这行代码叫做 声明,声明是 C 语言最重要的特性之一。这个声明完成了两件事情:定义了一个名为 number 的变量,定义 number 的具体类型。 int 是 C 语言的一个 关键字(keyword),表示一种基本的 C 语言数据类型。关键字是用于语言定义的。不能使用关键字作为变量进行定义。 示例中的 number 是一个 标识符(identifier),也就是一个变量、函数或者其他实体的名称。 变量赋值在入门例子程序中,我们声明了一个 number 变量,并为其赋值为 11,赋值是 C 语言的基本操作之一。这行代码的意思就是把值 1 赋给变量 number。在执行 int number 时,编译器会在计算机内存中为变量 number 预留空间,然后在执行这行赋值表达式语句时,把值存储在之前预留的位置。可以给 number 赋不同的值,这就是 number 之所以被称为 变量(variable) 的原因。 ![]() 在入门例子程序中,有三行 printf(),这是 C 语言的标准函数。圆括号中的内容是从 main 函数传递给 printf 函数的。参数分为两种:实际参数(actual argument) 和 形式参数(formal parameters)。我们上面提到的 printf 函数括号中的内容,都是实参。 return 语句在入门例子程序中,return 语句是最后一条语句。int main(void) 中的 int 表明 main() 函数应返回一个整数。有返回值的 C 函数要有 return 语句,没有返回值的程序也建议大家保留 return 关键字,这是一种好的习惯或者说统一的编码风格。 分号在 C 语言中,每一行的结尾都要用 ; 进行结束,它表示一个语句的结束,如果忘记或者忽略分号会被编译器提示错误。 关键字下面是 C 语言中的关键字,C 语言的关键字一共有 32 个,根据其作用不同进行划分 数据类型关键字数据类型的关键字主要有 12 个,分别是
控制语句循环的关键字也有 12 个,分别是 循环语句
条件语句
开关语句
返回语句 retur:子程序返回语句(可以带参数,也看不带参数) 存储类型关键字
7 J0 ]& j) u/ a- |" f7 O) Z 后记这篇文章我们先介绍了 C 语言的特性,C 语言为什么这么火,C 语言的重要性,之后我们以一道 C 语言的入门程序讲起,我们讲了 C 语言的基本构成要素,C 语言在硬件上是如何运行的,C 语言的编译过程和执行过程等,在这之后我们又加深讲解了一下入门例子程序的组成特征。 |