标签(空格分隔): 程序人生
“Hello world!”是很多的程序员的第一个程序,不管是什么语言,“Hello world!”总被当作是程序员通向里另一个世界的大门,你真的知道一个“Hello world!”的程序是如何在计算机内部执行的吗?
1.编写一个“Hello world!”并执行
- 新建一个hello.c
root@vmuser-virtual-machine:/home/vmuser# vim hello.c
2.开始写一个“Hello World”
#include<stdio.h>
void main()
{
printf("Helllo World\n");
}
3.编译
root@vmuser-virtual-machine:/home/vmuser# gcc hello.c -o hello
//如果写的没有错误,那么将会在当前的目录下生成“hello”的可执行文件
4.执行
root@vmuser-virtual-machine:/home/vmuser# ./hello
如图所示
Image may be NSFW.
Clik here to view.
这样你的确是会写一个“Hello world!”了。然而并不是这样!
2.什么是程序?
“Hello world!”的生命周期是从一个源文件“hello.c”开始的,其实也就是一个文本文档,其实也就是一个由0和1组成的编码的集合。现代操作系统的大部门的文本文档采用的编码方式一般都为ASCII编码。看下图,我们看看一段“Hello world!”的程序的源码是如何在计算机内部表示的!
Image may be NSFW.
Clik here to view.
我们可以看到“hello world”在计算机内部的存贮方式,但是实际上只不过是一串的二进制的‘010101……’的编码序列这样给我们的启示是,程序其实就是计算机内部的一些信息,然而计算机语言的作用就是我们如何的编码和解码,从而实现一个特定的作用。
GCC的编译过程
编译 hello.c 命令很简单,但实际上,看似很简单的这一步操作,却隐藏了很多操作细 节。下面将通过这个示例,对其中的一些细节进行还原和了解。一个计算机程序,从编码到执行计算机内部要完成以下的几个过程:
- 预处理
预处理器(cpp)根据#开头的命令,修改原始的c程序,将#后面的内容代替为头文件本身的内容,并把它插入程序文本之中,结果就得到了另一个以hello.i为扩展名的c程序。
在这里GCC要加上参数 -E
root@vmuser-virtual-machine:/home/vmuser# gcc -E hello.c -o hello.i
此时会生成hello.i的程序
Image may be NSFW.
Clik here to view.
用vi编辑器打来之后
Image may be NSFW.
Clik here to view.
一个简单的Hello world 居然有800多行的代码
跳转到末尾
Image may be NSFW.
Clik here to view.
可以看出,其实多余的代码就是有#include
root@vmuser-virtual-machine:/home/vmuser/he# gcc -S hello.i
Image may be NSFW.
Clik here to view.
用vi打开hello.s文件,可以可以看到汇编代码
Image may be NSFW.
Clik here to view.
- 汇编
汇编器(as)的作用是将hello.s的汇编程序程序打包成可以重新定位的目标程序->hello.o,也可以叫做可执行文件
得到了汇编文件后,通过 gcc 就可以得到机器码了。在终端输入下列命令,可以得到 hello.o 文件。
root@vmuser-virtual-machine:/home/vmuser/he# gcc -c hello.s
Image may be NSFW.
Clik here to view.
那么此时万事大吉了吗?No!
- 链接
请注意,由于”hello word”程序用了printf函数,链接器(ld)的作用就是将printf.o的可rm执行文件已某种方式合并到hello文件中,这样才可以生成可执行的hello文件!
尽管已经得到了机器码,但还是不可以运行的,必须要经过链接才能运行。在终端输入 下列命令,将会得到可执行文件 a.out。
root@vmuser-virtual-machine:/home/vmuser/he# gcc hello.o
生成了a.out的可执行文件
Image may be NSFW.
Clik here to view.
a.out 是 gcc 默认输出文件名称,可以通过-o 参数指定新的文件名。例如加上“-o hello” 参数,将会生成 hello 文件,这个文件和 a.out 实际上是一样的,用 md5sum 命令计算文件校 验值,两者完全一样
Image may be NSFW.
Clik here to view.
链接可分为动态链接和静态链接:
动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态 库才能运行。动态链接生成的程序小巧,但是必须依赖动态库,否则无法执行。
Linux 下的动态链接库实际是共享目标文件(shared object), 一般是.so 文 件,作用类似于 Windows
下的.dll 文件。 静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直 接运行,不过体积较大。Linux 下静态库是汇编产生的.o 文件的集合,一般以.a 文件形式出现。 gcc 默认是动态链接,加上-static
参数则采用静态链接。再来看 hello.c 示例,在链接的 时候加上-static 参数:
vmuser@Linux-host:hello$ gcc hello.o -static -o hello_static
Image may be NSFW.
Clik here to view.
可以看到,动态链接生成的文件大小是 8379字节, 而静态链接生成的文件却有 879558 字节,体积明显大了很多。
总的过程可以用图来描述
Image may be NSFW.
Clik here to view.
此时生成的文件才可以执行了!
(持续更新)