序言

从今天起,详细说说C语言。这一年多,在大多数语言和技术之间转了一大圈,终于看清楚了事实,决心静下心来好好学学C语言。初学者会认为C语言是个入门用的东西,没有必要深入研究。但对计算机领域再稍加了解之后,就会发现C语言的重要性,而且它并非是个简单的东西。

我想很多朋友跟我一样是个金庸迷,犹记得《天龙八部》中,乔峰大闹聚贤庄,一套“太祖长拳”击败少林数高僧,我还清楚的记得那回的名字:虽千万人吾往矣!何等气魄。江湖尽人皆知“太祖长拳”,是最基础的武功,每个人也都能使几下,但群雄看到乔峰的功力之后,才暗暗佩服原来“太祖长拳”也能所向披靡。

所以,把C语言比作“太祖长拳”再合适不过了,它是各种语言的基础,是Unix的编写语言,可以说是计算机技术领域的一块重要基石。很多人认为C语言简单,其实我更想说C语言简洁但不是简单。有时候我会下载Linux内核源码或者其他大型开源软件源码来看看,发现其中的C语言让国际上这些大“宗师”们用的变换莫测,你会惊叹这些人可以用C写出功能如此强大的软件,甚至是操作系统。

C语言博大精深,人人都想成为“乔峰”,但路还是要一步一步走,我觉得学编程最重要的一点就是不要被表象所迷惑,一定要想办法看清本质,不然会很痛苦,迷失在各种变化中而失去对它的兴趣。最近还看好了另外两门语言,一个是C++,一个是Python,尤其是Python,极力向大家推荐。

C起源

说到C就不得不提Unix,正是Unix催生了C,也是因为有了C,Unix(和后来的Linux)才有今天。最早创造Unix的人是贝尔实验室的汤姆森(Ken Thompson),Unix和C全诞生在这个实验室。但在没有C语言之前,Unix是用汇编写的,无法独立于硬件,也就是说,想把Unix移植到另一个型号或厂家的CPU上,就要把Unix重写一遍,这样很不现实。汤姆森的同事,也是Unix早期开发者之一,也就是C语言之父,丹尼斯 里奇(Dennis Ritchie,见下图)编写了C语言。之后,因为C语言是独立于硬件的,这帮贝尔实验室的天才们又用C语言重写了一遍Unix,而这个以C语言重写的Unix经过不断的改进和衍生,一直到了今天。如今里奇已经仙逝,汤姆森也已年近古稀,但Unix和C的精神却一直在各大技术社区流淌,生生不息。例如现在的Linus和Stallman,他们依然继承着前任的精神,在各自的领域里坚守着,并且影响了一代又一代的programmer。

C标准

重要更正:由博友garbageMan提示,目前最新标准为C11,2011年修订。具体内容可查阅相关文档。

  C语言一共存在三个标准,分别制定于89年、95年、99年,所以分别简称为C89、C95、C99,而与之对应,89年之前的C语言成为传统C。关于C语言标准的演变,以下抄录《C语言参考手册》中的内容:

从传统C到C89:

1、添加了真正的标准函数库。

2、新的预处理命令和特性。

3、函数原型,允许程序员在函数声明中指定参数的类型。

4、增加了一些新的关键字,包括const、volatile和signed。

5、宽字符、宽字符串和多字节字符。

6、在转换规则、声明和类型检测方面的许多小改动和澄清。

从C89到C95:

1、3个新的标准库头文件:iso646.h、wctype.h和wchar.h。

2、几个新的标记和宏,用于替换有些国家的字符集中不存在的操作符和标点符号。

3、printf/scanf函数家族的一些新的格式代码。

4、大量用于多字节符的新函数以及一些类型的常量。

从C95到C99

1、复数运算

2、整数类型的扩展,包括更长的标准类型。

3、可变长度的数组。

4、对非英语字符集提供了更好的支持。

5、对浮点类型提供了更好的支持,包括所有类型的数学函数。

6、C++风格的注释(//)

关于C++标准:

这里先不做详细讨论,只引用《C参考》中的一句话:标准C++近乎是(但不完全是)标准C的超集。

关于Clean C:

用标准C和标准C++的公共子集编写的C代码叫做Clean C。

注:博主认为,任何一门语言(也不止局限于语言,例如类Unix系统的标准),弄清楚它的标准都很重要,这样有助于了解一门技术的全貌。

C编译过程

到了这篇文章的重头戏了,很多朋友在学习C的过程中会忽略这一点。上来就开始不顾一切的编程,而不去剖析编译的原理,这就明显犯了避重就轻的错误,而不能看清楚事物的本质。所以,这里首先要搞清楚整个程序编译的过程。

我们先来看看gcc的帮助文档里面的阐述(如下图):

从C语言源代码,也就是.c文件,一直到最后的可执行文件,要通过四个关卡:preprocessing(预处理)、compilation(编译)、assembly(汇编)、linking(链接)。具体过程如下图,之后我会逐个阐述每个过程:

我们通过一个最简单的实验描述整个过程。首先编写一个最简单的C语言程序hello.c,这就是我们要编译的源代码,如下图,只有7行这样简单

第一步:preprocessing(预处理)

gcc 中,若要程序只进行预处理而不再往下编译,利用选项 -E,完整的命令为 gcc -E hello.c -o hello.i  ,预处理之后生成 hello.i(部分代码如下图)。预处理阶段主要处理预处理命令,hello.c 中就是第一行代码 #include <stdio.h>  ,因为 hello.c 程序中要利用 printf  函数,所以要从 stdio.h 这个头文件里引入 printf 的声明。可以看到,预处理之后的文件足足有855行,这全都是 printf 函数做的怪,可见我们最常用的printf 函数并不简单,仅头文件的声明就有这么多,所以C的编译器和库函数一起为我们隐藏了许多细节(这里再往深入探究下去,会涉及系统调用的原理,printf是一个需要用到系统调用的函数)。

第二步,compilation(编译):

这一步将预处理之后的 hello.i 编译成汇编代码 hello.sgcc 中的选项是 –S ,完整命令为 gcc -S hello.i -o hello.s ,编译之后生成汇编代码 hello.s (如下图),这份代码就是和体系结构相关的了,不过我们现在所用到的电脑大部分都是x86架构,应该不会有什么差别,但我想,如果拿到一些嵌入式设别上,这部分会代码就不同了,具体的汇编语言没有体系的学过,想深入的同学可以找找相关资料。可以看到代码量也不大。只有20行,因为这里的15行是 call printf,也隐藏了 printf 的细节。

第三步,assembly(汇编):

这一步将汇编代码 hello.s 编译为二进制的对象文件 hello.ogcc 中的选项是 –c , 完整的命令为 gcc -c hello.s -o hello.o ,执行之后会生成二进制机器代码写成的 hello.o (如下图),可以看到这个文件打开时乱码,说明这是人类不可读的二进制文件。每一个没有错误的 .c 文件经过这三部编译之后,都是生成一个.o 文件,o是object的缩写,而把很多的 .o 文件集合在一起就形成了库文件,库文件有分为静态库(.a)和动态库(.so)。

注:关于静态库和共享库的内容我会放到下一篇博客里,因为解释这整个过程足够写一篇的了。

第四步,linking(链接):

这一步负责形成最后的可执行文件,链接不是一个很好理解的过程,我们想象一下,如果我们编写一个稍微大一点儿的项目,肯定不会只有一个 .c 文件,而按照上一步所说,每一个 .c 文件编译过后都会形成一个 .o 文件,而链接的任务就是把这些 .o 文件链接在一起并找到程序的主入口。有人会说这有什么难理解的,但试想一下我们现在这种情况,我们只有一个 hello.c 文件,那还需要链接这步么?答案是当然需要,还记得让人操心的 printf 函数么? 对,这个函数是系统帮我们实现的,他的 .o 文件就在其他地方,这个地方就叫做动态库,一个操作系统帮我们维护的工具库,所以我们这里虽然只有一个 .c 文件,一样需要链接。那有人还会说,如果我连 printf 也不用呢?,虽然这有些抬杠的嫌疑,但也不是不可以出现这样的情况,但事实是不可以的。我猜想链接过程肯定还负责一些其他的工作,比如说最明显的 .o 文件时没有执行权限的,而链接后的文件时可执行的,感兴趣的同学可以再深入探究下, 我在这就先扯到这里。关于共享库的内容我下一篇再展开。

链接过程不需要选项,完整的 gcc 命令为: gcc hello.o –o hello , 生成的无后缀名的 hello 文件就是最后的可执行文件。这里如果有很多个 .o 文件,要全部加到命令行里,例如 gcc hello1.o hello2.o hello3.o –o hello。

gcc 命令

  下面简单说一下我问刚才用到的四个 gcc 选项: –E 、–S 、–c 、–o 。先看一下linux下的man手册的解释。

  可以看到前三个选项其实解释从三个级别上截断编译过程,而并非是向我们上面用到那样,看上去像是一个截断一个选项,其实你用 –c 直接把 .c 文件编译成 .o 也是可以的,而且正常情况也是这样做的,我这里只是为了演示编译的每一个过程,在现实中,前连个选项应该是调试和观察编译过程是用的比较多。

总结

  这篇文章简单介绍了一下C语言的历史和产生过程,并重点描述了整个编译过程,而关于库的相关内容我会在下篇文章里详细讨论。


文章中大部分概念都是我个人的收集、思考和总结,难免有些地方可能有出入,也欢迎各路朋友发现问题指点一二。

志同道合的朋友可以在微信中搜索公众账号:DarkSir 或 扫描博客公告栏中的二维码

博客文章将同步更新到公众微信账号

C语言笔记——简介与编译过程初探的更多相关文章

  1. 笔记:C 编译过程

    笔记:C 编译过程 参考了 编译器的工作过程 1 C 编译过程 配置 确定标准库和头文件位置 确定依赖关系 头文件的预编译 预处理 编译 连接 F4NNIU 2018-06-12 编译器的工作过程 h ...

  2. 用gcc编译c语言程序以及其编译过程

    对于初学c语言编程的我们来说,学会如何使用gcc编译器工具,对理解c语言的执行过程,加深对c语言的理解很重要!!! 1.预编译 --> 2.编译 --> 3.汇编 --> 4.链接- ...

  3. C语言简短程序gcc编译过程

    一.建立一个×.c源文件.这里起名:rocks.c 二.编辑源代码,在c源文件内输入如下代码: #include <stdio.h> int main() { puts("C R ...

  4. APM代码学习笔记2:编译过程

    make编译 所有位置的Makefile 引用的都是/mk/apm.mk target.mk 设置CONFIG_HAL_BOARD 例如linux就是HAL_BOARD_LINUX environ.m ...

  5. 转 C语言编译过程简介

    C语言编译过程简介 C语言编译过程简介 刚开始接触编程的时候,只知道照书敲敲代码,一直都不知道为什么在windows平台下代码经过鼠标那样点击几下,程序的结果就会在那个黑色的屏幕上.现在找了个机会将C ...

  6. C语言基础(21)-C语言编译过程及GCC参数简介

    任何C语言的编译过程可分为以下三部分: 一.预编译 在C语言中,以#开头的语句又叫预编译指令.预编译主要做以下两件事情: 1.将#include包含的头文件做简单的文本替换: 2.将代码中的注释删除. ...

  7. 【嵌入式开发】gcc 学习笔记(一) - 编译C程序 及 编译过程

    一. C程序编译过程 编译过程简介 : C语言的源文件 编译成 可执行文件需要四个步骤, 预处理 (Preprocessing) 扩展宏, 编译 (compilation) 得到汇编语言, 汇编 (a ...

  8. C语言编译过程以及gcc编译参数

    1.1       C语言编译过程,gcc参数简介 1.1.1          C语言编译过程 一.gcc - o a a.c -o:指定文件输出名字 二.C语言编译的过程: 1.1.1       ...

  9. gcc 学习笔记(一) - 编译C程序 及 编译过程

    一. C程序编译过程 编译过程简介 : C语言的源文件 编译成 可执行文件需要四个步骤, 预处理 (Preprocessing) 扩展宏, 编译 (compilation) 得到汇编语言, 汇编 (a ...

随机推荐

  1. 写一个Windows上的守护进程(1)开篇

    写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的 ...

  2. 依赖注入 | Dependency Injection

    原文链接: Angular Dependency Injection翻译人员: 铁锚翻译时间: 2014年02月10日说明: 译者认为,本文中所有名词性的"依赖" 都可以理解为 & ...

  3. Android4大组件

    http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html 快乐阅读: http://www.360doc.com/cont ...

  4. webpack ------require,ensure

    require-ensure和require-amd的区别: require-amd 说明: 同AMD规范的require函数,使用时传递一个模块数组和回调函数,模块都被下载下来且都被执行后才执行回调 ...

  5. TCP连接状态详解及TIME_WAIT过多的解决方法

    上图对排除和定位网络或系统故障时大有帮助,但是怎样牢牢地将这张图刻在脑中呢?那么你就一定要对这张图的每一个状态,及转换的过程有深刻地认识,不能只停留在一知半解之中.下面对这张图的11种状态详细解释一下 ...

  6. 跟我开发NSP(网上查询平台):如何选择开发项目

    我想通过一个真实的项目开发的全过程,记录一下开发过程的点点滴滴,记录一下过程中的前思后想.这个全过程包括,如何选择项目.如何分析项目.如何组织项目开发.如何设计开发流程.如何设计软件的总体架构.如何建 ...

  7. 一张图片入门Python

    一张图片入门Python 之前已有别人整理了,一张图入门Python,快速了解各种基本的语法. 英文版: 图 5.1. Quick Python Script Explanation 中文版: 图 5 ...

  8. hdu 1575 Tr A(矩阵快速幂乘法优化算法)

    Problem Description A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%. Input 数据的第一行是一个T,表示有T组数据. 每组数据的第一行有n ...

  9. iOS使用ffmpeg播放rstp实时监控视频数据流

    一.编译针对iOS平台的ffmpeg库(kxmovie) 最近有一个项目.须要播放各种格式的音频.视频以及网络摄像头实时监控的视频流数据,经过多种折腾之后,最后选择了kxmovie,kxmovie项目 ...

  10. IKAnalyzer使用停用词词典进行分词

    @Test // 測试分词的效果,以及停用词典是否起作用 public void test() throws IOException { String text = "老爹我们都爱您.&qu ...