首先,看如下最简单的C语言Helloword的代码
1#include <stdio.h>
2
3int main()
4{
5printf("hello, world\n");
6}
上面的代码我们保存在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程序,不过常以i为扩展名。
cpp hello.c -o hell.i
编译:将得到的i文件翻译成汇编代码.s文件。
gcc -S hello.i -o hello.s
汇编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的O文件。
该文件是二进制文件,字节编码是机器指令。
as hello.s -o hello.o
链接:将引用的其他O文件并入到我们程序所在的o文件中,处理得到最终的可执行文件。
gcc -o hello hell.o
硬件组成:
从下图中看出一个典型的系统由总线、Cpu、I/O设备、主存等构成。
CPU: Central Processing Unit, ALU: Arithmetic/Logic Unit, PC: Program counter, USB: Universal Serial Bus.
程序执行
我们已经讨论了可执行文件产生的过程。
接下来讨论哈可执行文件执行的过程。
从上面途中的彩色线条可以清晰的看到这个过程,我们简单的把它分为6步。
1.shell程序执行指令,等待用户输入,这里我们输入“hello”。
2.shell程序将字符逐一读到寄存器中
3.再从寄存器取出放到主存中
4.当我们敲入回车时,shell程序得知输入结束,将hello目标文件的代码和数据拷贝到主存,从而加载hello文件数据包括最终被输出的字符串“hello,world\n”.利用了DMA访问技术,数据可不经CPU直接到主存
5.执行主程序中的机器语言指令,将“hello,world\n”串的字节从主存拷贝到寄存器堆。
6.从寄存器中把文件拷贝到显示设备。
1.1 程序是怎样在计算机中运行的
从键盘中读入hello命令,经过shell的解析,shell执行一系列的
指令将hello目标文件中的代码和数据从磁盘中拷贝到主内存当中,
如下图所示:
这个过程会利用DMA技术,不经过CPU数据直接从磁盘拷贝到内存当中
一旦hello目标文件中的代码和数据被加载到了内存当中,处理器就开始执行hello程序中的主程序的机器语言指令,这些指令将“hello world\n”串中的字符从内存中拷贝到寄存器中,再从寄存器中将这些内容拷贝到显示设备上,最终显示在屏幕当中。
静态库和动态库的制作
我们在实际编程工作中肯定会遇到这种情况:有几个项目里有一些函数模块的功能相同,实现代码也相同,也是我们所说的重复代码。
比如,很多项目里都有一个用户验证的功能。
代码段如下:
//UserLogin.h文件,提供函数声明
int IsValidUser(char* username, int namelen);
//UserLogin.c文件,实现对用户信息的验证
int IsValidUser(char* username, int namelen)
{
int IsValid = 0;
/*下面是具体的处理代码,略去*/
return IsValid
}
如果每个项目都保存着这两个UserLogin.h和UserLogin.c文件,会有以下几个
弊端:
1、每个项目里都有重复的模块,造成代码重复。
2、代码的重用性不好,一旦IsValidUser的代码发生了变化,为了保持设计的一致性,我们还要手工修改其他项目里的UserLogin.c文件,既费时又费力,还容易出错。
库文件就是对公共代码的一种组织形式。
为了解决上面两个弊端,就提出了用库文件存放公共代码的解决方案,其要点就是
把公共的(也就是可以被多次复用的)目标代码从项目中分离出来,统一存放到库文件中,项目要用到这些代码的时候,在编译或者运行的时候从库文件中取得目标代码即可。
库文件又分两种:静态库和动态库。
如果程序是在编译时加载库文件的,就是使用了静态库。
如果是在运行时加载目标代码,
就成为动态库。
换句话说,如果是使用静态库,则静态库代码在编译时就拷贝到了程序的代码段,
程序的体积会膨胀。
如果使用动态库,则程序中只保留库文件的名字和函数名,在运行时去查找
库文件和函数体,程序的体积基本变化不大。
静态库的原则是“以空间换时间”,增加程序体积,减少运行时间;
动态库则是“以时间换空间”,增加了运行时间,但减少了程序本身的体积。
静态库的方式
动态链接的方式。