天天看點

Unix和Linux下的int

這也算和平同學的點題作文了,呵呵。

他對于Linux下很多時候,api函數大量使用int作為常用類型感到不解,問我,我呢,就試着回答一下哈。

問題如下:

根據<c++ primer>建議,表示像“容量”這樣的變量時,因為不可能出現負數,是以建議用size_t類型。但是linux下的程式,好像比較喜歡使用int。

為什麼不使用size_t(或者unsigned int)而使用int?表示範圍不是少了一半嗎?

我的回答:

嗯,這個問題算是比較偏門了,不過,我做過Windows開發,也做過Linux開發,兩個平台都用過,沒什麼道理哈,僅僅談點自己的感覺,嗯,不一定準确,大家有高見,還可以補充。

我覺得這個問題首先是一個文化問題。什麼叫文化,就是做這類事情的人們的一個通常的共識,就是大家都習慣這麼做事。我很早,嗯,07年開始轉向Linux開發的時候,就發現這點不同。

Windows呢,是微軟公司開發的,大公司,強調嚴謹的開發風格,大家可以從它推崇匈牙利命名法就看的出來。它對于變量命名,類型命名是有嚴格規定的,要求盡量準确,不給後來者留歧義。比如,很多Struct*,它會使用typedef顯式定名為一種新類型PStruct來管理,這樣,大家從字面上就可以一眼看出來,而不用到用的時候,跑去數星星。數星星很容易數錯的。我就幹過壞事,嘿嘿。

這個道理也很簡單,微軟是開發OS的,說白了,它的主要産品功能,除了Windows的操作功能,還需要提供大量的api給廣大程式員用,沒辦法啊,如果沒人給他的作業系統開發應用程式,他的OS賣不動的。

這就要求微軟不僅僅關注終端使用者的體驗,也特别關注程式員使用者的體驗,而明示的api,顯然是一種很好的使用者體驗,程式員不容易犯錯誤,被api的提供者限制着做正确的事情,程式員bug少,成功率就高,進而開發成本就低,于是,形成良性循環。

同時,這也為微軟的客服部門減少好多投訴哦,大家換位思考一下,如果微軟的api含糊不清,大家是不是要發飙?呵呵。

這裡面展現出來一個很重要的思想,微軟是把廣大應用程式員,也作為什麼都不懂的終端使用者在看待,試圖從api上建構最大的開發友好度,是以,它對于命名法很嚴格,api定名表意很清晰,同時,對于各種變量、類型,不厭其煩,多次定義,為了是讓各個行業的程式員, 用起來都盡量貼合自己的行業習慣。

這是有道理的,比如我現在在電力系統,我們定義變量類型,喜歡用Int16、Int32、Int64,Float16、Float32、Float64這類命名,int、double這些C/C++基本類型,反而不太流行。為啥,很多時候工業現場的人,不知道你這個類型到底是多少bits的,就會出錯。這是為了使用者看着清晰,也是行業習慣,是以,我很多時候開始寫程式前,都要先去定義這麼一批新的變量類型,友善和同僚們溝通代碼啊。

而Unix呢,不太一樣,我看過《Unix程式設計藝術》這本書,這裡面講了很多Unix程式員的文化。這麼說吧,我簡單點,Unix的開發者,預設使用者是和其水準相當的程式員,大家所有溝通的語言,都是計算機本專業的,大家很多時候用預設,暗示,就好了。

是以,Unix下的習慣,沒有那麼多分門别類的變量類型,大家還有個好習慣,呵呵,int包打一切。我看到幾乎大多數Unix的函數,就是gcc的基本庫,函數老是int來,int去,其實,給我的感覺,在Unix下,什麼都是int,為啥,Unix很講究把同類資源數組化管理,比如打開的檔案句柄,就是一個整數,程序ID,整數,線程ID,整數,使用者ID,整數,甚至,裝置都是整數表示。嗯,socket不說了,Windows下是一個特定資料類型SOCKET,而Unix下,你猜對了,沒錯,int,整數。

這叫什麼?其實是向量化管理,在系統内部檢索的時候,可以想象,Unix系統的開發者,大量使用數組,利用int這個整數在哈希檢索目标,達到效率最高。反正,我們不管什麼資源,在Unix内部,就是一個int型的ID表示,這其實已經是Windows句柄的概念了。

這兩種命名方法,其實各有優缺點,Windows的變量類型多,程式員學習的時候,成本高,但是,學會了就不容易出錯。而Unix的學習曲線低,沒那麼多類型名要去背,不過呢,用起來出不出錯,自理啊,Unix的開發者,相信大家的實力。嗯,隻是我自己有點信不過我自己,嘿嘿。

不過要我說呢,還真說不好誰對誰錯,其實都有道理,關鍵看OS的設計者,心目中的目标使用者的水準如何了。

回到和平同學的問題,用int,少一半,其實是有道理的,因為int是有符号的,有一半的表示範圍是負數,在表示很多容量的時候,比如說吧,malloc的數組,或者socket的表示範圍,确實負數沒有意義。是以,看起來被浪費了。

這其實不然,為什麼,原因很簡單。

int就算少一半,你能用多少?和平同學别見怪啊,我說句話,你問這個問題就表示還有點學生思維,總是想多多益善,就是我要把所有的資源都納入我的管理,呵呵。其實我們做工作做久了的,對于資料的邊界、範圍,反而有個思想,夠用就好,沒必要多。

int表示少一半,2G有吧,嗯,大多數使用者的數組,可不可能超過1G?其實,大多數時候,我們在這個地方用int,表示的範圍都很少的。少一半也夠用。socket不說了吧,理論上隻有65536,int再少幾半也夠用,呵呵。

夠用就行了,不用擔心的,真要是需要用大表示,自己做unsigned long來表示,也是可以的。

而且,Unix這麼設計,有個最大的優點,就是資料的自描述特性得到了前所未有的發揮。我們都知道,在表示範圍、容量的時候,int隻使用了正數部分,那負數部分做什麼?我來回答你,表示非法值,這個很重要。

試想一下,如果我們用的資料類型裡面,無法表示非法值,那麻煩了,api設計的時候,必須單獨設計一個參數,來傳遞非法值,這又涉及到&傳址調用,或者*直接傳指針調用,程式設計複雜度直線上升,設計的人也累,學的人也累,用起來更累。

這麼說可能不直覺,我舉個例子:

比如我要設計一個read函數,從某個檔案id讀取一段資料,大家注意啊,Unix下,檔案ID可以表示任何串行化裝置的。包括socket,可以用resv這類伯克利socket api,也可以直接使用C基本庫的read,都對,因為使用int表示,Unix可以把所有串行化裝置統一編址處理,用一類函數處理完。

函數原型我這麼設計,設計了兩個,大家比較一下:

Code:

int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax);   

unsigned int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax);   

可以吧,這雖然用了匈牙利命名法,但是,這是标準的C函數,沒錯吧。嗯,第一個應該是Unix的習慣,用int,第二個應該是Windows的習慣,用了無符号正整數,好,我們來看一種情況。

如果我們讀失敗了,要傳回一個錯誤資訊給上層調用者,怎麼辦?

第一個很簡單,由于其隻使用了int的正數部分來表示讀成功多少位元組,那好,-1來表示失敗就好了。

第二個就麻煩了,由于它隻傳回正整數,上層看見都都是對的,它沒有辦法傳回一個錯誤标示。說白了,這個api設計很失敗,無法滿足所有的應用傳回需求。那怎麼辦?也有兩種做法:

unsigned int ReadFrom(int fd,char* szBuffer,int nBufferSizeMax,bool& bSuccessFlag);   

#define READ_FAIL 0xFFFFFFFF   

//出錯傳回READ_FAIL   

大家别說用0啊,很多時候,Read到0,不是錯誤,是一種正常的狀态,比如socker的read,read到0Bytes,很可能是對方沒有發送,并不是說socket失敗了,需要重建鍊路。

嗯,大家再來看看,第一種,沒辦法,隻有多設計一個傳址的參數,讓函數傳回是否成功讀取的标志,上層來決定怎麼處理。

第二種呢,顯式定義一個傳回值為錯誤,這個傳回值永遠不被了解為正确的值,就是正确業務不使用,僅供傳回錯誤。

大家去比較一下Win32api,裡面很多用的都是第二種方法。

不過,這樣一來,大家覺得麻煩不麻煩,第一種,起碼要多做一個參數,程式員學習量增加,記住哦,一個函數好學,100個、1000個函數讓你背,你就覺得這個bSuccessFlag很讨厭了。

第二個更無禮,首先,它限制了業務,導緻了業務缺陷,至少,unsigned int表示的最大值,被用來做fail了,就是業務不能用了,其次,多了個宏,同樣的傳回值unsigned int,需要差別對待,大家說用起來麻煩不麻煩?

别怕麻煩啊,呵呵,去看看Win32Socket的函數,它的SOCKET就是用了unsigned int表示的最大值,作為錯誤的SOCKET辨別,其實就是按照上面第二種辦法設計的api,沒辦法,誰讓他把SOCKET定義為無符号正整數呢。自己給自己下絆子,呵呵。

嗯,還有什麼beginthreadex,CreateWindowsEx,這類函數,大家去看看HANDLE的資料定義,就知道為什麼參數設計得那麼繁瑣了。

很多時候,我們看api好用不好用,其實就看這些細節。

這裡多說一點吧,正是因為Unix和Linux下,很多api函數都用int做傳回值,-1已經被公認為非法,失敗的标志,大家用起來反而簡單,一般說來,看見一個Unix的api函數,猜都猜得出它怎麼表示傳回值,反正都是int嘛,-1就是失敗啦,0和正數都是成功。Windows下就麻煩了,查MSDN都要查半天,主要看它資料類型,以及相應的接口定義。

是以,别看Windows做了很多努力,做了很多程式員友好度的工作,有時候啊,我評論一句,還真不如Unix什麼都丢給程式員。

不過呢,也不好都說不對,如果應用程式員不是計算機界的專業人士,比如說,一個其他專業的工程師,需要臨時寫一小段程式解決個問題,Windows能保證他不出錯。為啥,資料類型定義錯了,編譯都過不去,自己查MSDN去。

是以,說來說去,我認為這個問題還是一個文化問題,兩種平台的開發文化,導緻了今天的局面。說不上誰好誰壞。

大家看着用吧。

嗯,我私人呢,喜歡Windows的匈牙利命名法,喜歡嚴謹的定名,但是,不是很喜歡它那麼多資料類型,還是喜歡int,呵呵。

很多時候,我設計的api大家覺得有點怪,其實就來自于此,又用Windows的命名法,但是,api函數參數設計卻更靠Unix一些,算兩面的綜合吧。

《0bug-C/C++商用工程之道》裡面,我的工程庫用的就是我的習慣,兩種風格兼而有之,因為我覺得,不管白貓黑貓,抓到耗子就是好貓,哪個地方合用用哪個,哪個辦法合用用哪個,沒什麼門戶之見的。

這不絕對啊,上述僅僅是我個人的習慣,大家還是自己選合用的辦法好了,沒必要和我一樣。

=======================================================

線上底價購買《0bug-C/C++商用工程之道》

(直接點選下面連結或拷貝到浏覽器位址欄)

<a href="http://student.csdn.net/link.php?url=http://student.csdn.net%2Flink.php%3Furl%3Dhttp%3A%2F%2Fstudent.csdn.net%252Flink.php%253Furl%253Dhttp%253A%252F%252Fstudent.csdn.net%25252Flink.php%25253Furl%25253Dhttp%25253A%25252F%25252Fstudent.csdn.net%2525252Flink.php%2525253Furl%2525253Dhttp%2525253A%2525252F%2525252Fs.click.taobao.com%252525252Ft_3%252525253F%2525252526amp%252525253Bp%252525253Dmm_13866629_0_0%2525252526amp%252525253Bn%252525253D23%2525252526amp%252525253Bl%252525253Dhttp%25252525253A%25252525252F%25252525252Fsearch8.taobao.com%25252525252Fbrowse%25252525252F0%25252525252Fn-g%25252525252Corvv64tborsvwmjvgawdkmbqgboq---g%25252525252Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%25252525252C2%25252525252C3%25252525252C4%25252525252C5%25252525252C6%25252525252C7%25252525252C8%25252525252C9%25252525252C10%25252525252C11%25252525252C12%25252525252C13%25252525252C14%25252525252C15%25252525252C16%25252525252C17%25252525252C18%25252525252C19%25252525252C20---40--coefp-0-all-0.htm%25252525253Fpid%25252525253Dmm_13866629_0_0" target="_blank">http://s.click.taobao.com/t_3?&amp;p=mm_13866629_0_0&amp;n=23&amp;l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0</a>

繼續閱讀