今天終於做了一個決定,就是要開始寫自己的博客了,其實是想將自己的學習心得發表出來,以幫助那些需要幫助的朋友,我的一個小小的夢想就是把這些文章整理成書進行發行。
說到這個技術話題,其實范圍是很廣的,許多朋友在進入IT行業的時候肯定是一頭露水,不知道自己將來是做什麼,或者為什麼選擇了IT這個行業,在這裡我僅舉幾個例子,說大的范圍如 移動、研發、雲計算、服務器、硬件、軟件、操作系統等等,說這些其實也只是一個簡單的介紹,具體到某一個具體的分類,還有許多,大家可以把它想像成一棵樹,分了幾個大的枝干,每個枝干又會分出許多小枝干,每個小枝干又會分出小小枝干,直到最後長出一些葉子,而每個葉子 也就可以理解成一種IT人員的工作類別吧,關於IT,其實有許多工作行業,大家可以登錄51JOB等網站進行查看,而我們現在討論的僅僅是windows平台下 程序設計這一小塊的某個小枝節,大家不要以為做IT的就是編程,這是一種極不正確的理解,所以認識到自己是位於什麼位置是非常重要的。這其實就是一種概念的體現,叫做分層,也可以理解成分工,有話老話說的好呀,社會需要分工,的確是這樣的。
那麼我們研究的現在是WINDOWS下的C程序語言,其實這個主要是讓大家更好理解一下什麼是程序,以及程序員是做什麼的。就像普通工作一樣,程序員也是分為三六九等的,大家看招聘網站的時候,有的公司可能會寫招聘 某某高級軟件工程師 有的可能是中級,這就意味著大家要擺正自己的態度。這就像是工廠招人的時候 會有 普工、焊工等分類一樣,普工待遇低,技工待遇高。許多人感覺只要把技術搞上去就行了,其實不然,在計算機這個領域,技術是一個非常復雜的話題,不要以為你可以學到家。這個可以通過在國內查找一些資料就可以看出來,當你工作中遇到某個問題的時候,需要查找一些相關資料,國內基本上找到的文章就那麼幾篇,而且都是簡單介紹,基本沒有什麼實用價值。有點扯遠了,還是回到這篇文章上來吧。其實我標題寫個01意味著這是一個系列,看完這個系列,我可以保證你對C語言的認識達到一個新的高度,但是你需要有一些基本的語法常識。好了,我們開始吧。
在開始之前,我希望大家隨便找一本書,看看C語言的歷史,這是上世紀70年代的產物,我們現在仍然在用,其實程序設計在國外50年代的時候就已經開始了,在國內基本上得到90年代左右了,所以,我們落後於其它一些國家,也就是國內對底層的了解不夠,以至於到現在國內一直沒有自己的操作系統、CPU等,又扯遠了。
一句話,我們知道CPU執行的0 1 ,那麼今天的第一個話題就得先解決掉 01 。
我們應該知道,進制只是一種表示方法,我們只是習慣於用十進制來表示數據,如 152 、 521、652541 等,這些都是十進制,也就是逢十進一的機制,當我們遇到電氣特性的時候,比如電壓有高低之分,也就可以用01來表示這種電氣特性,當然我們沒有必要研究那麼深入,只需要知道這些就可以,而CPU 只認識 二進制,也就是CPU 在執行的是N多個01的組合,有關進制的轉換我不打算再做介紹,希望大家可以找一些資料自己攻破這個難題,如 14 表示成 1110 ,我下面要介紹的是這裡面的一些數據關系。
先來講一個小插曲,說古代有一個國王,抓到了幾個罪犯,要判他們刑,但給他們一次機會,國王給他們出了一道題,國王拿出1000顆珍珠,和10個盒子,讓罪犯把這1000顆珍珠放入這10個盒子中,而且正好放完,等罪犯放好之後,國王的要求就是 國王隨便說一個 1 - 1000 的整數,如 516,罪犯要從 那 10個盒子中 選中 若干個,讓這幾個盒子中的珍珠總數=516。 就這麼一個問題,我們需要好好的來看一下,至於這個問題怎麼解我就不做解釋了,只是把答案放出來,這個就是 一個 以2為比例的等比數列,也就是:12481632641282564891000-前幾個數的和) 如,67=64+2+1 516=489+16+8+2+1 隨便一個1-1000的整數都可以寫出一個唯一的求和公式。
其實大家自己寫出來這個等比數列的和就可以得到一個結論,就是 前N 項的和等於第N+1項的值-1 如,1+2 = 4-1 1+2+4+8=16-1 這個特性希望大家一定要記住。
我們將其用指數形式表示出來
上面的這些數值大家也要背下來,其實並不難背吧,只需要記一些比較關鍵的就行了,如2^10=1024 那麼2^9=? 除以2就是了,但這些數值一定要背會。
下面我們就可以用它來做一些簡單的測試了,如2^0+2^1+...+2^64 = 多少? 結果 2^65-1 , 想不明白?你應該再看一下上面的知識了。
有了這些東西後,我們就可以來講一些知識了,在計算機中,我們講的是32位系統及32位的編譯系統,最好就是用 XP系統下的VC6.0 ,我建議大家都用這個環境進行學習,
並不是因為它有多好,而是因為...,大家以後就會明白的。
c語言裡有一個關鍵字 叫 sizeof ,它可以用來測試一種類型占用的字節數的大小。如 sizeof(int) = 4 sizeof(char)=1,這裡就引出一個問題,字節數,什麼意思,何解呢?
其實是這樣的,我們知道我們的程序是需要被操作系統加載到內存中才能被CPU訪問到我們程序中的數據和指令,那麼內存這個東西就顯得很重要了,我們可以在我的電腦上右擊,選擇屬性,可以查看到自己的內存容量 ,如,2GB, 我們先來解釋一下G是什麼,它就像中國的K一樣,1Kg=1000g 把g約掉,1K=1000 ,其實就是這麼回事,1G=1024M,1M=1024K,1K=1024,明白了吧,我們在描述重量的時候用的是g這個單位,也就是說B才是內存容量的基本單位,GB只能算是一個擴展單位,那麼再來討論一下B,B就是字節的意思,1B=8b,b就是0/1的意思,也就是說,8個0/1的組合就是一個字節,內存的基本單位是字節,這裡我還要再強調一下什麼是單位,就是最小的了,沒有特殊需要不要再拆分的意思,所以,我們以後會經常和B打交道,很少和b打交道,既然知道了我們的內存容量了,如2GB,512MB,或者3GB,或者4GB,那麼我們總是可以把它換算成 xB,x代表一個整數,意思是我們現在有了很多多個字節的意思,那麼怎麼管理這麼多個字節?這個問題可能不怎麼好解決,我先來舉一些例子,中國人多吧,13Y人口,政府怎麼管理這麼多人呢?身份證ID。那麼多的汽車、火車、飛機怎麼管理,車牌與航班號。賓館有幾十個房間怎麼管理,房間ID。類似的例子太多太多,那麼多企業,工商局怎麼管理,那麼多玩家,游戲運營商怎麼管理,那麼多數據包,TCP協議怎麼管理等等。這些例子可以給我們一思路,我們只要給他們貼一個標簽就行了吧,或者叫為每1個字節 賦一個ID 就行了吧,而我們的 CPU 在通過地址總線(可以理解為32根電線,每根上可以有高電壓(5V)和低電壓(接近0V)兩種狀態)訪問的內存的時候 正好提供了這樣32個狀態信號,也就是32個0/1的一個排列,這樣我們為我們每個字節都定義一個這樣的ID就行了,這個ID就是傳說中的 物理地址了啊。32個0/1的排列,意味著它的大小是4B,也就是在32位系統及32位編譯環境中 我們的地址都是4B,有的人可能不理解為什麼我說32位環境及32位編譯環境呢?用過TurboC的人可能會知道,TurboC裡的整數是2B,而在VC中是4B,所以要有這兩個限定條件。舉個例子,假如我們有 512MB(536870912B)的物理內存,我們就可以像下面這樣子進行編號
希望上面的數據不是很難理解,如果上面的東西你理解了,那麼左邊一列我們可以將其稱為物理地址,大家可以總結出來的一條理論是 一個地址對應一個字節很重要)
現在講一下 2 -8 -16 這三種進制之間的轉換吧,
我們知道 3 個2進制可以表示的范圍是 0-7 ,4個2進制可以表示的范圍是 0-15
1個8進制可以表示的范圍是 0-7 , 1個16進制可以表示的范圍是 0-15
所以我們經常會做如下的轉換
如 2進制數據 如十進制數415242 轉成2進制是(可以用電腦自帶的計算器) 1100101011000001010 ,很顯然 它不夠32位,這就像我們寫十進制數一樣,在前面加0是沒有意義的,所以將前面的0省略掉了,我們一般需要將它補齊
先來看轉成8進制,先將數據分成3個3個的,從右邊開始分,左邊不夠的被0,分完後如下
2進制001 100 101 011 000 001 010 現在我們只需要將每3個對應的8進制數據寫出來即可了
8進制 1 4 5 3 0 1 2 1453012 這就是我們的結果了
再來看轉成16進制,先將數據分成4個4個的,從右邊開始分,左邊不夠的被0,分完後如下
2進制 0110 0101 0110 0000 1010現在我們只需要將每4個對應的16進制數據寫出來即可了
16進制 6 5 6 0 A 6560A 這就是我們的結果了
那麼如果你看到這裡也沒有什麼問題,那麼恭喜你,你已經進入C語言的大門了, 我沒有說假話,You must trust me!
總結一下,我們現在學會了哪些,知道內存的基本單元是字節,知道系統管理內存字節們(請允許我加上們,表示復數)是通過編號的方式了,知道了 進制之間的轉換方式了。
那麼我們來看一下其它的一些概念
我們知道計算機是用來進行計算用的,那麼數據是要存儲在內存中才能被CPU訪問到,但是,數據在內存中到底是怎麼表示的呢?先來討論最簡單的數據,最簡單的就是char型數據,我們知道用sizeof關鍵字可以統計出它占一個字節的內存空間,那麼我們就拿這一個字節來說事,首先要糾正許多人的一個錯誤認識,我們知道在內存中的某個地址上的數據就是那個數值,但是它是什麼意義我們卻是無從知曉的。舉個例子,我們舉例的地址是 0X0012FF7C(其實就是2進制),如下
就像上面的數據,是連續的四個內存地址,其值分別是十六進制 的 41 5A 60 6B ,但是它們是什麼意義,我們卻是不知道的。
當我們在 程序中定義一個char 型變量的時候,其實就是在申請了 1B 的內存,我們可以為它賦值,如 char c = 'A' ; 我們申請了1B,將這1B 賦以 'A' 對應的16進制。 還有一點要說明 char 和 unsigned char 是兩種數據類型,是不一樣的。
我們先來看 char 型 ,占1B 對應 8b
xxxx xxxx x=0/1
上面的表達式表示的就是通用二進制表示形式
我們知道,給定一個數值,我們可以寫出其2進制,如 18 = 16 + 2 0001 0010 這就是它的表示形式,我們可以將這個數值放入內存中,但是這是不是一種通用的方式呢,換句話,用它們進行計算能符合計算公式嗎?還有負數怎麼表示呢?
先來看 -18 如何表示,計算機前輩們首先想到一個解決辦法,我們可以把最高位(最左邊的位) 拿出來,用來表示這個二進制表示的是一個正數還是一個負數,如
0000 0010 表示 +2
1000 0010 表示 -2
從上面,我們就可以看出來,這樣子犧牲了數值的表示范圍,因為 最高位不能用來表示 2^7 了,但這並不重要,重要的是它違背了計算規則,我們來看一下將兩個數據進行相加 的結果
這個計算結果,按照我們上面的定義,首先是負數(最高位為1),數值(後面7位)為36 也就是 -36 ,所以說,它違背了計算規則。
看來計算機前輩走的第一步是不正確的,那麼如何解決這個問題呢?
我們的目的是得到0 ,那麼我們如何得到0呢?
我們知道
注1:這裡,其存儲長度為8b,最左邊的1自然丟棄,這可能會是電氣特性(我們不需要關心) ,這樣我們就得到0了
通過上面的例子,我們只要能構造出一個 1111 1111 ,然後再加個1 就可以得到0 了。那麼我們就要想怎麼得到1111 1111 ,很顯然,這一步是很簡單的,來看下面的式子
整合一下上面的等式
從上面這個等式可以得出 -18 了吧,-18 的表示應該是 將+18 按位取反再加1(得到補碼),這樣我們就可以統一了它滿足了我們的計算公式。
現在,我想,我們應該知道補碼是怎麼得來的了,那麼,我再告訴你一個秘密,計算機在內存中存儲整型(整數類型 如char,short,int,long)的時候就是用這種方式存儲的哦。
好了,現在,我現在再給一個例子,上面我們討論的是8位的,現在我們來討論一個32位的,-615427545,隨便敲的一個負數
1、先求正數對應的二進制100100101011101010110111011001,然後劃分成4個4個的,左邊不夠的補0
DB 51 52 27這就是四個字節(連續的兩個16進制表示1個字節),在內存中存儲的形式。
緊接著問題也出現了,這四個字節,如果放在起始地址為 0X0012FF7C 的位置上,怎麼保存呢?
方式A
方式B
上面是兩種存儲方法,這東西並不是我們去實現,也不是我們去管理,而是由硬件決定的,不同的硬件平台會有不同的表示方法,我們所用的基本上是方式B,這種方式有一個名字,叫做小端模式,那麼可想而知,方式A就叫大端模式了,那麼我們怎麼去記憶這些呢?其實我們只需要記住小端模式就可以了,有一個很好記的公式:高高低低,什麼意思呢?地址是從0 到 一個很大的正數,那麼也就有了大小,如上面,0X12FF7C 相對0X0012FF7F是小地址,也叫低地址,那麼0X12FF7F就叫高地址了,DB 51 52 27這4個字節,也有高低之分,位於高位的叫高地址,也就是DB,位於低位的叫低地址,也就是27,那麼你也就基本可以理解了,高地址存放高字節數據,低地址存放低字節數據,好理解吧,等我們以後學了指針,再來討論如何判定一個平台是采用的什麼存儲方式。其實現在可以先說一下思想,我們就定義這樣一個整數,還是剛才那個整數,如果我們可以取到 0X0012FF7C 這個字節對應的值,暫時定義為X,我們就可以做出判斷了吧,如果X等於0X27 ,那麼就是小端,如果X等於0XDB,那麼就是大端喽,當然我們一般沒必要定義這麼大一個數據,一般用1來做這種判斷就可以了,我們現在所不會的只是怎麼取出來 X,這要我們學習指針後再來討論了。如果是1的話,我們可以寫出它的大小端存儲形式
小端模式
地址數值0X0012FF7C010X0012FF7D 000X0012FF7E 000X0012FF7F 00大端模式
地址數值0X0012FF7C000X0012FF7D 000X0012FF7E 000X0012FF7F 01是不是很簡單,很好理解吧。
理解了上面的知識點了吧,我們再來討論一個問題,范圍,如int 的范圍,unsigned char 的范圍
int 是有符號的,那麼最高位就是符號位,我們知道,右邊31位中 如果某一位的值為1,那麼它對應的值就是 2^n,但是符號位代表的是多少呢?其實跟右邊31位是一樣的,只不過代表的值是負的,什麼不理解?那麼我來解釋
注2:這裡的1代表 - 2^31 ,如果這是unsigned int 呢?那麼它就代表 2^31 很容易吧
那麼我們來討論int的取值范圍
最大值:int有正負之分, 首先可以確定的是最高位為 0 ,表示正數,後面的每個位都是 >=0的值,那麼肯定是當所有位為1的時候,此值最大,如下
0111 1111 1111 1111 1111 1111 1111 1111 其值是多少?要用上面的公式哦, 2^31-1 不理解?重頭再來一遍吧
最小值:肯定是負數哦,最高位為1,後面的每個位都是 >=0的值,一個負數加上任何正數都會使本值變大,所以後面 31 都應該為 0,如下
1000 0000 0000 0000 0000 0000 0000 0000 其值是多少,不用解釋了吧 -2^31
上面可以得到32位有符號整數范圍[ -2^31 , 2^31-1] , 換成16位,8位我想你也可以求出來吧
那麼無符號的呢?無符號意味著32個位都表示數值,所以最小為 32個0 ,最大32個1(值:2^32-1 還是公式哦)
在上面的講述中,我用了一個詞,物理地址,不知道大家在閱讀的時候有沒有注意到,為什麼要加上物理兩個字,這是因為還有另一個地址,叫邏輯地址,那麼邏輯地址是什麼意思?我們需要先來討論一下操作系統,我們的操作系統是在運行許多程序,你聽著歌可以浏覽網頁,可以打字,可以聊天,可以看視頻,當然操作系統的內核代碼也可由CPU來執行,如果將用戶寫的代碼和操作系統的代碼都直接爆露給用戶,那麼我們的應用程序一旦出現問題,你應該遇到過程序彈出錯誤對話框的情況吧,它所占用的內存數據可能會導致別的物理內存空間裡的數據出現錯誤,這就導致其它應用程序也崩潰,最嚴重的是導致操作系統的內核代碼被錯誤執行,遇到過系統藍屏的情況吧,一般是一些硬件驅動等與內核通信錯誤導致的,所以,我們的系統現在都是運行於 一種稱為 保護模式的 模式下,那麼保護模式保護什麼呢? 保護操作系統內核代碼不被用戶程序錯誤的訪問,保護程序A的數據不被程序B異常訪問。那麼操作系統現在就全權接受了這個任務,也就是我們的應用程序運行在自己的邏輯地址空間中,那麼這兩種地址之間是什麼關系呢?其實邏輯邏輯講的就是一種思維上的存在,它的范圍(0 --- 4G-1),它實際上還是用的物理內存,只是操作系統爆露給用戶的是一個別的地址,我們來看一下下面的數據
物理內存地址
程序A邏輯地址空間
0
1
2
....
....
4G-1
程序B邏輯地址空間
0
1
2
....
....
4G-1
程序A和程序B都需要物理內存,但是這些內存是由操作系統負責管理的,也就是A可能用的是A,C,E段,B可能用的是B,D段,但是A中A,C,E這些段的地址是邏輯性的,是由CPU的MMU采用某種算法映射到A中的,同樣B也是,但是A與B的空間具有獨立性,是不相關的,因為再返回物理內存中,對應的是由操作系統管理的ABCDE段,也就是說在A中是訪問不到在B中的數據的,這樣就起到了對程序數據的最基本的保護了,其實,在這4G的地址空間中,真正可以由用戶訪問的也只有2GB,當然可以啟用大地址,大家可以百度一下3GB大地址,一般服務器可能會用,但是現在的計算機配置已經很高了,基本上不怎麼使用,這2GB的數據是低2GB,那麼高2GB的地址空間用來干嘛呢?是操作系統運行代碼的映射,也就是大家可以理解成上面的G段映射到AB 的高2GB 地址空間了,用戶是無法訪問的。到這裡把物理地址與邏輯地址做了一個基本介紹,但是這個介紹是非常簡略的,也就是只提供一個模型,模型大家都理解的吧,我就不多解釋了。
說了那麼久的程序,到底什麼是程序呢?程序是指令與數據的集合,我們寫的就是指令,指令來處理各種數據,也就是算法,那麼這些只是小程序,大的程序,一般稱為解決方案,知道為什麼許多公司要你們提供解決方案嘛,說的就是這個東西,一個解決方案可以包含多個項目,每個項目完成一部分工作,現在VS系列(VISUAL STUDIO 2003 2005 2008 2010 2012 2013) 都是這種模式。我們暫時不用討論那麼復雜的,只討論一些簡單的就行了。
一個程序被操作系統加載到內存之後,系統主要是為它分配內存,然後將物理內存地址映射成邏輯地址空間,我們以後在討論的話,要記住都是在邏輯地址空間中的哦。
如果上面的東西你可以解理,那麼你是非常強的。01裡暫時就介紹這麼多。
本文出自 “千千阙歌” 博客,請務必保留此出處http://qianqianquege.blog.51cto.com/8004200/1304548