fe-tool
2008/11/05
生命是一種長期而持續的累積過程
許多同學應該都還記得聯考前夕的焦慮:
差一分可能要掉好幾個志願,甚至於一生的命運從此改觀!
到了大四,這種焦慮可能更強烈而複雜:
到底要先當兵、就業,還是先考研究所?
我就經常碰到學生充滿焦慮的問我這些問題。可是,這些焦慮實在是莫須有的!
生命是一種長期而持續的累積過程,
絕不會因為單一的事件而毀了一個人的一生,
也不會因為單一的事件而救了一個人的一生。
屬於我們該得的,遲早會得到;屬於我們不該得的,即使僥倖巧取也不可能長久保有。如果我們看清這個事實,許多所謂「人生的重大抉擇」就可以淡然處之,根本無需焦慮。而所謂 “ 人生的困境 ” ,也往往當下就變得無足掛齒。
以聯考為例:一向不被看好好的甲不小心猜對十分,而進了建國中學;一向穩上建中的乙不小心丟了二十分,而到附中。放榜日一家人志得意滿,另一家人愁雲慘霧,好像甲,乙兩人命運從此篤定。
可是,聯考真的意謂著什麼?
建國中學最後錄取的那一百人,真的有把握一定比附中前一百名前景好嗎?僥倖考上的人畢竟只是僥倖考上,一時失閃的人也不會因為單一的事件而前功盡棄。
一個人在聯考前所累積的實力,絕不會因為放榜時的排名而有所增減。
因為,生命是一種長期而持續累積的過程!所以,三年後乙順利的考上台大,而甲卻跑到成大去。這時回首高中聯考放榜的時刻,甲有什麼好得意?而乙又有什麼好傷心?同樣的,今天念清大動機的人當年聯考分數都比今天念成大機械的高,可是誰有把握考研究所時一定比成大機械的人考的好?
仔細比較甲與乙的際遇,再重新想想這句話:
「生命是一種長期而持續的累積過程,不會因為一時的際遇而終止增或減」。
聯考排名只是個表象,有何可喜?可憂?可懼?
我常和大學部同學談生涯規劃,問他們三十歲以後希望再社會上扮演什麼樣的角色。可是,到現在沒有人真的能回答我這個問題,他們能想到的只有下一步到底是當兵還是考研究所。
聯考制度已經把我們對生命的延續感徹底瓦解掉,剩下的只有片段的「際遇」,更可悲的甚至只活在放榜的那個 ( 光榮或悲哀的 ) 時刻!
但是,容許我不厭其煩的再重複一次:
生命的真相是一種長期而持續的累積過程 ( 這是偶發的際遇無法剝奪的 ) ,而不是一時順逆的際遇。
如果我們能看清楚這個事實,生命的過程就真是「功不唐捐」,沒什麼好貪求,也沒什麼好焦慮的了!剩下來,我們所需要做的無非只是想清楚自己要從人生獲得什麼,然後安安穩穩,誠誠懇懇的去累積就是了。
我自己就是一個活生生的例子。從一進大學就決定不再念研究所,所以,大學四年的時間多半在念人文科學的東西。畢業後工作了幾年,才決定要念研究所。碩士畢業後,立下決心:從此不再為文憑而唸書。
誰知道,世事難料,當了五年講師後,我又被時勢所迫,整裝出 國念博士。出國時,一位大學同學笑我:全班最晚念博士的都要回國了,你現在才要出去?兩年後我從劍橋回來,覺得人生際遇無常,莫此為甚:一個從大一就決定再也不鑽營學位的人,竟然連碩士 和博士都拿到了!屬於我們該得的,哪樣曾經少過?而人生中該得與不該得的究竟有多少,我們又何曾知曉?從此我對際遇一事不能不更加淡然。
當講師期間,有些態度較極端的學生會當面表現出他們的不屑;從劍橋回來時,卻被學生當做不得了的事看待。這種表面上的大起大落,其實都是好事者之言,完全看不到事實的真相。從表面上看來,兩年就拿到劍橋博士,這好像很了不起。但是,在這「兩年」之前我已經花整整一年,將研究主題有關的論文全部看完,並找出研究方向;而之前更已花三年時間做控制方面的研究,並且在國際著名的學術期刊中發表論文。
而從碩士畢業到拿博士,期間七年的時間我從不停止過研究與自修。所以,這個博士其實是累積了七年的成果,(或者,只算我花在控制學門的時間,也至少有五年),根本也沒什麼好驚訝的。
常人不從長期而持續的累積過程來看待生命因積蓄而有的成果,老愛在表面上以斷裂而孤立的事件誇大議論,因此每每在平淡無奇的事件上強做悲喜。
可是對我來講,每當講師期間被學生瞧不起,以及劍橋剛回來時被同學誇大本事,都只是表象。事實是:我只在乎每天二十四小時點點滴滴的累積。
拿碩士或博士只是特定時刻裡這些成果累積的外在展示而已,人生命中真實的累積從不曾因這些事件而終止或加添。
常有學生滿懷憂慮的問我:
「老師,我很想先當完兵,工作 一兩 年再考研究所。這樣好嗎?」
「很好,這樣子有機會先用實務來印證學理,你念研究所時會比別人瞭解自己要的是什麼。」
「可是,我怕當完兵又工作後,會失去鬥志,因此考不上研究所。」
「那你就先考研究所好了。」
「可是,假如我先念研究所,我怕自己又會像念大學時一樣茫然,因此念的不甘不願的。」
「那你還是先去工作好了!」
「可是。。。。。。。」
我完全會可以體會到他們的焦慮,可是卻無法壓抑住對於這種話的感慨。其實,說穿了他所需要的就是兩年研究所加兩年工作,以便加深知識的深廣度和獲取實務經驗。
先工作或先升學,表面上大相逕庭,其實骨子裡的差別根本可以忽略。在「朝三暮四」這個成語故事裡,主人原本餵養猴子的橡實是「早上四顆、下午三顆」,後來改為「朝三暮四」,猴子就不高興而堅持改回到「朝四暮三」。其實,先工作或先升學,期間差異就有如「朝三暮四」與「朝四暮三」,原不值得計較。
但是,我們經常看不到這種生命過程中長遠而持續的累積,老愛將一時際遇中的小差別誇大到攸關生死的地步。最諷刺的是:當我們面對兩個可能的方案,而焦慮的不知何所抉擇時,通常表示這兩個方案或者一樣好,或者一樣壞,因而實際上選擇哪個都一樣,唯一的差別只是先後之序而已。
而且,愈是讓我們焦慮得厲害的,其實差別越小,愈不值得焦慮。
反而真正有明顯的好壞差別時,我們輕易的就知道該怎麼做了。
可是我們卻經常看不到長遠的將來,短視的盯著兩案短期內的得失:
想選甲案,就捨不得乙案的好處;想選乙案,又捨不得甲案的好處。
如果看得夠遠,人生常則八、九十,短則五、六十年,先做哪一件事又有什麼關係?甚至當完兵又工作後,再花一整年準備研究所,又有什麼了不起?
當然,有些人還是會憂慮說:「我當完兵又工作後,會不會因為家累或記憶力衰退而比較難考上研究所?」
我只能這樣回答:
「一個人考不上研究所,只有兩個可能:
或者他不夠聰明,或者他的確夠聰明。不夠聰明而考不上,那也沒什麼好抱怨的。
假如你夠聰明,還考不上研究所,那只能說你的決心不夠強。假如你是決心不夠強,就表示你生命中還有其他的可能性,其重要程度並不下於碩士學位,而你捨不得丟下他。既然如此,考不上研究所也無須感到遺憾。不是嗎?」
人生的路這麼多,為什麼要老斤斤計較著一個可能性?
我高中最要好的朋友,一生背運:高中考兩次,高一念兩次,大學又考兩次,甚至連機車駕照都考兩次。畢業後,他告訴自己:我沒有人脈,也沒有學歷,只能靠加倍的誠懇和努力。現在,他自己擁有一家公司,年收入數千萬。
一個人在升學過程中不順利,而在事業上順利,這是常見的事。有才華的人,不會因為被名校拒絕而連帶失去他的才華,只不過要另外找適合他表現的場所而已。
反過來,一個人在升學過程中太順利,也難免因而放不下身段去創業,而只能乖乖領水過活。
福禍如何,誰能全面知曉?
我們又有什麼好得意?又有什麼好憂慮?
人生的得與失,有時候怎麼也說不清楚,有時候卻再簡單不過了:
我們得到平日累積的成果,而失去我們不曾努力累積的!
所以重要的不是和別人比成就,而是努力去做自己想做的。
功不唐捐,最後該得到的不會少你一分,不該得到的也不會多你一分。好像是前年的時候,我在往藝術中心的路上遇到一位高中同學。他在南加大當電機系的副教授,被清華電機聘回來給短期課程。
從高中時代他就很用功,以第一志願上台大電機後,四年都拿書卷獎,相信他在專業上的研究也已卓然有成。回想高中入學時,我們兩個人的智力測驗成績分居全學年第一,第二名。可是從高一我就不曾放棄自己喜歡的文學,音樂,書法,藝術和哲學,而他卻始終不曾分心,因此兩個人在學術上的差距只會愈來愈遠。
反過來說,這十幾二十年我在人文領域所獲得的滿足,恐怕已遠非他所能理解的了。我太太問過我,如果我肯全心專注於一個研究領域,是不是至少會趕上這位同學的成就?
我不這樣想,兩個不同性情的人,註定要走兩條不同的路。不該得的東西,我們註定是得不到的,隨隨便便拿兩個人來比,只看到他所得到的,卻看不到他所失去的 ,這有什麼意義?
從高中時代開始,我就不曾仔細計算外在的得失,只安心的做自己想做的事:
我不喜歡鬼混,願意花精神把自己分內的事做好;我不能放棄對人文精神的關懷,會持續一生去探討。
事實單單純純的只是:我只在乎每天二十四小時生命中真實的累積,而不在乎別人能不能看到我的成果。
有人問我,既然遲早要念博士,當年念完碩士就出國,今天是不是可以更早升教授?我從不這樣想。老是斤斤計較著幾年拿博士,幾年升等,這實在很無聊,完全未脫離學生時代「應屆考取」的稚氣心態!
人生長的很,值得發展的東西又多,何必在乎那三,五年?反過來說,有些學生覺得我「多才多藝」,生活「多采多姿」,好像很值得羨慕。可是,為了兼顧理工和人文的研究,
我平時要比別人多花一倍心力,這卻又是大部分學生看不到,也不想學的。
有次清華電臺訪問我:「老師你如何面對你人生中的困境?」
我當場愣在那裡,怎麼樣都想不出我這一生什麼時候有過困境!
後來仔細回想,才發現:
我不是沒有過困境,而是被常人當作「困境」的境遇,我都當作一時的際遇,不曾在意過而已。
剛服完兵役時,長子已出生卻還找不到工作。我曾焦慮過,卻又覺得遲早會有工作,報酬也不至於低的離譜,就不曾太放在心上。念碩士期間, 家計全靠太太的薪水,省吃儉用,但對我而言又算不上困境。一來,精神上我過的很充實,二來我知道這一切是為了讓自己有機會轉行去教書 ( 做自己想做的事 ) 。三十一歲才要出國,而同學正要回系上任教,我很緊張 ( 不知道劍橋要求的有多嚴 ) ,卻不曾喪氣。因為,我知道自己過去一直很努力,也有很滿意的心得和成果,只不過別人看不到而已。
我沒有過困境,因為我從不在乎外在的得失,也不武斷的和別人比高下,而只在乎自己內在真實的累積。
我沒有過困境,因為我確實了解到:生命是一種長期而持續的累積過程,絕不會因為單一的事件而有劇烈的起伏。
同時我也相信:屬於我們該得的,遲早會得到;屬於我們不該得的,即使一分也不可能加增。假如你可以持有相同的信念,那麼人生於你也會是寬廣而長遠,沒有什麼了不得的「困境」,也沒有什麼好焦慮的了。
若要讚美晴天,請等到黃昏之後
2008/11/02
(C++)virtual與pure virtual
如果子class 有override virtual function,就會跳到下面的class做,base class不做事
如果子class 沒有override virtual function,base class就要做事
public:
virtual void f(){
cout<< "BBB";
}
};
class A:public B{
public:
void f(){
cout <<"AAA";
}
};
int main(){
B *b = new A();
b->f(); // 印出AAA
return 0;
}
public:
virtual void f(){
cout<< "BBB";
}
};
class A:public B{
};
int main(){
B *b = new A();
b->f(); // 印出BBB
return 0;
}
如果Base Class的function不是virtual,則子class的function無法覆蓋。如下例:
Ex:
class B{
public:
void f(){ // non-virtual
cout<< "BBB";
}
};
class A:public B{
public:
void f(){ // cannot override a non-virtual function
cout <<"AAA";
}
};
int main(){
B *b = new A();
b->f(); // 印出BBB
return 0;
}
Pure virtual function
base class的Pure virtual function不實做。
子class一定要override pure virtual function
Ex:
class B{
public:
virtual void f() = 0;
};
class A:public B{
public:
void f(){
cout <<"AAA";
}
};
int main(){
B *b = new A();
b->f(); // 印出AAA
return 0;
}
Virtual function: Differences between C++ and Java
| C++ | Java | |
| Pure Virtual Function | vitual void func() = 0; | abstarct void func(); |
| Impure Virtual Function | vitual void func(); | void func(); |
| Non-Virtual Function | void func(); | final void func(); |
| Hiding Inherited Names | Yes | No |
| Prohibiting Inheritance | No | final class |
2008/10/27
別再掉進DLL地獄的陷阱裡(DLL Hell)~.NET解決之道
Introduction
DLL 陷阱是一個惡夢, 是一種相當奇怪的問題。
相信很多讀者都有這樣的經驗,如果你的軟體今天原本運作順暢,當你安裝某個新軟體之後,突然間電腦就無法運作了。這絕對不是你的硬體有問題,也不是應用程 式的問題,而是作業系統設計上的缺失,這樣的問題層出不窮,這通常是因為新的應用程式版本覆蓋掉共享的程式庫(DLL),而且往往修改了一些現存應用程式 所必需的「bug」,這個缺失有了一個名字叫做DLL Hell (DLL地獄)。開發人員與系統管理者(以及使用者)面臨最大的挑戰就是版本更新的問題 ,他們花很多時間在 Windows 登錄檔 (Regedit) 上試著解決其問題而吃盡苦頭 。
.在Microsoft .NET的世界裡,軟體元件再也不需要登錄(Registry)了! NET Framework包含了一些功能,可以實際消除「DLL Hell」的問題,一項稱之為「side-by-side」開發模式的新功能。
DLL & DLL Hell
為什麼要使用 DLL (Dynamic Linking Library) - 動態連結檔 ?
微軟當初為Windows設計動態連結檔主要是擷取它的兩項優點:一是動態連結、一是資源共享。資源共享的例子相當顯而易見,例如之前曾經提過 Windows有三個核心的動態連結檔:Kernel主要是負責系統和應用程式的記憶體、行程和執行緒等等的管理工作;User主要負責使用者介面和訊息 的傳遞;GDI則負責系統的任何圖形繪製、顯示等工作。而這些動態連結檔所提供的任何函數都可以在必要的時候,讓每一個Windows環境底下的執行檔使 用。因為DLL具備節省記憶體的特性,因此自從Windows 3.1版以來,它就逐漸成為Windows程式設計的主流
˙動態連結檔可以資源共享
許多大型軟體廠商的眾多軟體產品可能都會有許多可以共用的模組,如果每一套軟體各自擁有一份這些可以共用的模組,不僅會造成磁碟空間的浪費,還會讓維護這 些模組的工作變的既複雜、又凌亂。最好的方法就是僅保持一份程式碼,然後透過共享的方式讓其他自家的應用程式也可以存取其中共用的模組。共用模組的方法之 一就是將模組製作成動態連結檔,然後透過軟體的安裝程式複製到電腦,那麼只要安裝了其中一套軟體之後,其他自家的產品就可以互相共用這一套動態連結檔。
假設有一函數庫X供三個應用程式A、B、C使用,如果函數庫為目的碼或原始程式碼,則程式編譯之後,函數庫X將會各自成為執行檔A、B、C的一部份,而將來如果應用程式A、B、C同時執行,函數庫X也會各自佔用一份記憶體,顯然這是比較浪費記憶體的方式。
如果函數庫為DLL形式,則編譯之後,函數庫並不會成為執行檔的一部分,而將來如果應用程式A、B、C同時被執行,則系統只會載入一份函數庫讓程式A、程式B、程式C共用,如圖。
Figure: 程式與DLL的共用架構圖
˙動態連結檔節省記憶體空間
動態連結檔的資源共享可以節省磁碟空間,而動態載入的連結方式則可以節省記憶體空間。動態連結檔採用動態載入的連結方式,動態載入讓程式檔在需要相關的函
數或資源的時候,才載入放置在動態連結檔裡面的函數或資源,這種方式將可以有效地使用記憶體。不論是節省磁碟空間或記憶體空間,都是希望利用動態連結檔所
提供的共享函數與系統資源的方式,降低整個Windows環境對於硬體設備的需求。
DLL的問題 - DLL Hell
˙動態連結檔到底出了什麼問題?
其實DLL的優點(程式碼共用、節省記憶體),正是其缺點的起源。原本是立意良好的DLL,有一天會變成DLL Hell,恐怕是當初DLL的設計者所始料未及的。
而之所以會出現DLL Hell,也是因為動態連結檔可以與其他程式共用函數、共享資源所引起,可謂「成也共用、敗也共用」。此話怎講呢?為了要讓其他程式共用動態連結檔所提供 的函數或資源,動態連結檔的設計者必須相當謹慎地、縝密地考慮到功能的一致性、回溯相容等細節問題,否則一旦程式所使用動態連結檔沒有提供所預期的功能, 那麼使用者就會為此而掉入地獄了。
但是要完全考慮到一致性或回溯相容,實在是困難重重,就算真的要做到,也會讓利用動態連結檔的軟體廠商付出相當的成本;但,有必要付出這些成本嗎?想想現 今的電腦執行環境,與當初微軟設計動態連結檔的時候已經有相當、相當大的變化。現在的硬體比起當初已經便宜太多、太多了,個人電腦的記憶體都是從64MB 起跳,配備128MB記憶體的電腦更是比比皆是,而硬碟容量更是以GB計算。在如此的硬體環境之下,Windows程式設計師還需要這麼刻苦地考慮共用的 問題嗎?而且動態連結檔的動態載入,其實已經替Windows系統節省了不少系統資源,因此微軟也重新調整動態連結檔的設計理念,而且也針對作業系統進行 改善,希望不要再有任何使用者掉入因為共用動態連結檔而起的地獄深淵。
˙數種DLL Hell 的狀況
讓我們想一想,如果某一副程式或物件類別有90%符合我們的需求,卻有10%不符合,怎麼辦呢?對副程式來說,大概只有修改「原始程式碼」一途。
假設程式A會使用物件X,在程式A安裝到系統時,會同時安裝物件X,假設另一個程式B也會使用到物件X,那麼程式B直接複製到硬碟中即可正常運作,因為物 件X已經存在於系統中,這聽起來很好,因為程式A與程式B可以共用物件X。然而對程式A來說,原本在安裝後,執行得好好的,卻可能在未知的一天變成無法執 行,這就是所謂的DLL Hell。以下為描述DLL Hell的兩種狀態。
狀況 1. 動態連結檔沒有善盡回溯相容的責任
如果程式A使用的是1.0版的物件X,而程式B使用的是 2.0 版的物件X(通常是因為程式B開發的時間較晚,使用較新的版本),結果會怎樣呢?結果在程式B被安裝到系統時,物件X 2.0版也必須安裝到系統中,此時系統中 1.0 版的物件X將會被 2.0 版所取代。
在大部分的情況下,物件X 2.0版相容於1.0版,所以程式A依然可以正常運作,但有時候卻會出現 2.0 版及 1.0 版不相容的情況,此時程式A便無法正常執行了。此種DLL Hell的起因則是的設計者,原因在於動態連結檔沒有善盡回溯相容的原則。試著想想A.exe 需要 X.dll 所提供的功能,但是在新版的 X.dl l裡面,功能竟然被取消了,這時候也極可能發生DLL Hell。
但是誠如之前所討論,有時往往很難保證百分之百的回溯相容,而且目前個人電腦的硬體配備已經不再像以前簡陋,因此微軟也提出了所謂Side-By- Side的動態連結檔,讓程式都能擁有自己專屬的動態連結檔,進而減少共用動態連結檔以避免這種DLL Hell的發生。
狀況 2. 動態連結檔善盡回溯相容的責任, 但動態連結檔本身出現bug
另一種情況,物件X的提供者確實考慮到版本相容的問題,而根據物件的規格來看,新舊版也的確相容,但程式A使用新版的物件X就是有問題,畢竟程式A並沒有與新版的物件X一起運作過,誰知道會發生什麼情況?
|
| Server DLL (X.DLL) | Server Codes |
|
| (X 1.0) (原始版本) | Value = 0 |
|
| (X 2.0) (更新版本) | Err.Raise Number:=APP_ERROR, Description:="Negative Value " |
如上表所發生的不幸的事, 縱使 X 2.0 的開發人員小心翼翼透過 VB6.0 的二進位相容模式控制DLL版本,且所有的內部GUID值與方法和參數都完全相同,由於X 1. 0 之中有一個名為 Value 的屬性名稱, 當此一屬性設為負數時, 該屬性就會變成零, 但卻不會出現錯誤訊息。這個做法是錯誤的, X 2.0版將此臭從解決了- 若將Value屬性設為負數, 則會拋出錯誤。
當程式 B 以 X 2.0 散佈時, 這支程式B 當然也可以正常運作。不過, 如果將 X 2.0 安裝在系統之中, 程式A 會當掉。之前程式將Value 屬性設為 -1 不會有問題, 但現在會出現執行時期的錯誤。同時程式 A的開發人員並未在這設計錯誤檢視的機制。
在許多真實的案例中, 要善盡二進位相容模式控制DLL版是非常困難的, 亙何況即使是二進位相容模式控制DLL版本還是有可能造成DLL Hell。
.NET 如何解決DLL Hell 的問題
自我描述的Assembly(Self-describing Assembly)
Assembly(組合)是簡化部署與版本管理的關鍵。Assembly是部署與版本管理的基本單元,其中包含一群資源(Resource)與型別 (Type),以及它們內含的Metadata,同時也包括此組合在建造時所依賴的其他組合的版本資訊。有了獨立完整的組態資訊,同一組合的不同版本也可 安裝在同一部機器上,搭配共通語言執行環境具備根據各組合的組態資訊,載入正確版本的依賴組合(Dependent Assemblies)的能力,安裝與解除安裝的過程,就如同複製檔案與刪除檔案一樣單純,以往因先後安裝彼此覆蓋而產生的所謂“DLL Hell”的版本失控的現象不復存在。
Figure . Self-Describing Assembly 可包括多個檔案
Metadata(定義)是Assembly能自我描述的關鍵。Metadata是編輯器在產生執行碼時,伴隨產生的定義性資訊,舉凡元件所使用的型別、 屬性、方法、事件甚至輔助與備註等資訊都可包含在內,而且保證與執行碼的一致性,完全取代並且超越了傳統分離式的IDL(Interface Definition Language) 檔案與型別庫(Type Library)所扮演的功能,同時元件服務要求與執行所需的資訊皆動態來自Metadata 。.asseembly directive 作為辨識 assembly 本身 , .assembly extern directives 定義此 assembly 所依賴的其他 Assemblies。
Figure . 可使用 IL Disassembler (Ildasm)呈現 DLL 的 metadata
簡化的部署與版本管理
˙Application-Private Assemblies (Isolated Assembly)
Application-Private Assemblies (or 被隔離的 assembly) 只能被一個應用程式所使用- 它不能被其他的應用程式所共用。隔離 assembly 讓程式開發者有著對應用程式絕對的控制權,這也是 .NET應用程式的預設方式。
開發好的Application-Private Assemblies 要在另一個 .NET 環境進行安裝時,手續只有一個,就是Copy And Paste。只要把編譯好的程式,無論是EXE執行檔、DLL元件、ASP.NET的.aspx網頁或Web Service的.asmx檔,全部都是以複製/貼上的方式部署在和應用程式相同的目錄,這些檔案複製完成後,不需額外註冊或設定。.NET程式執行時, 如果需要額外的元件,首先會自本身執行檔下的同一目錄開始尋找,因此,每套應用程式預設都是使用本身同一目錄下的元件,不同應用程式間不會相互干擾,也消 除了DLL Hell 的困擾。(註: The CLR finds these assemblies through a process called probing. Probing is simply a mapping of the assembly name to the name of the file that contains the manifest.)
˙Shared Assemblies - Side by side execution (並排執行)
然而,應用程式共享 assembly 還是有其必要性, 因為讓每個應用程式都有自己一份 copy (如 System.Windowns.Forms, System.Web or a common Web Forms control )是件很奇怪的事 。
為了解決DLL Hell的問題,.NET增加了一種新的技術,稱為Side by side execution,意思是應用程式可以擁有各自版本的DLL,例如程式 A使用版本1.0的物件、而程式B使用版本2.0的物件,1.0版與2.0版的物件可以同時在系統執行。
透過Side by side execution的技術,應用程式只要安裝成功之後,就不用擔心DLL更新版本,或規格的改變,因為就算DLL改朝了,應該程式也不用換代。以下簡述 Side by side execution的過程圖 及應用步驟:
Figure . Process for implementing strong names
step 1. create a key pair using the Strong Name tool (Sn.exe) .
Sn –k MyKey.snk
嚴格名稱(Strong names)是一種在.NET 架構下可減少 .NET DLL 陷阱的功能
(註: The author of an assembly generates a key pair, signs the file containing the manifest with the private key, and makes the public key available to callers by the Strong Name tool. The key pair is passed to the compiler using the custom Assembly attribute )
Step 2. sign an assembly with a strong name and version number:
In Assemblyinfo.vb,
Step 3. Install Assembly to Global assembly cache (GAC)
gacutil /i:myassembly.dll
(註: See the .NET Framework SDK documentation for a full description of the options supported by gacutil.)
Figure : Global Assembly Cache
Step 4. 應用程式與 Assembly 的 版本繫結 (Version Policy)
.NET 提供有組態設定機制可以控制Assembly 的繫結, 故在程式中可以載入相關 Assembly的升級版本。組態設定機制是由XML設定檔來負責,透過這種安全機制可以控制程式的安全、版本以及遠端的功能。每一個應用程式可以有XML程式設定檔來指定應用程式所要繫結Assembly的不同版本
程式設定檔的檔案名稱為程式名稱加上 .config 副檔名, 如 Myapp.exe.config。
例如以下範例設定檔會載入MarineCtrl 版本 5.0.0.1 而非版本 5.0.0.0
The .NET Framework NET Assembly 自我描述與版本管理功能讓 zero-impact 的部署安裝成為可能,同時也終結了DLL Hell 。
Application-Private Assemblies (or 被隔離的 assembly) 只能被一個應用程式所使用 - 它不會被其他的應用程式所影響。 隔離的 assembly 讓程式開發者對應用程式有著絕對的控制權,開發好的Application-Private Assemblies只要部署在和應用程式同一目錄即可。
透過Side by side execution的技術,應用程式只要安裝成功之後,就不用擔心DLL更新版本,或規格的改變, 它允許 一個 assembly 的多個版本在一個機器上同時被安裝並執行, 而且每一個應用程式都可以要求和不同的 Assembly 版本繫結。
The .NET Framework 紀錄應用程式版本資訊,並在執行應用程式時使用此資訊載入應用程式所需依賴的正確版本的 Assemblies。
2008/10/15
C/C++中的日期和時間 time_t與struct tm轉換
摘要:
本文從介紹基礎概念入手,探討了在C/C++中對日期和時間操作所用到的數據結構和函數,並對計時、時間的獲取、時間的計算和顯示格式等方面進行了闡述。本文還通過大量的實例向你展示了time.h頭文件中聲明的各種函數和數據結構的詳細使用方法。
關鍵字:UTC(世界標准時間),Calendar Time(日歷時間),epoch(時間點),clock tick(時鐘計時單元)
1.概念
在 C/C++中,對字符串的操作有很多值得注意的問題,同樣,C/C++對時間的操作也有許多值得大家注意的地方。最近,在技術群中有很多網友也多次問到過 C++語言中對時間的操作、獲取和顯示等等的問題。下面,在這篇文章中,筆者將主要介紹在C/C++中時間和日期的使用方法.
通過學習許多C/C++庫,你可以有很多操作、使用時間的方法。但在這之前你需要了解一些「時間」和「日期」的概念,主要有以下幾個:
Coordinated Universal Time(UTC):協調世界時,又稱為世界標准時間,也就是大家所熟知的格林威治標准時間(Greenwich Mean Time,GMT)。比如,中國內地的時間與UTC的時差為+8,也就是UTC+8。美國是UTC-5。
Calendar Time:日歷時間,是用「從一個標准時間點到此時的時間經過的秒數」來表示的時間。這個標准時間點對不同的編譯器來說會有所不同,但對一個編譯系統來說,這個標准時間點是不變的,該編譯系統中的時間對應的日歷時間都通過該標准時間點來衡量,所以可以說日歷時間是「相對時間」,但是無論你在哪一個時區,在同一時刻對同一個標准時間點來說,日歷時間都是一樣的。
epoch:時間點。時間點在標准C/C++中是一個整數,它用此時的時間和標准時間點相差的秒數(即日歷時間)來表示。
clock tick:時鐘計時單元(而不把它叫做時鐘滴答次數),一個時鐘計時單元的時間長短是由CPU控制的。一個clock tick不是CPU的一個時鐘周期,而是C/C++的一個基本計時單位。
我們可以使用ANSI標准庫中的time.h頭文件。這個頭文件中定義的時間和日期所使用的方法,無論是在結構定義,還是命名,都具有明顯的C語言風格。下面,我將說明在C/C++中怎樣使用日期的時間功能。
2. 計時
C/C++中的計時函數是clock(),而與其相關的數據類型是clock_t。在MSDN中,查得對clock函數定義如下:
clock_t clock( void );
這個函數返回從「開啟這個程序進程」到「程序中調用clock()函數」時之間的CPU時鐘計時單元(clock tick)數,在MSDN中稱之為掛鐘時間(wall-clock)。其中clock_t是用來保存時間的數據類型,在time.h文件中,我們可以找到對它的定義:
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif
很明顯,clock_t是一個長整形數。在time.h文件中,還定義了一個常量CLOCKS_PER_SEC,它用來表示一秒鐘會有多少個時鐘計時單元,其定義如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
可以看到可以看到每過千分之一秒(1毫秒),調用clock()函數返回的值就加1。下面舉個例子,你可以使用公式clock()/CLOCKS_PER_SEC來計算一個進程自身的運行時間:
void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}
當然,你也可以用clock函數來計算你的機器運行一個循環或者處理其它事件到底花了多少時間:
#include 「stdio.h」
#include 「stdlib.h」
#include 「time.h」
int main( void )
{
long i = 10000000L;
clock_t start, finish;
double duration;
/* 測量一個事件持續的時間*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- ) ;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
system("pause");
}
在筆者的機器上,運行結果如下:
Time to do 10000000 empty loops is 0.03000 seconds
上面我們看到時鐘計時單元的長度為1毫秒,那麼計時的精度也為1毫秒,那麼我們可不可以通過改變CLOCKS_PER_SEC的定義,通過把它定義的大一些,從而使計時精度更高呢?通過嘗試,你會發現這樣是不行的。在標准C/C++中,最小的計時單位是一毫秒。
3.與日期和時間相關的數據結構
在標准C/C++中,我們可通過tm結構來獲得日期和時間,tm結構在time.h中的定義如下:
#ifndef _TM_DEFINED
struct tm {
int tm_sec; /* 秒 – 取值區間為[0,59] */
int tm_min; /* 分 - 取值區間為[0,59] */
int tm_hour; /* 時 - 取值區間為[0,23] */
int tm_mday; /* 一個月中的日期 - 取值區間為[1,31] */
int tm_mon; /* 月份(從一月開始,0代表一月) - 取值區間為[0,11] */
int tm_year; /* 年份,其值等於實際年份減去1900 */
int tm_wday; /* 星期 – 取值區間為[0,6],其中0代表星期天,1代表星期一,以此類推 */
int tm_yday; /* 從每年的1月1日開始的天數 – 取值區間為[0,365],其中0代表1月1日,1代表1月2日,以此類推 */
int tm_isdst; /* 夏令時標識符,實行夏令時的時候,tm_isdst為正。不實行夏令時的進候,tm_isdst為0;不了解情況時,tm_isdst()為負。*/
};
#define _TM_DEFINED
#endif
ANSI C標准稱使用tm結構的這種時間表示為分解時間(broken-down time)。
而日歷時間(Calendar Time)是通過time_t數據類型來表示的,用time_t表示的時間(日歷時間)是從一個時間點(例如:1970年1月1日0時0分0秒)到此時的秒數。在time.h中,我們也可以看到time_t是一個長整型數:
#ifndef _TIME_T_DEFINED
typedef long time_t; /* 時間值 */
#define _TIME_T_DEFINED /* 避免重復定義 time_t */
#endif
大家可能會產生疑問:既然time_t實際上是長整型,到未來的某一天,從一個時間點(一般是1970年1月1日0時0分0秒)到那時的秒數(即日歷時間)超出了長整形所能表示的數的范圍怎麼辦?對time_t數據類型的值來說,它所表示的時間不能晚於2038年1月18日19時14分07秒。為了能夠表示更久遠的時間,一些編譯器廠商引入了64位甚至更長的整形數來保存日歷時間。比如微軟在Visual C++中采用了__time64_t數據類型來保存日歷時間,並通過_time64()函數來獲得日歷時間(而不是通過使用32位字的time()函數),這樣就可以通過該數據類型保存3001年1月1日0時0分0秒(不包括該時間點)之前的時間。
在time.h頭文件中,我們還可以看到一些函數,它們都是以time_t為參數類型或返回值類型的函數:
double difftime(time_t time1, time_t time0);
time_t mktime(struct tm * timeptr);
time_t time(time_t * timer);
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
此外,time.h還提供了兩種不同的函數將日歷時間(一個用time_t表示的整數)轉換為我們平時看到的把年月日時分秒分開顯示的時間格式tm:
struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);
通過查閱MSDN,我們可以知道Microsoft C/C++ 7.0中時間點的值(time_t對象的值)是從1899年12月31日0時0分0秒到該時間點所經過的秒數,而其它各種版本的Microsoft C/C++和所有不同版本的Visual C++都是計算的從1970年1月1日0時0分0秒到該時間點所經過的秒數。
4.與日期和時間相關的函數及應用
在本節,我將向大家展示怎樣利用time.h中聲明的函數對時間進行操作。這些操作包括取當前時間、算時間間隔、以不同的形式顯示時間等內容。
4.1 獲得日歷時間
我們可以通過time()函數來獲得日歷時間(Calendar Time),其原型為:
time_t time(time_t * timer);
如果你已經聲明了參數timer,你可以從參數timer返回現在的日歷時間,同時也可以通過返回值返回現在的日歷時間,即從一個時間點(例如:1970年 1月1日0時0分0秒)到現在此時的秒數。如果參數為空(NULL),函數將只通過返回值返回現在的日歷時間,比如下面這個例子用來顯示當前的日歷時間:
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NULL);
printf("The Calendar Time now is %d\n",lt);
return 0;
}
運行的結果與當時的時間有關,我當時運行的結果是:
The Calendar Time now is 1122707619
其中1122707619就是我運行程序時的日歷時間。即從1970年1月1日0時0分0秒到此時的秒數。
4.2 獲得日期和時間
這裡說的日期和時間就是我們平時所說的年、月、日、時、分、秒等信息。從第2節我們已經知道這些信息都保存在一個名為tm的結構體中,那麼如何將一個日歷時間保存為一個tm結構的對象呢?
其中可以使用的函數是gmtime()和localtime(),這兩個函數的原型為:
struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);
其中gmtime()函數是將日歷時間轉化為世界標准時間(即格林尼治時間),並返回一個tm結構體來保存這個時間,而localtime()函數是將日歷時間轉化為本地時間。比如現在用gmtime()函數獲得的世界標准時間是2005年7月30日7點18分20秒,那麼我用localtime()函數在中國地區獲得的本地時間會比時間標准時間晚8個小時,即2005年7月30日15點18分20秒。下面是個例子:
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *local;
time_t t;
t=time(NULL);
local=localtime(&t);
printf("Local hour is: %d\n",local->tm_hour);
local=gmtime(&t);
printf("UTC hour is: %d\n",local->tm_hour);
return 0;
}
運行結果是:
Local hour is: 15
UTC hour is: 7
4.3 固定的時間格式
我們可以通過asctime()函數和ctime()函數將時間以固定的格式顯示出來,兩者的返回值都是char*型的字符串。返回的時間格式為:
星期幾 月份 日期 時:分:秒 年\n\0
例如:Wed Jan 02 02:03:55 1980\n\0
其中\n是一個換行符,\0是一個空字符,表示字符串結束。下面是兩個函數的原型:
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
其中asctime()函數是通過tm結構來生成具有固定格式的保存時間信息的字符串,而ctime()是通過日歷時間來生成時間字符串。這樣的話,asctime()函數只是把tm結構對象中的各個域填到時間字符串的相應位置就行了,而ctime()函數需要先參照本地的時間設置,把日歷時間轉化為本地時間,然後再生成格式化後的字符串。在下面,如果lt是一個非空的time_t變量的話,那麼:
printf(ctime(<));
等價於:
struct tm *ptr;
ptr=localtime(<);
printf(asctime(ptr));
那麼,下面這個程序的兩條printf語句輸出的結果就是不同的了(除非你將本地時區設為世界標准時間所在的時區):
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NULL);
ptr=gmtime(<);
printf(asctime(ptr));
printf(ctime(<));
return 0;
}
運行結果:
Sat Jul 30 08:43:03 2005
Sat Jul 30 16:43:03 2005
4.4 自定義時間格式
我們可以使用strftime()函數將時間格式化為我們想要的格式。它的原型如下:
size_t strftime(
char *strDest,
size_t maxsize,
const char *format,
const struct tm *timeptr
);
我們可以根據format指向字符串中格式命令把timeptr中保存的時間信息放在strDest指向的字符串中,最多向strDest中存放maxsize個字符。該函數返回向strDest指向的字符串中放置的字符數。
函數strftime()的操作有些類似於sprintf():識別以百分號(%)開始的格式命令集合,格式化輸出結果放在一個字符串中。格式化命令說明串 strDest中各種日期和時間信息的確切表示方法。格式串中的其他字符原樣放進串中。格式命令列在下面,它們是區分大小寫的。
%a 星期幾的簡寫
%A 星期幾的全稱
%b 月分的簡寫
%B 月份的全稱
%c 標准的日期的時間串
%C 年份的後兩位數字
%d 十進制表示的每月的第幾天
%D 月/天/年
%e 在兩字符域中,十進制表示的每月的第幾天
%F 年-月-日
%g 年份的後兩位數字,使用基於周的年
%G 年分,使用基於周的年
%h 簡寫的月份名
%H 24小時制的小時
%I 12小時制的小時
%j 十進制表示的每年的第幾天
%m 十進制表示的月份
%M 十時制表示的分鐘數
%n 新行符
%p 本地的AM或PM的等價顯示
%r 12小時的時間
%R 顯示小時和分鐘:hh:mm
%S 十進制的秒數
%t 水平制表符
%T 顯示時分秒:hh:mm:ss
%u 每周的第幾天,星期一為第一天 (值從0到6,星期一為0)
%U 第年的第幾周,把星期日做為第一天(值從0到53)
%V 每年的第幾周,使用基於周的年
%w 十進制表示的星期幾(值從0到6,星期天為0)
%W 每年的第幾周,把星期一做為第一天(值從0到53)
%x 標准的日期串
%X 標准的時間串
%y 不帶世紀的十進制年份(值從0到99)
%Y 帶世紀部分的十制年份
%z,%Z 時區名稱,如果不能得到時區名稱則返回空字符。
%% 百分號
如果想顯示現在是幾點了,並以12小時制顯示,就象下面這段程序:
#include 「time.h」
#include 「stdio.h」
int main(void)
{
struct tm *ptr;
time_t lt;
char str[80];
lt=time(NULL);
ptr=localtime(<);
strftime(str,100,"It is now %I %p",ptr);
printf(str);
return 0;
}
其運行結果為:
It is now 4PM
而下面的程序則顯示當前的完整日期:
#include
#include
void main( void )
{
struct tm *newtime;
char tmpbuf[128];
time_t lt1;
time( <1 );
newtime=localtime(<1);
strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);
printf(tmpbuf);
}
運行結果:
Today is Saturday, day 30 of July in the year 2005.
4.5 計算持續的時間長度
有時候在實際應用中要計算一個事件持續的時間長度,比如計算打字速度。在第1節計時部分中,我已經用clock函數舉了一個例子。Clock()函數可以精確到毫秒級。同時,我們也可以使用difftime()函數,但它只能精確到秒。該函數的定義如下:
double difftime(time_t time1, time_t time0);
雖然該函數返回的以秒計算的時間間隔是double類型的,但這並不說明該時間具有同double一樣的精確度,這是由它的參數覺得的(time_t是以秒為單位計算的)。比如下面一段程序:
#include 「time.h」
#include 「stdio.h」
#include 「stdlib.h」
int main(void)
{
time_t start,end;
start = time(NULL);
system("pause");
end = time(NULL);
printf("The pause used %f seconds.\n",difftime(end,start));//<-
system("pause");
return 0;
}
運行結果為:
請按任意鍵繼續. . .
The pause used 2.000000 seconds.
請按任意鍵繼續. . .
可以想像,暫停的時間並不那麼巧是整整2秒鐘。其實,你將上面程序的帶有「//<-」注釋的一行用下面的一行代碼替換:
printf("The pause used %f seconds.\n",end-start);
其運行結果是一樣的。
4.6 分解時間轉化為日歷時間
這裡說的分解時間就是以年、月、日、時、分、秒等分量保存的時間結構,在C/C++中是tm結構。我們可以使用mktime()函數將用tm結構表示的時間轉化為日歷時間。其函數原型如下:
time_t mktime(struct tm * timeptr);
其返回值就是轉化後的日歷時間。這樣我們就可以先制定一個分解時間,然後對這個時間進行操作了,下面的例子可以計算出1997年7月1日是星期幾:
#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
struct tm t;
time_t t_of_day;
t.tm_year=1997-1900;
t.tm_mon=6;
t.tm_mday=1;
t.tm_hour=0;
t.tm_min=0;
t.tm_sec=1;
t.tm_isdst=0;
t_of_day=mktime(&t);
printf(ctime(&t_of_day));
return 0;
}
運行結果:
Tue Jul 01 00:00:01 1997
現在注意了,有了mktime()函數,是不是我們可以操作現在之前的任何時間呢?你可以通過這種辦法算出1945年8月15號是星期幾嗎?答案是否定的。因為這個時間在1970年1月1日之前,所以在大多數編譯器中,這樣的程序雖然可以編譯通過,但運行時會異常終止。
5.總結
本文介紹了標准C/C++中的有關日期和時間的概念,並通過各種實例講述了這些函數和數據結構的使用方法。筆者認為,和時間相關的一些概念是相當重要的,理解這些概念是理解各種時間格式的轉換的基礎,更是應用這些函數和數據結構的基礎。
2008/09/30
留學新加坡:學習是一種享受
新加坡崇尚精英主義,其精英教育培養出高素質的人才,使新加坡進入世界發達國家的行列。而隨著出國熱潮的高漲,新加坡必然成為中國學生出國留學的熱點之一。近日,記者對部分在新加坡留學的中國學生進行了採訪,他們的生活、學習以及對未來的打算等,可供擬到新加坡留學的各界人士參考。
陳寬,來自廣東深圳,原就讀于深圳外國語學校,初三畢業後去新加坡留學,現在就讀于新加坡萊佛士書院中四。在新加坡讀書已經有一年零四個月了。留學新加坡充滿了挑戰
在這一段時間當中,我經歷了不少,感覺也成長了很多。我對在新加坡的學習以及生活都十分滿意,充滿了挑戰。
初三畢業的時候,班主任按照中考成績推薦我考新加坡。新加坡學校的條件十分的誘人:學費住宿費全免,同時每年還有2200新幣的零用錢。許多去英國、美國留學的同學,因為負擔著龐大的留學費用,心理壓力無比沉重。相比之下,我感覺自己輕松許多。
東方教育以及西方教育有很大不同。新加坡的教育制度與英國劍橋大學的LEVEL接軌,學位證書得到整個西方社會承認。教育體現人性化,自由度十分高。讓學生自己發揮的空間很大。年終的學習成績不僅參考期末成績,平時的成績更為重要。平常的每一次作業,每一次小測驗,都會記入年終的總成績。
新加坡學校的課外活動團體有著極其嚴格的評分標準,並作為往後升學的重要考核之一。每一個學生都必須至少參加一項課外活動團體,可以是籃球、足球、橄欖球、管樂隊、弦樂隊等等。每周有固定的訓練時間,從校外請來專業的教練進行指導,有全國性的比賽以及表演等。培養一技之長,有時比學習本身更為重要。
盡管學段內學生十分忙碌,但是兩個長假時學校則極少布置作業,讓學生真正意義上的休養生息,放松頭腦,安排自己的時間。生活上,因為獨自在外求學沒有父母的幫忙,經常要自己處理各種各樣的問題,有助于提高生活自理能力和待人處事的能力。有一次,存錢時因為機器出現故障而有1300元新幣被扣,必須自己打電話到銀行進行交涉。
新加坡的生活充滿關愛。我們的監護人藍南順主任以及副校長對我們非常好。我們幾十個來自中國的學生,每個人生日的時候都會收到主任為我們買的蛋糕。逢年過節的時候,藍南順主任會安排我們到新加坡本地人家中過節,讓我們有家的感覺。而副校長也經常和我們一起踢足球,關係十分融洽。新加坡本地同學對我們也十分友善,經常邀請我們到他們家去玩。
我就讀的萊佛士書院以培養領導人為宗旨,如今已經培養了三位總統、兩位總理,在歐美的名牌大學當中也極負盛名。英語是這邊的主要工作語言,也是這邊的教學語言,在這裏學習英語為我們往後走向西方搭上了一座橋梁。同時,作為一個華人佔大多數的國家,這裏體現著濃厚的儒家氛圍以及中華文化底蘊,感覺非常熟悉,也讓我們有更多的時間和空間來適應環境。
如果要選擇來新加坡,就準備好接受挑戰。我們在這裏的成績以及各方面表現都將被新加坡教育部記錄,若結果不盡如人意的話有可能會被送回中國。來到這裏,周圍很多來自中國、馬來西亞、印度尼西亞等國家的獎學金得主,你將會發現自己正生活在一群最優秀的人群當中。宋超倫,17歲,來自中國吉林省吉林市,到新加坡已經一年零四個月,現就讀于萊佛士書院初四年級。生活從來就不單調
我對新加坡的生活相當滿意,我們的監護人藍主任和副校長都很照顧我們,讓我體會到了家的關愛和溫暖。
學校為了幫助我們提高英語水平給我們額外補習英文,數理化方面都還不是太困難,我們基本上都在初中學過了,但是是英語教學。學校特別開設了中華文學課,還從國內請來了極負盛名的老師。
選擇在初中畢業出國留學現在仍不被大家所認可,因為這個階段剛好是世界觀人生觀形成的時期,一個良好健康的社會環境很重要。但是新加坡社會風氣積極健康,而且華人居多不會有種族歧視,所以父母很放心。此外,新加坡東方文化和西方文化相輔相成、水乳交融,一點也不感覺陌生,很快就能融入其中。
至于留學費用,所有的學費、住宿費、夥食費都是免費的。去醫院看病、開藥也同樣免費或是日後報銷,需要住院的話只需要交住院費的20%。除了這些免費政策,我一年還有2200元新幣的生活費。我的一些朋友能夠很合理的用錢,在保證生活質量的同時,還有余額購買往返回國的飛機票。在這邊我們學會了如何合理支配用錢,也理解了掙錢的不易,培養了我們節省的美德。
教育質量環境可以說是一流的。我所在萊佛士書院佔地面積有10公頃,綠茵茵的足球場、蔚藍的遊泳池、籃球場、網球場應有盡有。每個教室都有電腦和電教設備。從空調吹來的爽爽的涼風也給我們提供了良好的學習場所。樹陰下的長凳更是有些學生的鐘愛。我們學校的圖書館是新加坡最古老的圖書館,藏書數量有一萬多本。圖書館配備的電腦同樣可以提供上網查找資料的方便。我們的老師都是從一流大學畢業的。
我覺得任何國家的基礎教育都沒有中國那麼牢固,我也是受益匪淺。但是中國的教育太格式化了,學生都被固定在一個框框裏面,缺乏創新思維和那種敢于嘗試的勇氣。新加坡教育就靈活了很多。以我就讀的萊佛士書院為例,學校實行英才教育,注重培養學生的領袖氣質,學校也開設培養領導才能的課程,讓每一個學生都有機會接受這種訓練。而且注重團隊精神,很多活動或者是課題的研究都是以小組的形式出現,大家在研究的過程中互幫互助,相輔相成。另一點,就是新加坡很注重學生的實踐能力。在課堂上老師就拓展學生的發散思維,啟發創新精神。更重要的是學生在學會理解知識之後,懂得學以致用。
新加坡這邊社會風氣積極健康,是個法制社會,很安全。在來新加坡之前,最好將從小到大所接種的疫苗記錄下來,以免再接種一次。簽證和護照新加坡方面會幫助處理的,只要保存好就可以了。
新加坡是一個東西方文化融合的社會,在這個國家有不同種族的居民,大家不同膚色,不同語言,在一起和平共處。在這個多種族、多文化的社會中,你首先就學會了怎樣尊重別人,如何能友好的和別人一同生活,知道如何尊重別人,也就贏得了別人的尊重。
新加坡的生活是平淡而又充滿快樂,在這邊有可能會遇到很多困難,尤其是父母不在身邊,一切都要自己抉擇,這將是一段不可磨滅的成長經歷,過早的離開父母注定我們會更加成熟。自己一個人在外學會了照顧自己,也多了份戀家的情節。為了能更好地適應英文環境,最好不要把自己局限在中國留學生這個圈子裏,擴大自己的接觸面,有助于英文水平提高,你也會驚喜地發現世界的廣大,有很多東西等待你去發現。劉英,22歲,來自中國成都。目前正在新加坡管理學院念商務學位三年級,今年6月畢業。
新加坡非常適合國際學生學習生活
我對新加坡的學習生活很滿意,新加坡很清潔、安全、美麗。新加坡的教育體係很卓越,非常適合國際學生生活學習。新加坡的留學申請非常方便,新加坡的留學費用相對美國、英國、澳大利亞都便宜。
在中國,學生沒有太多的選擇,在新加坡選擇很多。新加坡的學生很多來自不同的國家,教育背景完全不同。但是在中國,因為人口多,激烈的競爭給學生很多無形的壓力。新加坡是個良好的、適合生活的地方,生活非常舒適。
在新加坡留學,不要僅限于與中國留學生交流,要用一種開放的心態多認識新加坡的社會和文化。勇于嘗試健康的新鮮事物,不要以自我為中心。學習很重要,但是也要花時間和功夫去學習一些國內學不到的東西。不能懶惰,積極勤懇。要有一定的自我約束力,不要放縱于電腦遊戲、煙酒等不良事物。獨自在外求學,需要比較好的生活自理能力。
2008/09/29
程式與香雞排
清華大學資訊工程碩士。曾任軟體工程師、大學資訊系兼任講師、專欄作家、技術顧問。著有《爪哇夜未眠》,譯有《深入淺出設計模式》等書。
當程式員很可憐,在台灣當程式員尤其可憐。薪資低、工作量大、地位不高、技術又容易被淘汰。難怪有人半開玩笑地告訴我,他以後不寫程式要改行去賣香雞排。
照理說,軟體開發是很專業的領域,越是專業的領域,越是處於金字塔的尖端,應該薪資很不錯才是,但不知怎地,台灣的程式員就是從來未獲重視。我們不要表面上的重視,我們要薪資上的重視。據我所知,大陸程式員的薪資水平,比起我們台灣高出許多(從國民所得、物價、房價來比較),美國程式員的薪資更是高得讓我猛嚥口水。
想想看,如果你在台北市租一間會漏水的小公寓,月租兩萬元(管理費和水電另計)。莫名其妙的多元入學方案實施之後,小孩壓力更大,要補習的東西更多。如果你有兩個小孩,每個月的補習費共要花上五千元。小孩要學費、生活費,又是另一個五千元。(乾脆含淚將小孩送人扶養。)
長得不怎麼好看的老婆就只會天天敷 SKII 面膜,因為她妄想 SKII 獨特的 Pitera 成分可以讓她的皮膚水水嫩嫩的,就和鄭秀文一樣。明明身材不好,卻又特別喜歡買 DKNY 昂貴的流行服裝。
光是這些支出加上你自己的支出,就已經超過五萬元了。你認為軟體公司會花五萬元請一個程式員嗎?在台灣,程式員要有五萬元以上的收入,恐怕要另有兼職才行。
於是你到歐萊禮兼職翻譯書,拼了老命把下班後的時間和假日的時間都拿來翻譯書,結果超過半年才翻譯完一本,還好歐萊禮仁慈不扣你延遲交稿的違約金。但這半年來身體變差了,微薄的稿酬光是拿來扣掉白蘭式雞精和補藥的支出,平均一個月也只多了約一萬元的收入,但總算因此達到收支平衡。
但這倒也不算真正的收支平衡,意外的收入支出也是有的:統一發票中獎收入平均一個月進帳 200 元,但偶而被倒會,加上有某個不長進的親戚時常來伸手要錢,你每個月還得多支出 5,000 元。婚喪喜慶的禮金支出、平常還要繳這個稅和那個稅、這個費和那個費的 ... 每每讓你心疼地暗暗叫苦。
台灣的軟體公司一向不肯好好地花錢雇用優秀的程式員,還奢談什麼知識經濟。在寫程式與賣香雞排之間作抉擇,如果我要留在台灣,我可能會選擇賣香雞排,如果我要出國謀生(美國、新加坡 ...),我會選擇寫程式。畢竟,要寫程式,就要到一個尊重程式員專業能力的地方。寫程式的薪資不高,就沒辦法吸收好的人才,至少我就不打算在台灣寫程式寫太久。興趣當然重要,但付不出帳單光靠興趣撐著,你認為能撐多久?賣香雞排稱不上是知識經濟,但只要不炸得太難吃,至少收入比寫程式好。
所以我覺得,到夜市賣香雞排的提議還真是可以考慮考慮,畢竟在台灣當程式員,一家大小在除夕夜如同賣火材的小女孩一般餓死凍死的機會很高。賣香雞排,雖然辛苦,但看著香雞排老闆們眉開眼笑的,荷包滿滿的,還可以趁著傍晚開市前,開著賓士轎車帶著全家出遊呢。
台北市饒河街夜市的攤販告訴我,攤位租金一個月一萬元出頭,我估算了一下,如果我平均一天擺攤 6 小時,賣了香雞排 300 個,每個淨賺 13 元,一個月淨賺 117,000 元,扣掉攤位租金 15,000,可以收入約十萬新台幣(免稅),實在比程式員普遍的月薪 30,000~40,000(未稅)好太多了。而且香雞排的炸法不會每年推出新版本。
在台灣的軟體公司內部,有許多非科班出身的程式員,他們的薪資低廉,通常又很努力。軟體公司就算聘到了這種便宜又努力的程式員,也不要太高興,因為這樣的程式員,通常都只是把目前的公司當一個學習的過渡階段,等到學得差不多,拍拍屁股就走人了,才沒打算一輩子接受這樣的低薪。但可悲的是,大部分的公司都沒有良好的程式員生涯規劃制度,反正大家互相利用。所以這些程式員很可能在做計畫的過程中,學不到東西又磨得身心俱疲。
所以,何苦來哉!不如我們通通去賣香雞排吧!但是你們只能到通化街夜市和士林夜市賣,不可以到我屬意的饒河街夜市和我搶生意(我打聽過,饒河街的攤位租金是三者中最便宜的)。我的攤位名稱要取做什麼?... 嗯!就叫做「Java 雞排」好了,以紀念我曾有過的 Java 程式員身份。我打算把雞排分成三種大小,最大到最小分別叫做 J2EE、J2SE 以及 J2ME。哪天你到饒河街夜市,發現了一個乾乾瘦瘦、看起來營養不良的少年頭家用著生硬的台語在吆喝著:「來呦!來呦!好呷的香雞排,J2EE 一塊 50,J2SE 一塊 40,J2ME 一塊 30......」請你也來光顧一下吧!... 看在 Java 的份上。
再論香雞排
網路上到處張貼轉寄「程式與香雞排」一文,使得該文章在網路上引起程式員廣大的迴響。當「該文作者」在 BBS 各版和各種網頁討論區都看到自己的文章被人到處張貼討論時,驚駭莫名。更荒謬的是,該文章居然還繞了一圈被人轉寄到「該文作者」的 email 信箱中。該文章引起迴響的程度遠遠超出「該文作者」的預期。
有的人對於「該文作者」寄予無限的同情,畢竟該文章內容寫得太負面了。Oh! No. 你們搞錯同情對象了,事實上該文並非「該文作者」的寫照,畢竟「該文作者」未婚(不用擔心 SKII 和 DKNY 的支出),也還沒未婚生子(不用擔心多元入學方案和補習費),也從不標會(對高風險事物沒興趣),也從不對統一發票的中獎號碼(這點小錢根本不放在眼裡),也沒有不長進的親戚敢上門伸手要錢(言語犀利苛薄,親戚躲他像躲鬼一樣避之唯恐不及),也不是非科班出身的便宜程式員(雖然沒本事取得博士學位,但好歹也是個清華大學的資訊碩士),也在台灣和大陸的 Java 圈子都小有名氣(但不排除有些人孤陋寡聞)。橫看豎看,「該文作者」都不算是一個受到壓榨的程式員。甚至「該文作者」經常逛街瘋狂瞎拼買到手軟,出門懶得擠公車只坐計程車,在路上看到賣口香糖的老人或殘障人士一定會捐錢,...... 這麼努力花錢,但不知怎地錢就是花不完,所以「該文作者」可以說是生活得相當優渥的。而且「該文作者」還得寸進尺,常嚷嚷不想工作,宣稱以後要辭職回家變賣家產,靠父母親一點一滴掙來的財產來過無憂無慮的生活。你一定想問:既然如此,「該文作者」幹嘛寫了一篇胡亂抱怨的文章,無端惹來你掬一把同情之淚?我希望你不要因此怨懟「該文作者」欺騙你的感情,「該文作者」只是利用文學中「示現修辭格」的手法來撰寫該文章,以求得強烈的閱讀效果,這和「十八歲賺一億」一書作者蓄意欺騙的行徑是完全不同的。「該文作者」悲天憫人,對於許多程式員的遭遇感同身受,透過該文章,將他所知道的一切披露出來,代替許許多多台灣的程式員發聲。
夠了!我們不要再談論「該文作者」了!免得讓你因為過度羨慕他而開始哀聲嘆氣,畢竟這麼有福氣又囂張的人實在不多。該文作者「托夢」請我整理這些日子以來讀者的看法,我無法拒絕這樣的請託,因為我和「該文作者」的關係實在太密切了。
有一些有志進入軟體產業的在學學生憂心忡忡,表示看了該文章之後,信心開始動搖,有幻滅的感覺。我認為,幻滅是成長的開始,早點經歷幻滅總是好事,才不會一廂情願地只看到事物好的一面,也因此會多做好一些準備(不管是心理上的準備或技能上的準備),成功的機會就會大幅提高。學生距離就業還有許多年的光景,只要好好把握這些年充實自己,其實以後你們在軟體產業的發展前途仍然是很不錯的。至於程式與香雞排的作者,則是一個「先天下之憂而憂,後天下之樂仍憂」的一個「馬不停蹄地憂傷」的小子,所以你們不要把他那過度負面的看法放在心上。香雞排,你們負責吃就好了,要賣也是我來賣。
也有許多讀者對於該文章感到心有戚戚焉,這類的讀者以程式員居多。我要警告這類的程式員,如果你平常不好好加強自己的分析設計技巧、管理能力、或其他技能,當程式員當了五年後仍然只是最低階的程式員,還得親自寫程式,辛辛苦苦地追著瑣碎的技術跑,那麼你就真得要小心考慮,賣香雞排或許真的比較適合你。
也有人的薪資不止五萬元,質疑該文章太悲觀。一般來說,這種人可能在外商公司、或者已經脫離程式員等級、或者在不太軟的軟體公司(例如 chip design),或者在非軟體公司的 MIS 部門。我認識一個年資約兩年的程式員,在一家不太軟的外商軟體公司當程式員,月薪約 6 萬台幣,但這樣的價碼可是大多數的程式員都無法達到的。薪水這麼高的程式員,不需要轉行賣香雞排。
有些人則沒有受到「該文作者」的恫嚇所打擊,仍堅持要繼續在軟體行業走下去,我很佩服這樣的人,因為你正是軟體產業需要的人。你不把錢看得太重,而且肯努力、有興趣,只要繼續持之以恆,我相信你在未來的報酬自然也不會太差的。但是你千萬要慎選公司,選擇一個有發展前景又願意栽培你的公司,否則你最後還是可能會去賣香雞排的。
還有一些讀者提出許多有建設性的寶貴意見,這些意見包括了:「雞排一塊不只賺 13 元,一天不只賣 300 個,十萬元的估計太保守」、「珍珠奶茶其實更有賺頭,每杯淨賺八成」、「滷味不需要技巧,比較適合程式員轉行」、「當靈修講師不需要大學學歷,輕輕鬆鬆月入七萬元」。但不管怎樣,我還是對賣香雞排情有獨鍾,我兩小時前才剛到饒河街夜市吃了一個香雞排,此刻仍然齒頰留香,回味無窮。賣香雞排的收入也有很大的差異,有些香雞排的攤位門可羅雀,有些香雞排的攤位生意好到老闆累得一邊吊點滴一邊炸香雞排。前陣子我看到電視新聞採訪一位在新竹科學園區附近賣香雞排的女老闆,她說她每個月賺 45 萬元以上。她還告訴記者香雞排好吃的秘訣在於醬料,但任憑記者怎麼追問,她仍不肯透露醬料的作法。急於開店賣香雞排的我對此婆娘小氣之舉很不以為然,我悻悻然地自言自語:「哼!秘訣?你以為我無法得知你的醬料配方?」我決定重金禮聘擅長偷拍的「郭女士」出馬,先接近該香雞排店的女老闆成為她的靈修姊妹淘,取得信任之後,再潛入該女老闆的廚房裝設針孔攝影機。我相信,以郭女士純熟的偷拍手法,醬料的配方會落入我手中。有了她的醬料,加上我獨門研發使用高筋麵粉和數十種珍貴藥材做出來的銷魂蝕骨酥皮,我寫程式的苦日子即將結束,我終將成為香雞排大亨的。想到這裡,我喜孜孜地笑了。
香雞排三部曲:完結篇
我所撰寫的香雞排首部曲(程式與香雞排)在台灣程式員圈子造成一股轟動,幾乎每個程式員都讀過,成了網路上的熱門話題。香雞排話題太過度發酵,是我所不樂見的。每當有程式員感嘆自己的收入太低時,就會丟出一句「不如改行去賣香雞排」以為自嘲,香雞排儼然是軟體產業主管最不願意碰觸的禁忌圖騰。
據我所知,許多程式員讀過該文章之後,紛紛開始自怨自艾,不想在這個圈子繼續待下去。我自感罪孽深重,所以連忙以比較樂觀的態度寫了香雞排第二部曲(再論香雞排),希望達到平衡作用。但是現在,我必須告訴你,香雞排第二部曲太過於樂觀,我必須寫下這篇香雞排第三部曲,也就是香雞排系列的完結篇,告訴你其實也不要太樂觀。為了提高可信度,本文章會提出許多數據,以為佐證。
根據我多方打量,在台灣,大學畢業,具有工作經驗一年的程式員,約 30,000~35,000 新台幣(月薪),我們就姑且取最高值 35,000 新台幣來估算好了。而在中國大陸,大學畢業,具有工作經驗一年的程式員,(月薪)薪資差異頗大,有 2,000 人民幣的,也有 4,000 人民幣的,我取估計值為 3,500 人民幣,約等於 14,000 新台幣(人民幣幣值以新台幣四倍估算)。
你很覺得很更高興嗎?因為你的薪資是大陸程式員的 2.5 倍(35,000 / 14,000 = 2.5)。先別高興,你必須把物價和國民所得也考慮進去才行。
考慮物價因素。台灣電腦雜誌的售價 200 元新台幣,大陸電腦雜誌的售價 10 元人民幣,約 40 元新台幣,是台灣的 1/5。台灣電腦書籍的售價 600 新台幣,大陸書籍的售價 30 人民幣,約 120 新台幣,是台灣的 1/5。也就是說,台灣的資訊出版品售價約為大陸的 5 倍,但台灣的程式員薪資只有大陸的 2.5 倍。也就是說,在台灣的程式員買一本書,在大陸的程式員可以買兩本書。
考慮國民所得因素。台灣國民所得年平均近 15,000 美金,約等於 525,000 新台幣,是台灣程式員月收入的 15 倍。大陸國民所得年平均近 1,000 美金,約等於 35,000 新台幣,是大陸程式員月收入的 2.5 倍。簡單來說,大陸程式員工作 2.5 個月,就能賺到大陸民眾一整年的所得;台灣程式員工作 15 個月,才能賺到台灣民眾一整年的所得。比較起來,大陸程式員賺錢的速度是台灣程式員的 15 / 2.5 倍,也就是 6 倍。
因為大陸的都市生活水平比未開發的地方高出許多,貧富差距、城鄉差距很大,為了減少誤差,我們必須把這些因素所造成的影響也算進去。我不知道大陸的房租、交通費、餐飲費用為何,所以無法比較。但我估計,這會使得前述的「6 倍」數字稍微降低一點,所以我把「6 倍」降為「5 倍」,這樣的估算應該算合理,差距 5 倍耶!如果你的薪水原本是四萬元,五倍就是 20 萬新台幣。想想,在台灣,一個月有了二十萬的進帳,日子豈不快樂似神仙。
台灣程式員的薪資水準比人家差五倍,難道能力也比人家差五倍?我不這麼認為,我反倒認為台灣的程式員普遍能力比大陸程式員好,因為台灣的資訊產業、資訊教育很早就開始了。像我這種寫了近 19 年程式的人也不是很少見,有過十年以上程式經驗的人更是比比皆是。
香雞排三部曲系列文章不是為我自己抱怨,因為我對自己的未來有一套規劃,不打算讓自己陷入「富爸爸,窮爸爸」一書所謂的「mouse race」中。只是,有多少程式員會有這樣的規劃。台灣的程式員就像是在被熱水緩慢地煮熟的青蛙。而我就像是一隻已經跳離開鍋子,開始尋找水塘的青蛙,我寧願在夏天發燙的柏油路上,被川流不息的來車壓成扁扁的「青蛙乾」,也不願意當一隻任人烹煮的青蛙。
我知道我這篇文章又會引起一些人的抗議,特別是資訊產業主管的抗議。如果你是這樣的主管,我勸你先別找我興師問罪,因為根據我的判斷,這篇文章效用只有短暫的。原本不自覺水溫上升的青蛙,在讀過這篇文章之後開始騷動「你說什麼!鍋底正在加熱!」... 一陣子緊張之後又漸漸歸於平靜,或許是因為已經習慣這樣的環境了,或許是因為擔心出了鍋子會找不到水源而乾死,所以青蛙繼續待在鍋內,真正跳出鍋子另覓水塘的青蛙只有極少數。而有趣的是,勸青蛙不要跳出鍋子的那些主管們,本身可能也是一隻待煮熟的青蛙呢!
拿起溫度計,現在就量一下你周圍的水溫有多少度?攝氏 60 度?攝氏 70 度?
2008/09/13
健康的迷思:土雞蛋真的更有營養嗎?
問題一:可能被農藥污染
大家看中土雞蛋的一種重要原因是它們是土生土長的“走地雞”,吃的是田間雜草、糧食顆粒,似乎比飼料養雞的蛋更加綠色環保,范志紅卻有不同看法,她提出:既然在鄉村中,雞被農藥毒死的事情屢見不鮮,那麼雞蛋被農藥污染的危險不能忽視。農家散養的雞經常有機會接觸農藥、化肥、鼠藥、殺蟲劑等化學用品,其危險性甚至可能高於雞場所產的雞蛋。
問題二:蛋殼太臟清洗不易
從衛生角度來說,土雞蛋也有隱患。土雞蛋蛋殼上的一般沾有各種臟東西,如果不經仔細清洗,很容易污染剩蛋容器、水池和手,想想你在烹飪的時候還會用手做其他食物,用到水池、廚具,稍不留意就給食品衛生帶來隱患。還有些人會直接把雞蛋放入冰箱,更加會造成整個冰箱的污染。所以拿過雞蛋應當仔細洗手,磕碰蛋殼的地方也要擦洗乾淨。
蛋黃顏色深的就是土雞蛋嗎?
很多人根據蛋黃顏色更深而判定是否是土雞蛋。其實,蛋黃的顏色與飼料原料有關,和養殖方式無關。蛋黃顏色深淺通常僅表明色素含量的多寡,飼料中增加青菜葉、藻類、玉米等有色原料,或特意喂一些類胡蘿蔔素物質,則蛋黃顏色自然變深。而有些色素如葉黃素、胡蘿蔔素等可在體內轉變成維生素A,因此,在無污染情況下,蛋黃顏色較深的雞蛋營養稍好一些。這種雞蛋不一定就是土雞蛋。
而根據畜牧學方面的研究資料和食品測定數據,土雞蛋和飼料蛋相比,蛋白質和脂肪含量差異不大,維生素AD含量略低,omega-3脂肪酸比例略高,風味較好。可以看出兩者營養差異不是很大,那麼又何必放著好好的雞蛋不吃,千方百計找農家雞蛋呢?況且要補充對人體心血管有益的omega-3脂肪酸,多吃魚肉效果更好。
范志紅最後指出,從營養價值上來看,重要的問題是雞吃什麼,雞的生活狀態是否健康。是否農家養的土雞蛋,並不那麼重要。相比之下,一些經過認證的綠色雞蛋較為令人放心。這些雞蛋不是關在籠子裏,其活動區域更安全,蛋殼往往還經過處理,衛生條件較為令人放心。同時,品牌雞蛋都有生產日期,有利於了解雞蛋的新鮮度。
夏季天氣炎熱,細菌活動猖獗,范志紅建議,無論你購買哪種雞蛋,建議你少買一些,短期內吃完,避免雞蛋回家之後繼續久存,發生散黃甚至變質的現象。
Buddhism and Software Developer
In today's fast-paced society, we are often surrounded by work, goals, and external pressures. However, the wisdom found in Buddhism off...
-
如果你平時在玩線上遊戲或是工作中經常需要大量點擊滑鼠,那麼一定有大量點擊滑鼠的經驗,非常浪費時間與精力!這個小程式可以讓您免於在做這些無意義的工作了! MouseClick可以依據使用者需求設定點擊的頻率(毫秒)。並且在2.0版可以讓使用者自由選擇滑鼠左鍵,右鍵,中鍵的點擊...
-
Abstract The DrivingDirection package (com.google.googlenav.DrivingDirection) is removed since Android SDK 1.1. However, in this art...
-
如果你忘記即時通的密碼,MessenPass是一個可以顯示儲存在你電腦的密碼的軟體。他可以破解的密碼相當多種,可見下列表。 MSN Messenger Windows Messenger (In Windows XP) Windo...