已经忘了第一次写c语言程序到底是什么时候的事了。不过我却明白,当时我肯定是知其然而不知所以然。不知从什么时候开始对程序执行背后的东西感兴趣了,而且愈演愈烈,现在终于下定决心去搞明白它了。
就以c语言中最经典的例子来说明吧:
上面的代码我们保存在helloworld.c文件中。其本质实际上是由0、1的比特(位)序列构成的。8位为一个字节。每个字节对应某个文本字符。不少系统用ASCII来表示文本字符。实际是由一个唯一的同字节大小的整数值来表示每个字符。下面给出helloworld.c的ASCII表示。
# i n c l u d e <sp> < s t d i o .
35 105 110 99 108 117 100 101 32 60 115 116 100 105 111 46
h > /n /n i n t <sp> m a i n ( ) /n {
104 62 10 10 105 110 116 32 109 97 105 110 40 41 10 123
/n <sp> <sp> <sp> <sp> p r i n t f ( " h e l
10 32 32 32 32 112 114 105 110 116 102 40 34 104 101 108
l o , <sp> w o r l d / n " ) ; /n }
108 111 44 32 119 111 114 108 100 92 110 34 41 59 10 125
以此类推,在计算机系统中,任何介质中的数据都是比特序列。把他们区分成不同的数据对象,是通过数据对象的上下文来确定的。
在这里,概略地简述一下c语言源码从编译到执行的流程:
![](http://hi.csdn.net/attachment/201011/19/0_1290177571jp4y.gif)
程序编译
程序的编译过程如下图所示,分为预处理、编译、汇编、链接等几个阶段。
![](http://hi.csdn.net/attachment/201011/19/0_1290176930lmcI.gif)
预处理:预处理过程主要处理那些源代码文件hello.c中的以“#”开始的预编译指令。比如“#include“、“#define”等,主要处理规则如下:
1. 将所有的“define”删除,并且展开所有的宏定义。
2. 处理所有的条件预编译指令,比如“#if”、“#ifdef”、“#elif”、“#else”、“#endif".
3. 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。注:这里的文件仅仅指的是头文件而非c文件。
4. 删除所有的注释“//”和“/* */”。
5. 添加行号和文件名标识,比如#2 "hello.c" 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
6. 保留所有的#pragma编译器指令,因为编译器需要使用他们。
以下为hello.i的内容(样板):
编译: 编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成的汇编代码文件。
汇编: 将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。该文件是二进制文件,字节编码是机器指令。
链接: 将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件
硬件组成:
从下图中看出一个典型的系统由总线、Cpu、I/O设备、主存等构成。
在这里我们只是先睹为快,后面真正执行时会大量设计cpu与主存、磁盘的交互。。。
程序的运行:
程序的链接、装载及运行非常复杂,下一篇将丝丝入扣地剖析它。。。