你还记得"Hello World"吗?
每个程序员,在进入职业生涯的时候,碰到的第一个程序毫无疑问就是"Hello World"。相信大家对它都不陌生,但是不陌生不代表对它有足够深入的了解。今天我们就来聊一下这个带领无数人进入程序世界的"简单"程序。
"Hello World"之所以简单,是简单在它所实现的功能简单,但是就是如此简单的功能背后,蕴含的却是计算机前辈们付出无数心血来为我们奠定了雄厚的根基。我们眼中也许只有"Hello World"这个简单的程序,但却常常忽略了它背后复杂的机制。
当然,本文并不旨在鼓励大家去重复发明轮子,而是提倡每一位程序员应该尽可能去了解轮子背后的原理。做到知其然知其所以然!
"Hello World"的那些事
先秀一下c语言的Hello world程序:
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
咋一看,程序功能非常简单,无非就是往终端输出一个"Hello World"字符串。再往深一步,大部分童鞋可能都知道#include<stdio.h>是什么意思,知道stdio.h是什么头文件,知道c语言库是怎么回事。但是恐怖90%以上的童鞋也是仅限于此。
不服的童鞋不妨来挑战一下以下问题(以下问题是按从浅到深的难度来排序):
-
"Hello World"程序为什么需要先编译才能执行?
-
裸机怎么运行"Hello World"程序?
-
main函数执行之前发生了什么?main函数执行之后又发生了什么?
-
printf函数怎么实现的?怎么实现不定数量的参数?为什么它能在终端输出字符串?
-
"Hello World"程序在运行时,程序在内存中的布局是怎么样的?
-
简单说一下,"Hello World"程序为什么既能裸机运行,也能在操作系统运行?
-
操作系统是怎么一步步加载执行"Hello World"程序的?
-
编译器在将c文件转化成可执行文件的过程,做了哪些步骤?
-
可执行文件有哪些类型?全部都是机器码吗?
-
可执行文件主要有哪些部分组成,每部分是怎么组织存放的?
-
不同的编译器(ARMCC、GCC)/硬件平台/操作系统,对程序的编译有哪些影响?
-
"Hello World"程序能编译成静态库或者是动态库吗?它们有什么区别?
-
操作系统加载静态库和动态库的过程是怎么样的?
能回答清楚上面问题的,可能本系列文章不适合你看。在您面前,相信我还是个菜鸟。但是对这些问题不甚了解,而又对它抱有浓厚兴趣的同学,欢迎与我一起学习,共同成长。
经验分享 携手进步
作为一个IT男,在程序员的路上摸爬滚打了几年以后,越发认识到程序员内功的重要性。程序的编译、链接、装载对于程序员就犹如九阳神功、乾坤大挪移之于张无忌,没有它们的支撑,始终很难在遨游在程序的广阔天地中。
回想起当年的自己,经常在敲代码的时候碰壁,有时还碰得头破血流。举几个曾经折磨过我的例子,相当长一段时间我都在这些问题中反复纠结:
-
为什么程序是从main开始执行的?
-
malloc分配的空间是连续的吗?内存碎片是怎么产生的、
-
PE/ELF文件里面装的是什么东西?
-
目标文件是怎么产生的?链接又是怎么实现的?
-
程序编译报错都是语法问题,哪些链接报错又是什么问题?
-
句柄是什么玩意?
-
.text/.rodata/.data/.bss这些段为什么就能构成一个基本的程序?
-
...
类似的问题非常多,在这个探索的过程花了很多时间去探索学习,也投入不少精力去总结归纳,现在我计划把这些探索的经验,以短篇幅、形象生动的形式分享给大家。希望我的文章的能够帮助大家减少类似我这样的痛苦历程!
除了形象生动的理论讲解外,在这个过程也会借助RT-thread、Linux的部分源码,帮助大家在源码中去验证理论知识。