文档章节

C语言笔记——简介与编译过程初探

花果山妖
 花果山妖
发布于 2014/08/02 13:23
字数 2975
阅读 18
收藏 1

序言

 

从今天起,详细说说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。

0103

 

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的帮助文档里面的阐述(如下图):

0101

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

0102

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

0104

第一步: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是一个需要用到系统调用的函数)。

01050106

第二步,compilation(编译):

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

0107

第三步,assembly(汇编):

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

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

0108

第四步,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手册的解释。

0109

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

 

总结

 

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

 

 


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

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

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

 

本文转载自:http://www.cnblogs.com/darksir/p/3802622.html

共有 人打赏支持
花果山妖
粉丝 3
博文 17
码字总数 9727
作品 0
沈阳
私信 提问
NDK开发笔记—ndk环境安装及其搭建

ndk环境安装及其搭建 软件下载 链接:pan.baidu.com/s/1cev9FK 密码:7yab 当然其他版本的也可以,建议不要用最新的 注意:执行Javah的时候生成头文件是对Java文件所生成的.class进行处理的命...

codeGoogle
2017/09/26
0
0
LinuxPHP详解和mysql初探

浏览器只能够解码html格式的文档 动态网站是根据用户请求能做出对应响应甚至于对不同用户返回内容是不一样的 java:一直编译到处运行,是因为java的程序是在java的虚拟机中运行的,而底层系统...

GeniusLP
2017/11/18
0
0
博客导航——一站式搜索(所有博客的汇总帖)

博客导航——一站式搜索 以后博客肯定会越来越多的,所以这做一个整理,方便各位朋友能快速的锁定自己想要的资源 课程 巧用第三方快速开发Android App 热门第三方SDK及框架 Android Studio G...

qq_26787115
2016/01/08
0
0
Postgres大学(象牙塔) 公开课——开发组课程准备

注意 1、本系列课程为step by step进入PostgreSQL开发,只适用于新手。 2、教学过程中操作系统选用红帽系列(CentOS,Fedora等等)。 也可以采用其他OS,但这并不属于本次课程内容,如有问题...

有理想的猪
2015/12/06
317
2
HelloGCC Workshop 2012

2012年11月10日, 我们将来迎来2012年HelloGcc WorkShop,这已经是ChinaUnix社区第三次和HelloGcc工作组合作,在国内为推广GNU Gcc等相关技术交流而举办该项活动。今年我们继续选择北京中科院...

teawater
2012/10/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
2
0
微信网页授权获取用户信息(ThinkPHP5)+ 微信发送客服消息(一)

以thinkphp5为实例,创建控制器 class Kf extends Controller { /** * [protected description]微信公众号appid * @var [type] */ protected $appid = "xxxxxxxxxxxxxxx"; /** * [protected......

半缘修道半缘君丶
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部