剖析Hello World
来源:互联网 发布:mac svn客户端使用教程 编辑:程序博客网 时间:2024/06/07 14:54
Eg: Hello示例:
#include <stdio.h>int main(int argc,char **argv){ printf("Hello World \n"); return 0;}
程序很简单,知道C语言的童鞋都会,但是只有一些问题需要你知道,你为什么要包含stdio.h的头文件——如果你只是在终端输入“man printf”输出结果并不会告诉我们为什么,因为输入“man printf”得到的输出结果是以PRINTF(1)标准结束的,这个命令是用户空间的命令/usr/bin/printf 它只对shell-scripts有用,因为有些指令或程序有一个以上的主题,他们位于不同的区段中,因此要查看较后的区段,可以在man中指定。
知道如何正确的使用用户手册(执行 man man 指令):
区段1 用户指令(执行程序或shell命令)
区段2 系统调用(内核提供功能)
区段3 程序库调用 (程序库的函数)
区段5 文件格式(eg:/etc/passwd)
区段6 游戏
区段7 其它
区段8 系统指令
区段9 内核内部指令
通过上面的列表我们可以知道,我们需要的内容在区段3中,如果它是一个单独的系统调用也可能在区段2中。
因此:
wangye@wangye:~$ man 3 printf
......SYNOPSIS #include <stdio.h> int printf(const char *format, ...);......
可以帮助你找到你需要的头文件。
编译Hello.c文件可以使用GUN Complier Collection(GCC)编译器。
使用“which gcc”可以帮你找到gcc的安装路径:
wangye@wangye:~$ which gcc/usr/bin/gcc如果没有安装gcc 可在 root 下执行如下命令:
wangye@wangye:~$ aptitude search gcc......i gcc - GNU C 编译器 p gcc-4.1 - GNU C 编译器 p gcc-4.1-base - GNU 编译器套装 (基本包) p gcc-4.1-doc - documentation for the GNU compilers (gcc, gobjc, g++p gcc-4.1-locales - The GNU C compiler (native language support files) .......i表示已安装,p表示未安装。
安装命令如下:
root@wangye:/home/wangye# apt-get install gcc gcc-4.1-doc
这时我们可以编译我们的Hello.c了
wangye@wangye:~$ gcc -g Hello.c -o Hello // -g表示使用gcc编译 HelloWorld.c 是源文件 “-o HelloWorld” 表示生成目标文件,目标文件的名字为HelloWorldwangye@wangye:~$ lsHello Hello.c执行:wangye@wangye:~$ ./HelloHello World
上面的内容看起来很简单,但是究竟发生了什么会出现这样的结果?
1.2.1:程序翻译:
以上述代码为例:"gcc -o hello hello.c " (-o hello表示生成的可执行文件名称为hello)代码转化过程如下:
1、预处理:把hello.c(机器无关)源程序中以“#”开头的预处理项,进行包含替换,生成预处理文件"test.i"(机器无关),当然具体工作还不只这些。例如:#include “stdio.h”就是把“stdio.h”文件内容替换到该行位置;总体说来就是不对代码进行任何转化,只进行替换和包含工作。验证结果如下:
输入“gcc -E hello.c -o hello.i”//gcc处理源程序,只进行预处理生成hello.i文件
输入“cat hello.i” //显示hello.i文件内容
extern char *ctermid (char *__s) __attribute__ ((__nothrow__));# 886 "/usr/include/stdio.h" 3 4extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__));extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__)) ;extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__));# 916 "/usr/include/stdio.h" 3 4# 2 "HelloWorld.c" 2int main(int argc,char **argv){ printf("Hello World \n"); return 0;}
可以看到,以“#”开头的文件包含指令已经消失,被相应内容替换掉了,其他程序代码没有任何改变,注意:hello.i 仍然是文本文件,与hello.c 基本无异。
2、编译:根据处理器指令集(x86、mips、51、arm),把经预处理的“test.i”(机器无关)文件转换成汇编文件“test.s”(机器相关)。不同型号的处理器架构不同,造成他们之间的汇编指令集互不兼容,那同样一段高级程序生成的汇编程序就不相同;反过来讲,对于同一种处理器,即便选择的高级语言种类不同(c++、.net),编译也会产生相同的汇编程序。这么做的好处是:把高级语言和汇编语言之间的连线切断,就可以制造出不同类别的“编译程序”,只修改“编译程序”,就可以为不同的高级语言创建合适的编译系统。分层就有这个好处:抽象级别高,容易修改(只要接口一致即可)。验证结果如下:
输入命令“gcc -S hello.i -o hello.s” //把hello.i文件转换成hello.s文件
输入命令“cat hello.s”
.file"Hello.c".section.rodata.LC0:.string"Hello World ".text.globl main.typemain, @functionmain:pushl%ebpmovl%esp, %ebpandl$-16, %espsubl$16, %espmovl$.LC0, (%esp)callputsmovl$0, %eaxleaveret.sizemain, .-main.ident"GCC: (Debian 4.4.5-8) 4.4.5".section.note.GNU-stack,"",@progbits
可以看到,把与机器无关的代码“hello.i”进行转化,生成了机器相关的汇编文件“hello.s”。注意:“hello.s”仍然是文本文件,可以用vi直接编辑。
3、汇编:把与机器相关的汇编文件“hello.s”(文本文件)转化成机器相关的可执行(内含机器指令,也叫可重定位的目标文件)文件“hello.o”(二进制文件)。二进制文件hello.o基本上就十分接近可执行的机器文件了,但是还不能正常运行,需要经过下一步的“链接操作”才能真正被执行。验证结果如下:
输入“gcc -c hello.s -o hello.o” //把汇编文件转换成目标文件
输入“cat hello.o”
ELF�4( U��������$��������Hello World GCC: (Debian 4.4.5-8) 4.4.5.symtab.strtab.shstrtab.rel.te80]AzzQ��ment.note.GNU-stack4<%PP0P$��HelloWorld.cmainputs
可以看到,这次已经无法用文本编辑器来正常解析出该文件了,因为二进制文件的解析方式不再是单个字节为单位,而是根据自己的规则调整的。
4、链接:把“hello.o”(二进制文件)转化成“hello”(可执行的二进制文件)。前面讲到“hello.o”几乎接近可执行二进制文件了,但仍然缺点什么。看第一幅图,在链接时,加入了printf.o文件,两个文件输出了一个文件。这是因为我们调用了printf库函数,该函数并不是我们自己定义的,而是在系统库中。既然想用这个函数,肯定也需要把这个函数的相关二进制代码也添加进来才行,这里就是做了这个工作。系统自己已经编译好了目标文件,我们只需要进行“链接”,把调用printf函数的地方,强制跳转到printf.o文件中即可,工作完成再跳回来,这就是为什么hello.o目标文件也叫可重定位文件,因为hello.o文件中还有一些执行代码的跳转地址并没有最终敲定。这里只有一个hello.c文件,如果自己定义了多个源文件,文件之间相互调用的话,也会进行这项工作的。
把printf封装成库是有好处的,因为它是二进制文件(见上图)打开时乱码,这样的话别人就不能看到它的源代码,也就无法模仿可以保护知识产权;也能防止随意修改,造成系统错误。
输入“./ hello” //执行该程序,因为linux的权限管理较严格,即便是执行本目录下的程序也需要加上“./”表示本目录,windows不是这样,权限管理相对较松
系统输出:hello,word
总结:这4个步骤下来,才算完成源程序向可执行程序的转化。中间各个步骤的工作在这里只是大致描述了一下,并不代表他们只做这些事情,具体工作还得研究相关编译器才行。
- 剖析Hello World
- 最小 Docker 镜像 hello-world 剖析
- Hello World!【Hello World】
- PHP-Zend引擎剖析之Hello World(二)
- 从简单的Hello World来剖析项目结构
- PHP-Zend引擎剖析之Hello World(二)
- PHP-Zend引擎剖析之Hello World(二)
- Java中System.out.println("Hello, World");深入剖析
- Hello, world!
- Hello World!
- Hello world!
- Hello World!
- Hello World!
- hello world!
- Hello World !
- Hello,World!
- Hello World!
- Hello world!
- Linux下系统调用的组成与实现
- Key-Value型数据库自结
- java的内存泄露
- poj 1837 0-1背包问题
- Android开发 之 多个Activity时的完美退出方法
- 剖析Hello World
- 【iOS 7】使用UIScreenEdgePanGestureRecognizer实现swipe to pop效果
- C++ 实现 算数表达式求值
- cocos2d-x节点(TransformUtils.h)API
- cocos2d-x节点(CCVertex.h)API
- cocos2d-x节点(ccUtils.h)API
- cocos2d-x节点(ccUTF8.h)API
- IDA 字符串解密脚本
- hdu 4771 Stealing Harry Potter's Precious