『壹』 自學c語言 零基礎 看什麼書 該怎麼學
現在,市面上的 C語言書籍多的數不過來,但是很多不適合你這種初學者使用,作為一名老程序員,我給你推薦幾本我看過認為還不錯的書:
1) 《C語言小白變怪獸》:既適合對計算機一竅不通的小白,也適合有編程基礎的讀者,還可以用來進階。同時,最重要的是,這本書的作者還在不斷地更新這本書,我認為最大的特點是通俗易懂,深入淺出,只要認真看,就一定能看懂,我認為這是C語言入門的首選教材,吐血推薦,經典中的經典!!!
2) 《啊哈C語言》:適合對計算機一竅不通的純小白,有編程基礎的讀者請忽略。這本書語言幽默,生動形象,通俗易懂,給人耳目一新的感覺,但是這本書非常淺顯,並不深入,所以適合純小白入門,找到門以後也就扔了。這本書著重於語言藝術,針對純小白下足了功夫,小學生坐在馬桶上都能學會編程。說實話,我非常欣賞作者的創意和用心,國內像這樣寫書的少之又少,入門的書就應該是這個樣子。
上邊這兩本書是我推薦給你用於C語言入門的,我不建議你使用譚浩強的《C語言程序設計》這本書入門,雖然這本書的知名度很高,因為很多大學把它作為C語言的授課教材,然而,這本書被吐槽最多,不但代碼不標准,而且有很多錯誤,據說可以找出幾十個錯誤,這樣很容易誤導你這種小白。
等你有一定的C語言基礎了,可以看下面的這幾本書,進一步提升你的編程水平:
1) 《C Primer Plus》:暢銷 30 余年的C語言入門經典書籍,至今無人能撼動它的地位,它是最符合C語言標準的書籍,作為一名C語言程序員,不收藏一本都覺得自己很水。
2) 《C程序設計語言,The C Programming Language》:C語言創始人丹尼斯·里奇(Dennis MacAlistair Ritchie)的著作,和《C Primer Plus》並駕齊驅,很難分出伯仲,這兩本書都是一樣的優秀,都是C語言程序員必須收藏的。
3) 《C專家編程》:適合已經具備C語言基礎、想進階的讀者。這貌似是一本吐槽C語言的書,基本上介紹了大多數C語言的坑,有不少精髓,在C語言界久負盛名,進階時一定要看。
4) 《C陷阱與缺陷》:和《C專家編程》類似,也是給C語言挑刺的,闡述了很多容易讓人誤解的語法細節,重在幫助C程序員繞過編程過程中的陷阱和障礙。
5) 《程序員的自我修養 -- 鏈接、裝載與庫》:這真是一本深入骨髓的書,讓你徹底明白程序的編譯、鏈接、裝載和運行的全過程,你會從此上天,拉開和別人的差距,看任何問題都有了深度和高度。重點推薦!
所有的這些書我都有電子版,需要可以找我要!
『貳』 《程序員的自我修養鏈接、裝載與庫》epub下載在線閱讀全文,求百度網盤雲資源
《程序員的自我修養》(俞甲子)電子書網盤下載免費在線閱讀
鏈接:
書名:程序員的自我修養
作者:俞甲子
豆瓣評分:8.8
出版社:電子工業出版社
出版年份:2009-4
頁數:459
內容簡介:
這本書主要介紹系統軟體的運行機制和原理,涉及在Windows和Linux兩個系統平台上,一個應用程序在編譯、鏈接和運行時刻所發生的各種事項,包括:代碼指令是如何保存的,庫文件如何與應用程序代碼靜態鏈接,應用程序如何被裝載到內存中並開始運行,動態鏈接如何實現,C/C++運行庫的工作原理,以及操作系統提供的系統服務是如何被調用的。每個技術專題都配備了大量圖、表和代碼實例,力求將復雜的機制以簡潔的形式表達出來。本書最後還提供了一個小巧且跨平台的C/C++運行庫MiniCRT,綜合展示了與運行庫相關的各種技術。
對裝載、鏈接和庫進行了深入淺出的剖析,並且輔以大量的例子和圖表,可以作為計算機軟體專業和其他相關專業大學本科高年級學生深入學習系統軟體的參考書。同時,還可作為各行業從事軟體開發的工程師、研究人員以及其他對系統軟體實現機制和技術感興趣者的自學教材。
『叄』 一個程序員需要有怎樣的自我修養
作為一名程序員,一個「程序員的自我修養」是什麼?
盡管我們不一定要像尹天仇那麼的認真對待自己的事業,但,一些基本的修養,作為一名新時代的碼農,總應該是要具備的吧。不過真要說修養,方面還是挺多的,技術自我提示自不必說。但我並不打算從這個大家都覺得理所當然的技術方面入手,而是談談,可讀性代碼,這個容易被大家忽視的基本素養。
1、遵從所在團隊的代碼規范。
一個高效、成熟的團隊,必定有一個屬於自己的代碼規范,這個規范是團隊的寶貴的財富,它是整個團隊從各種坑中爬起來後積累的經驗教訓。什麼是規范,它是人們從無數經驗中總結出來的規則,標准。而代碼規范,指導團隊成員如何以最短的時間寫成最高效,可讀性強的代碼。試想,如果成員不遵從規范,你用駝峰命名,他用下劃線,這對程序的可讀,將造成多大的影響。我想,應該沒有一個人願意去閱讀一段,各種變數命名形式都能見得到,private, public 方法隨意排序,甚至常量類都散落在各個角落的代碼吧。
代碼,一個作用是讓機器閱讀,另一個重要的作用是讓人閱讀!!!
2、遵從行業內通用的規范
在團隊的代碼規范未涉及到的,那請按照行業內的規范來編寫代碼。規范的一個好處是,可以明顯減少學習和交流成本。在java中,當我們看到全大寫的變數名時,我們就知道這是常量,而不需要去看注釋,不需要去看代碼邏輯。為什麼這么迅速,因為行業里大家都習慣把常量用大寫命名。但假如你用其他命名方式命名常量,比如team_nums命名常量,不僅不能讓人迅速知道這是個常量,而且可能讓人誤會這是個變數,增加了團隊成員學習和溝通成本,甚至可能誤導他們。就見過一位仁兄,明明用的是工廠模式,偏偏按模版模式的命名方式來命名,問他,他說他知道這是工廠模式,但他覺得,更應該叫模版模式。。。我的天,,你這么任性,以後還能做朋友么?
舉個例子,我們需要根據支付類型,來生產多個支付產品,於是,我們寫了個工廠類,命名為FactoryPay。當其他人看到一個類叫FactoryPay,他們會猜測,這應該是個工廠類,負責生產各種支付產品的工廠,然後按照這個猜測去閱讀代碼,就能比較快速的理解整個類的作用。但是,假如我取名PowerPay,別人還不知道是啥,看了半天,才明白,這是個工廠的作用。這就明顯增加了他人的學習成本和維護代碼的成本。
不管你是新手還是老鳥,務必了解施行行業規范,切勿為了標新立異而違反規范。這么低端的裝逼,就沒必要採用了,要裝也寫個高端的框架來提升逼格唄。
3、變數、方法命名要能表達變數作用
在程序員這個圈子很久了,就發現,程序員這貨,都喜歡這套,「這個介面幹嘛用的,有文檔么」,「自己看代碼去」。很多時候都是一臉黑。
盡管程序員閱讀別人代碼技術都是一流,不管你是有沒有注釋,不管你是怎麼循環嵌套,也不管你是怎麼命名,他們都能耐心的,把代碼分析個所以然來。但,對於程序員這個視時間寶貴如生命,分分鍾都能創造幾百萬價值的群體來說,您行行好,給我們省點時間吧,把變數是幹啥用的,說清楚唄,沒准節省的這幾分鍾,多賺個幾萬,還能請大家出去嗨呢。
每每看到部門的某大神,用一個神一般的變數名「flag」,我就有吐血的沖動,他還這個flag一直雪藏,不用,只是傳遞到第n個方法才使用,頓時心力交瘁,我的天,這個flag都是是幹嘛用的啊,後來才明白,是isPay的意思,用來標識用戶是否支付成功了。當時一口老血吐屏幕上,心裡狂吐槽,老兄,你命名個isPay會死么,我的腦細胞這么不值錢么。到後來看到,去魔法數字,用int NUM_7 = 7,而不是MAX_MEMBERS來表示最大成員、用x y z來命名變數名,各種只有作者,或者作者後來都忘了的獨特命名方式,都見怪不怪了。更有甚者,一個變數命名為passed,作用居然是「未通過」的意思,當時就石化了,作者還真是用心良苦,這都要考我細心不細心。
一個好的變數名,能幫助閱讀者了解變數的作用,也輔助了對整段代碼的理解。
4、不要show英語,鄉下的孩子傷不起唉
LZ所在的團隊,英語一直都是團隊的硬傷,但總是能看到,某位仁兄,加上大把大把的英文注釋,有些變數名也取些高大上的復雜的英語單詞。敢問,你這么高的逼格,以後我們怎麼和你玩啊。(那位仁兄其實就是LZ,年輕時唉,罪過罪過)
代碼是用來溝通的,傳遞作者意圖的,都看不懂,怎麼溝通交流。建議英語好的童鞋,英語能力可以放到閱讀英文書籍中展示,在代碼中,如果團隊英語能力很弱,避免使用英文,變數命名也盡量按照團隊英語水平來命名
5、添加必要的注釋
正如上面LZ說的,經常遭遇「你仔細看看代碼,就知道幹嘛用的」這樣的神回復。盡管閱讀代碼是每個程序員的強項,但必要的注釋,比如邏輯比較復雜的地方,添加必要的注釋,對提升團隊成員閱讀熟悉代碼的效率是有很大幫助的。試想,一個類,幾百行,沒有一行注釋,對於閱讀者來說,閱讀它將是一個多麼恐怖的事。
6、注釋保持簡潔,避免沒有必要的注釋
即看過一行注釋都沒有的代碼,也看過注釋比代碼還要多的程序。一個是讓人生不如死,一個是讓人痛不欲生。(唉,有時不僅感嘆,在程序員界混,真的是難)。
LZ就經常看過,一大段注釋,啰嗦了半天,要不就是沒表達清楚重點,要不就是只為說明它是個循環的作用!!!譬如i++這樣的代碼,有必要加個「每個計數增加1」這樣的注釋么,這完全是把讀者定位為非程序員啊,或者就是嚴重鄙視讀者的編程水平。
注釋是幫助閱讀的人更好的理解程序的邏輯,只是輔助,如果不重視通過命名等方式來傳遞代碼的作用,而是依賴於注釋,這就是本末倒置了。而且,冗長啰嗦的注釋,這到底是幫助人理解,還是阻礙人理解啊,是讀程序還是讀小說啊。
7、擁有自己的編碼規范
規范是為了讓團隊更快的理解、熟悉代碼的,同理,擁有自己的一套規范,就能幫助其他人更快的理解我們所寫的功能,減少學習和溝通成本。
8、代碼清晰簡潔的表達出作者的意思
在我們每次寫完一段代碼時,一定要問問自己,代碼是否表達清楚了我的意思,是否需要添加些注釋,名字取得是否恰當了,別人在閱讀時是否吃力。。每每看到別人一團糟的費解的代碼,就時刻提醒自己,一定要把代碼寫好咯,我也確實是這么做的,一遍又一編的檢查,看變數名、方法名是否表明了它的用途,是否有些不必要的、只是為了提升逼格的代碼,別人是否能在短時間內看懂。所有的這些,只是為了寫出一段更優美的代碼。
9、堅持並捍衛上面的准則
經常能聽到,有些公司是代碼行數來定義績效的,但作為一個有操守,並秉承基本自我修養的程序員,我們絕不能為了各種誘惑或者脅迫,甚至是自己的惰性、個性,而放棄寫出簡潔清晰,可讀的代碼。
以上的幾點,並不是嚴格的意見或者建議,只是提醒廣大程序員同胞們,在痴心與高端的技術時,千萬不要忘了,代碼不僅機器要閱讀,人也需要閱讀。就算你寫出再復雜的代碼,但它讓人完全無法閱讀,這有什麼用呢。這就如同,你很牛逼很牛逼,但別人聽不懂你說的話,還不是沒用。如果你真的寫出了可讀性強的代碼,但你也不應該鳴鳴得意,我覺得,寫出一段優美,健壯,可讀性高的代碼,是一個程序員最基本的自我修養。
『肆』 程序員的自我修養的作品目錄
第1部分 我叫邵總
第1章 溫故而知新
第2章 編譯和鏈接
第3章 目標文件里有什麼
第4章 靜態鏈接
第5章 WINDOWS PE/COFF
第3部分 裝載與動態鏈接
第6章可執行文件的裝載與進程
第7章 動態鏈接
第8章 LINUX 共享庫的組織
第9章 WINDOWS 下的動態鏈接
第4部分 庫與運行庫
第10章 內存
第11章 運行庫
第12章 系統調用與API
第13章 運行庫實現
附錄
索引
『伍』 python 是否有能列出動態鏈接庫中有哪些方法的庫
最近看了《Gray hat python》一書,這才知道為什麼python是黑客必學的編程語言。通過python的ctypes模塊,可以直接調用動態鏈接庫中的導出函數,而且甚至可以直接在python中構建出復雜的C結構體!!!使得python也具備了底層內存操作的能力,再配合python本身強大的表達能力,能不讓人激動么。
之前為了在python中調用動態鏈接庫導出的函數,你需要自行解析出這些導出函數的地址。而現在ctypes庫會替我們完成這個麻煩的過程,大大方便了我們直接在python中調用C函數的能力。
ctypes模塊中有三種不同的動態鏈接庫載入方式:cdll, windll, oledll。不同之處在於鏈接庫中的函數所遵從的函數調用方式(calling convention)以及返回方式有所不同。
cdll用於載入遵循cdecl標准函數調用約定的鏈接庫。windll則用於載入遵循stdcall調用約定的動態鏈接庫。oledll與windll完全相同,只是會默認其載入的函數會統一返回一個Windows HRESULT錯誤編碼。
先復習一下有關函數調用約定的知識:函數調用約定指的是函數參數入棧的順序、哪些參數入棧、哪些通過寄存器傳值、函數返回時棧幀的回收方式(是由調用者負責清理,還是被調用者清理)、函數名稱的修飾方法等等。基本上我們最常見的調用約定就是cdecl和stdcall兩種。在《程序員的自我修養--鏈接、裝載與庫》一書的第10章有對函數調用約定的更詳細介紹。
cdecl規定函數參數列表以從右到左的方式入棧,且由函數的調用者負責清除棧幀上的參數。stdcall的參數入棧方式與cdecl一致,但函數返回時是由被調用者自己負責清理棧幀。而且stdcall是Win32 API函數所使用的調用約定。OK,就這么多,夠了。
測試一下在Linux平台和Windows平台下通過ctypes模塊導入C庫中函數的小例子:
Windows 下:
from ctypes import *
msvcrt = cdll.msvcrt
msg = "Hello world!\n"
msvcrt.printf("Testing: %s", msg)
Linux下:
from ctypes import *
libc = CDLL("libc.so.6")
msg = "Hello, world!\n"
libc.printf("Testing: %s", msg)
可以看到動態鏈接庫中的printf被直接導入到python中來調用了。
那麼,在python中怎麼表示C的類型?不用擔心,下面這張表就能搞定。
有了這個映射關系,多復雜的C類型也能在python中表達出來。
在C中定義一個聯合:
union
{
long barley_long;
int barley_int;
char barley_char[8];
}barley_amount;
而在python中同等的定義為:注意一下python中定義數組的方式。
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char * 8),
]
測試一下這個例子,在python中定義一個聯合體,為其賦值,再分別訪問其成員。
from ctypes import *
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char * 8),
]
value = raw_input("Enter the amount of barley to put into the beer vat:")
my_barley = barley_amount(int(value))
print "Barley amount as a long: %ld" % my_barley.barley_long
print "Barley amount as an int: %d" % my_barley.barley_int
print "Barley amount as a char: %s" % my_barley.barley_char
『陸』 求《程序員的自我修養鏈接裝載與庫》全文免費下載百度網盤資源,謝謝~
《程序員的自我修養鏈接裝載與庫》網路網盤pdf最新全集下載:
鏈接: https://pan..com/s/1TIAvLwNe3A65hsVu4LIJxg
簡介:本書對裝載、鏈接和庫進行了深入淺出的剖析,並且輔以大量的例子和圖表,可以作為計算機軟體專業和其他相關專業大學本科高年級學生深入學統軟體的參考書。同時,還可作為各行業從事軟體開發的工程師、研究人員以及其他對系統軟體實現機制和技術感興趣者的自學教材。
『柒』 對於程序員的自我修養這本書,你怎麼看
還不錯的一本書,主要講了裝載,鏈接,庫,三個方面,是程序員調試程序,了解程序運行原理不可多得的一本好書,推薦!!!
『捌』 請高手推薦學習操作系統的好書及學習方法
《程序員的自我修養——鏈接、裝載與庫》
這本書介紹系統軟體的運行機制和原理,涉及在Windows和Linux兩個系統平台上,一個應用程序在編譯、鏈接和運行時刻所發生的各種事項,包括:代碼指令是如何保存的,庫文件如何與應用程序代碼靜態鏈接,應用程序如何被裝載到內存中並開始運行,動態鏈接如何實現,C/C++運行庫的工作原理,以及操作系統提供的系統服務是如何被調用的。
『玖』 作為一個程序員,有哪些職業自我修養呢
保持對代碼的熱情,持續學習
從現實工作的角度考慮,這可能不是最重要的一條,畢竟持續學習這類話,已經老生常談到快厭煩的地步了。比起學習,可能在每個寫過的類裡面加上你的注釋,甚至包括姓名還有聯系方法更實用些。但我想說的是,保持對本行業持續熱情,並不斷學習,應該是每個從業者最基本的素養,而對於程序員來講,就是保持對代碼的熱情。
當然,這里的保持熱情、不斷學習,並不是一句空話,而是要真正做到的。從長遠角度講,只有不斷提升自己的專業技能,才能更好的應付將來對本職工作提出的新的要求。同時,對於自己來講,也只有不斷學習,才能讓自己時刻保持出色的競爭力。
養成良好的工作習慣
從編程本身來講,盡量寫出邏輯嚴密的代碼,多寫注釋,少留bug,不要給後人挖坑,經常做代碼評審(code review)和代碼重構(code refactoring)。一個好的編程習慣可以提高效率、減少重復工作等等。從職場溝通角度來講,養成即時反饋,定時匯報的工作習慣,可以避免因為溝通不暢導致多走許多彎路。
因此,對於一個程序員來講,養成一個良好的工作習慣,也因該是作為一個程序員的基本自我職業修養。(高中上完想學電腦編程,電腦編程培訓多少錢)
及時調整心態的能力
大多數程序員的工作,都是項目制的。在項目期間,996甚至007都是常態,因此可能會有比較大的壓力。同時,在項目實施過程中,遇到挫折也要及時調整心態重整旗鼓。所以,作為一名碼農,也要有及時調整心態的能力。
『拾』 C文件如何成為可執行文件(編譯、鏈接、執行)——摘自《程序員的自我修養》
本文算是我閱讀《程序員的自我修養》(俞甲子等著)相關章節的筆記,文中直接引用了原書中的敘述,強烈建議大家去看原書,本文只做概要介紹而用。——註:文中有很多引用圖的地方,請大家自己去找原書看,支持正版!我遇到一個問題,Linux C編程中的問題:.. char *p; unsigned int i = 0xcccccccc; unsigned int j; p = (char *) &i; printf("%.2x %.2x %.2x %.2x\n", *p, p[1], p[2], p[3]); memcpy(&j, p, sizeof(unsigned int)); printf("%x\n", j); ... Output: ffffffcc ffffffcc ffffffcc ffffffcc 0xcccccccc My questions are: 1. Why it prints "ffffffcc ffffffcc ffffffcc ffffffcc"? (if p is unsigned char* then it will print correctly "cc cc cc cc") 2. Why pointer to char p copied to j correctly, why not every member in p overflow? since it is a signed char. 這是別人在郵件列表中提出的問題,在試圖回答這個問題的過程中,突然發現,自己對連接器的工作並不熟悉,因此拿來好書《程序員的自我修養》來看,並做如下匯報,強烈推薦《程序員的自我修養》!!!寫好的C語言文件,最終能夠執行,大致要經過預處理、編譯、匯編、鏈接、裝載五個過程。預編譯完成的工作: (1)將所有的"#define"刪除,並展開所有的宏定義 (2)處理所有條件預編譯指令 (3)處理#include預編譯指令,將被包含的文件插入到預編譯指令的位置,這個過程是遞歸進行的。 (4)刪除所有的注釋 (5)添加行號和文件名標識,以便調試 (6)保留所有的#pragma編譯器命令,因為編譯器需要使用它們。編譯完成的工作: (1)詞法分析 掃描源代碼序列,並將其分割為一系列的記號(Token)。 (2)語法分析 用語法分析器生成語法樹,確定運算符號的優先順序和含義、報告語法錯誤。 (3)語義分析 靜態語義分析包括生命和類型的匹配,類型的轉換;動態語義分析一般是在運行期出現的與語義相關性的問題,如除0錯。 (4)源代碼生成 源代碼級優化器在源代碼級別進行優化:如將如(6+2)之類的表達式,直接優化為(8)等等。將語法書轉換為中間代碼,如三地址碼、P-代碼等。 (5)代碼生成 將源代碼轉換為目標代碼,依賴於目標機器。 (6)目標代碼優化匯編完成的工作: 將匯編代碼變成機器可以執行的指令鏈接完成的工作: 鏈接完成的工作主要是將各個模塊之間相互引用的部分處理好,使得各個模塊之間正確銜接。鏈接過程包括:地址和空間分配、符號決議和重定位。 首先講靜態鏈接,基本的靜態鏈接如下: 我們可能在main函數中調用到定義在另一個文件中的函數foo(),但是由於每個模塊式單獨編譯的,因此main並不知道foo的地址,所以它暫時把這些調用foo的指令的目標地址擱置,等到最後鏈接的時候讓連接器去修正這些地址(重定位),這就是靜態鏈接最基本的過程和作用;對於定義在其他文件中的變數,也存在相同的問題。具體過程如下: (1)空間和地址分配 1)空間與地址分配:掃描所有輸入目標文件,獲得各個段的屬性、長度和位置,並且將目標文件中的符號表中所有的符號定義和符號引用收集起來,放到一個全局符號表中。 2)符號解析和重定位:使用第一步收集到的信息,讀取輸入文件中段的數據、重定位信息,並進行符號解析與重定位、調整代碼中的地址等。 動態鏈接的過程更為復雜,但是完成的工作類似。 動態鏈接的初衷是為了解決空間浪費和更新困難的問題,把鏈接過程推遲到運行時進行 首先介紹一個重要的概念——地址無關代碼。為了解決固定裝載地址沖突的問題,我們希望對所有絕對地址的引用不作重定位,而把這一步推遲到裝載的時候再完成,一旦模塊裝載地址確定,即目標地址確定,那麼系統對程序中所有的絕對地址引用進行重定位。同時我們希望,模塊中共享的指令部分在裝載時不需要因為裝載地址的改變而改變,所以把指令中那些需要被修改的部分分離出來,跟數據放在一起,這樣指令部分就可以保持不變,而數據部分可以在每個進程中擁有一個副本,這種方案目前被稱為地址無關代碼(PIC,Position-independent Code)。 我們需要解決如下四種引用中的重定位問題: 1)模塊內部調用或者跳轉:這個可以用相對地址調用或者基於寄存器的相對調用,所以不需要重定位2)模塊內部數據的訪問:用相對定址的方法,不過鏈接器實現得十分巧妙: call494 <__i686.get_pc_thunk.cx> add$0x188c, %ecx mov$0x1, 0x28(%ecx) //a=1 調用一個叫做__i686.get_pc_thunk.cx的函數,把call的下一條指令的地址放到ecx寄存器中,接著執行一條mov指令和一個add指令3)模塊間數據的訪問:在數據段里建立一個指向全局變數的指針數組,也成全局便宜表(GOT),當要引用全局變數時,可以通過GOT相對應的項間接引用: GOT是做到指令無關的重要的一環:在編譯時可以確定GOT相對於當前指令的偏移,根據變數地址在GOT中的偏移就可以得到變數的地址,當然GOT中哪個每個地址對應於哪個變數是由編譯器決定的。4)模塊間的調用、跳轉:採用上面類似的方法,不同的是GOT中相應的項存儲的是目標函數的地址,當模塊需要調用目標函數時,可以通過GOT中的項進行間接跳轉。 地址無關代碼小結: 現在,來看動態鏈接中的另一個重要問題——延遲綁定(PLT)。當函數第一次被用到時才進行綁定,否則不綁定。PLT為了實現延遲綁定,增加了一層間接跳轉。調用函數並不是通過GOT跳轉的,而是通過一個叫PLT項的結構進行跳轉的,每個外部函數在PLT中都有對應的項,如函數bar,其在PLT對應的項的地址記為bar@plt,實現方式如下: bar@plt: jmp* (bar@GOT) pushn pushmoleID jump_dl_runtime_resolve 鏈接器的這個實現至為巧妙: 如果在連接器初始化階段,已經正確的初始化了bar@GOT,那麼這個跳轉指令的結果正是我們所期望的,但是,為了實現PLT,一般在連接器初始化時,將"pushn"的地址放入到bar@GOT中,這樣就直接跳轉到第二條指令,相當於沒有進行任何操作。第二條指令「pushn」,n是bar這個符號引用在重定位表「.rel.plt」中的下標。接著將模塊的ID壓棧,跳轉到_dl_runtime_resolve完成符號解析和重定位工作,然後將bar的地址填入到bar@GOT中。下次再調用到bar時,則bar@GOT中存儲的是一個正確的地址,這樣就完成了整個過程。 在鏈接完成之後,就生成了你要的可執行文件了,如ELF文件,至於這個文件的詳細的信息,可以參考相關的文檔。 現在,你要運行你的可執行文件,這是如何做到的呢? 我們從操作系統的角度來看可執行文件的裝載過程。操作系統主要做如下三件事情:(1)創建一個獨立的虛擬地址空間,但由於採用了COW機制,這里只是復制了父進程的頁目錄和頁表,甚至不設置映射關系(參考操作系統相關書籍)。(2)讀取可執行文件頭,並且建立虛擬空間與可執行文件的映射關系。(3)將CPU的指令寄存器設置成可執行文件的入口地址,啟動運行。我們來看一下執行過程中,進程虛擬空間的分布。 首先我們來區分Section和Segment,都可以翻譯為「段」,那麼有什麼不同呢?從鏈接的角度來講,elf文件是按照Section存儲的,從裝載的角度講,elf文件是按照Segment存儲的。」Segment」實際上是從裝載的角度重新劃分了ELF的各個段,將其中屬性相似的Section合並為一個Segment,而系統是按照Segment來映射可執行文件的。