#!/bin/ksh
DATE=`date +%Y%m%d`
export ORACLE_HOME=/oracle/app/db
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
DATE=`date +%Y%m%d`
/oracle/app/db/bin/exp boss_v2/ owner=boss_v2 compress=n direct=y recordlength=65535 file=./boss_v2$DATE.dmp log=./log/boss_v2$DATE.log
if [ -f boss_v2$DATE.dmp ]
then
rm -f old.dmp
mv new.dmp old.dmp
mv boss_v2$DATE.dmp new.dmp
fi
posted @
2009-12-11 13:57 poower 閱讀(200) |
評論 (2) |
編輯 收藏
另外,
FireFox的
收藏夾(書簽)也可以通過菜單選項直接導(dǎo)出,具體方法是:打開
Firefox,點擊“書簽 -> 書簽管理”啟動書簽管理器,點擊“文件 -> 導(dǎo)出”來備份現(xiàn)有的書簽。在另一臺機器上,再用書簽的“導(dǎo)入”功能把備份的書簽導(dǎo)入到新的
Firefox的配置中即可。
posted @
2009-11-20 21:34 poower 閱讀(172) |
評論 (0) |
編輯 收藏
輕松面試找到理想員工-非官方的面試技術(shù)指南
簡述:
本文作者Joel Spolsky 是紐約市一家軟件公司Fog Creek Software的創(chuàng)始人。他畢業(yè)于耶魯大學(xué),曾分別在美國微軟、Viacom、Juno等公司任軟件設(shè)計師、經(jīng)理職位。本文來自于《祖兒談軟件》,文章原名為《輕松面試找到理想員工——非官方的面試技術(shù)指南》,作者最初本意是針?/p>
作者:周思博 (Joel Spolsky 更新日期:2005-05-22
來源:chinese.joelonsoftware.com 瀏覽次數(shù):
譯: Chen Bin 編輯: Rick Ju 2000年3月23日
雇傭合適的人對于Fog Creek軟件公司來說是非常關(guān)鍵的。在我們這個領(lǐng)域,有三類人可以挑選。在一個極端, 是哪些混進來的, 甚至缺乏最基本的工作技巧. 只要問這類人兩三個簡單的問題,再讀一下他們的簡歷,就可以輕易地剔除他們。另一個極端的類型是 才華橫溢的超級明星 這些人僅僅為了好玩就用匯編語言為Palm Pilot(一種手掌電腦)寫了一個Lisp(一種人工智能編程語言)編譯器。在這兩種極端類型中間的是一大群不能確定水平的候選者,也許他們中的某些人能干些什么?這里的關(guān)鍵是明白超級明星和那一大堆屬于中間類型的人的區(qū)別,因為Fog Creek軟件公司只雇傭超級明星。下面我要介紹一些找出超級明星的技巧。
Fog Creek公司最重要的雇傭標(biāo)準(zhǔn)是:
有頭腦, 并且
完成工作
就是這些了。符合這樣標(biāo)準(zhǔn)的人就是我們公司需要的員工了。 記住這條標(biāo)準(zhǔn)。 每天上床前背誦這條標(biāo)準(zhǔn)。我們公司的目標(biāo)之一就是雇傭擁有這樣的潛質(zhì)的人,而不是雇傭懂某些技術(shù)的人。任何人所擁有的某些具體技術(shù)都會在幾年內(nèi)過時,所以,雇傭有能力學(xué)習(xí)新技術(shù)的人,要比雇傭那些只在這一分鐘知道SQL編程是怎么回事的人對公司更劃算一點。
有頭腦確實是一個很難定義的品質(zhì)。但是讓我們看一些在面試時能提問的一些問題,通過這些提問,我們可以找出擁有這種品質(zhì)的人。完成工作非常關(guān)鍵。看起來有頭腦但是不能完成工作的人經(jīng)常擁有博士學(xué)位,在大公司工作過,但是在公司中沒有人聽他們的建議,因為他們是完全脫離實際的。比起準(zhǔn)時交活兒,他們寧愿對于一些學(xué)院派的東西沉思。這些人由以下特性而可以識別出來。他們總是愛指出兩個根本不同的概念間的相似性。例如,他們會說“Spreadsheets是一種特殊的編程語言”,然后花一個禮拜寫一篇動人的,智慧的白皮書。這篇白皮書論述了,作為一個編程語言,spreadsheet關(guān)于計算語言特性的方方面面。聰明,但是沒用。
現(xiàn)在,我們來談?wù)?strong>完成工作但是沒有頭腦的人。他們愛做蠢事。從來也沒有考慮過將來得靠他們自己或者別的什么人來亡羊補牢。通過制造新的工作,他們成為了公司的負(fù)債而不是資產(chǎn)。因為他們不僅沒有為公司貢獻價值,還浪費了好員工的時間。這些人通常到處粘貼大堆的代碼,而不愿意寫子程序。他們是完成了工作,但是不是以最聰明的方式完成工作。
面試時最重要的法則是:
做決定
在面試結(jié)束時,對于被面試者,你不得不做一個直截了當(dāng)?shù)臎Q定。這個決定只有兩個結(jié)果:雇傭或者不雇傭. 回到你的電腦前,立刻用電子郵件通知招聘負(fù)責(zé)人你的決定。電子郵件的主題應(yīng)該是雇傭或者不雇傭。接著你需要在正文中寫兩段來支持你的決定.
沒有其他的答案。永遠(yuǎn)不要說,“雇傭你,但是不能在我的團隊中”。這是非常粗魯?shù)模驗槟阍诎凳緫?yīng)試者沒有聰明到能有和你一起工作的資格,但是以他的頭腦適合進入那些天生輸家隊伍。如果你發(fā)覺自己被誘惑,想說出那句“雇傭你,但是不能在我的隊伍中”,那么就簡單的把這句話變成“不雇傭”再說出口。這樣就沒事了。甚至如果某個人在特定領(lǐng)域很能干,但是在別的隊伍中將會表現(xiàn)不好,也是不雇傭。事物變化的如此之快,我們需要的是在任何地方都能成功的人。如果某些情況下你發(fā)現(xiàn)了一個白癡專家(擁有某些特殊能力的白癡),這個專家對于SQL非常,非常,非常的精通,但是除此之外什么也學(xué)不會,不雇傭。在Fog Creek公司他們沒有將來。
永遠(yuǎn)不要說,“也許,我吃不準(zhǔn)”。如果你吃不準(zhǔn),意味著不雇傭。看,比你想象的容易的多。吃不準(zhǔn)?就說不!同樣,如果你不能作出決定,那意味著不雇傭。不要說,”嗯,雇傭,我想是這樣的。但是關(guān)于...,我想知道 ...”。這種情況就是不雇傭。
最重要的是記住這點,放棄一個可能的好人要比招進一個壞人強(譯者按:中國有位哲人說過,寧可錯殺一千,不可放過一個,呵呵)。一個不合格的求職者如果進入了公司,將要消耗公司大量的金錢和精力。其他優(yōu)秀員工的還要浪費時間來修復(fù)這個人的錯誤。如果現(xiàn)在你還在猶豫,不雇傭。
如果你是Fog Creek公司的面試官,當(dāng)你拒絕了大量的應(yīng)聘者時,不要為Fog Creek公司將因此雇不到任何人了而憂慮。這不是你的問題。這是招聘負(fù)責(zé)人的問題。這是人力資源部的問題。這是Joel(譯者注: Fog Creek公司的老板,本文作者)的問題。但不是你的問題。不停地問自己,哪種情況更糟糕?一種情況是我們變成了一個龐大的,糟糕的軟件公司,充斥著許多腦袋空空如可可果殼的家伙,另一種情況是我們是一個小而高品質(zhì)的公司。當(dāng)然,找到優(yōu)秀的應(yīng)聘者(并聘用他們)是很重要的。找到有頭腦而且完成工作的人是公司中的每個員工的日常工作之一。但是當(dāng)你作為Joel Creek公司的一員真的開始面試一個應(yīng)聘者時,要裝作現(xiàn)在正有很多優(yōu)秀的人想打破頭擠進Fog Creek公司。總之,無論找到一個不錯的應(yīng)聘者是多么的難,永遠(yuǎn)不要降低你的標(biāo)準(zhǔn)。
但是你如何作出雇傭或者不雇傭這樣艱難的決定?你只要在面試過程中不停地問自己:這個人有頭腦嗎?這個人能完成工作嗎?要作出正確的回答,在面試時你必須問對問題。
開個玩笑,下面我要問個有史以來最差的面試問題: “Oracle 8i中的數(shù)據(jù)類型varchar和varchar2有什么區(qū)別”這是一個可怕的問題。掌握這種瑣碎的技術(shù)細(xì)節(jié)和Fog Creek公司想雇傭你之間沒有任何聯(lián)系。誰會去記這種東西?如果有在線幫助,你可以在15秒內(nèi)找到答案。
實際上,還有更差的問題,等會兒我會談到的。
現(xiàn)在我們要談到有趣的部分了:面試時提哪些問題。我的面試問題清單來自于我去微軟公司找第一份工作的經(jīng)歷。這里實際上有幾百個微軟面試問題。每個人都有偏愛的問題。你也可以發(fā)展一套自己的面試問題以及面試的個人風(fēng)格,這樣你就可以比較容易地作出雇傭/不雇傭的決定。以下是我成功使用過的一些面試技巧,
在面試前,我讀一遍應(yīng)試者的簡歷,然后在一張紙片上隨便寫以下我的面試計劃。這個計劃實際上就是我要問的問題清單。以下是一個例子(用來面試程序員的):
- 介紹
- 應(yīng)試者參加過的項目
- 無法回答的問題
- C語言函數(shù)
- 你滿意嗎?
- 設(shè)計問題
- 挑戰(zhàn)
- 你還有什么問題?
在面試前,我非常,非常當(dāng)心,避免自己先入為主。如果在面試前你就已經(jīng)想當(dāng)然地認(rèn)為,一個麻省理工的博士一定是一個有頭腦的人。那么在接下來的一小時的面試時間內(nèi),無論那個麻省理工的博士說什么都不能改變你的最初印象。如果在面試前你就認(rèn)為這個應(yīng)試者是個傻瓜,那么他面試時說什么也無濟于事。面試就象一個非常精巧的天平。一小時的面試結(jié)束后就要對一個人下結(jié)論是不容易的(但是你又必須在面試結(jié)束后得出結(jié)論)。一些不起眼的細(xì)節(jié)可能會影響最后的結(jié)論。如果你在面試開始前對于應(yīng)試者有了一點了解的話,就好比天平的某一端加上了重重的砝碼。這樣面試本身就會變得沒有用處了。以前有一次在面試前,一個招聘負(fù)責(zé)人跑進我的房間說,“你肯定會愛上這個家伙的!" 對一個男孩? 天哪,這簡直讓我發(fā)瘋。我本來應(yīng)該說,“嗯,如果你這么確定我會喜歡他,為什么你不干脆雇傭他,何必讓我浪費時間來面試?”但是那時我還太年輕幼稚, 所以還是面試了那個人。當(dāng)這個家伙開始說一些蠢話時,我對自己說,“哇塞,這應(yīng)該是個例外情況,也許是大智若愚。”我開始帶著玫瑰色眼鏡看他了。于是我以說“雇傭”結(jié)束了面試,雖然他是一個糟糕的面試者。接下來發(fā)生了什么事?除了我,其他的面試官都說,不要雇傭這個人。教訓(xùn)是,不要聽別的人的話,在面試應(yīng)試者前不要四處打探這個面試者的情況。最重要的是不要和別的面考官談?wù)搼?yīng)試者,除非你們都已經(jīng)作出了獨立的判斷。這才是科學(xué)的做法。
作為面試步驟的第一步,介紹的目的是讓應(yīng)試者放輕松。我通常花30秒鐘,講一下我是誰,接下來面試會如何進行。我總是使得應(yīng)試者確信,我們關(guān)心的是他(她)如何解決問題的,而不是他(她)的最終答案是對還是錯。順便說一下,面試時,你不要和應(yīng)試者隔著一個桌子坐著,否則在你和面試者之間就有了一個障礙,并且暗示著一種比較正式嚴(yán)肅的氣氛,這樣應(yīng)試者就很難放松了。更好的辦法是把桌子靠墻放著,或者和應(yīng)試者坐在桌子的同一邊,這樣有助于應(yīng)試者放松。只有應(yīng)試者不會因為緊張而表現(xiàn)失常,你才能更有效的進行面試.
第二步的內(nèi)容就是問應(yīng)試者最近做了些什么項目。對剛畢業(yè)的學(xué)生, 如果有論文就問問論文, 沒有的話, 就問問他們做過什么很喜歡的大作業(yè).例如,有時候我會問一下,“你最喜歡上學(xué)期哪門課程?不一定要和計算機相關(guān)的。”事實上,如果應(yīng)試者回答的課程和計算機沒有關(guān)系,我會比較高興。有時候你會發(fā)現(xiàn)這個計算機系應(yīng)屆生選擇了盡可能少的計算機相關(guān)課程,但是卻選修了很多和音樂相關(guān)的課程。但是他(她)卻說最喜歡的課程是《面向?qū)ο髷?shù)據(jù)庫》。哼哼,不錯啊. 不過如果你直接承認(rèn)你喜歡音樂勝于計算機, 而不是在這兒胡說八道的話, 我會更高興一點。
當(dāng)面試有工作經(jīng)驗的人時,你可以讓他們談一下前一份工作。
我問這個問題的目的是在尋找一樣品質(zhì):熱情。在應(yīng)試者談到他(她)最近做過的項目時,你觀察到以下跡象都是不錯的:
- 談到他們做過的項目時變得熱情洋溢;他們的語速更快,語言更生動活潑。這說明他們對某些東西有興趣,有熱情(因為現(xiàn)實中有許多人對所做的項目根本漠不關(guān)心呢)。即使他們激動地表達對做過的項目的負(fù)面感情,這也是一個好的信號。“我曾經(jīng)為上一個老板安裝Foo Bar Mark II,但他是個傻瓜!”表現(xiàn)出熱情的人就是我們要雇傭的人。差的應(yīng)試者對工作根本就不關(guān)心,所以根本不會激動。一個非常好的信號是當(dāng)應(yīng)試者很激動地談?wù)撋弦环莨ぷ鳎灾劣跁簳r忘記了他們正在被面試。有時候應(yīng)試者剛開始面試時表現(xiàn)的很緊張 -- 這是很正常的現(xiàn)象,所以我通常忽略不計。但是當(dāng)他們談到單色計算藝術(shù)(Computational Monochromatic Art)時,這個家伙變得極端興奮, 一點都不緊張了。不錯,我喜歡這樣的應(yīng)試者,因為他們關(guān)心他們做的事。(什么是單色計算藝術(shù)?拔掉你的電腦顯示器的電源就可以看到了)
- 能認(rèn)真地去解釋事情。某些人被我拒掉的原因就是他們不會用普通人能明白的語言去解釋他們做過的項目。很多工科專業(yè)的人總是以為所有人都知道Bates理論(譯者注: Bates Theorem,一種經(jīng)濟學(xué)的理論)或者Peano公理組(譯者注: Peano's Axioms,數(shù)論中的一些定理)是什么。如果應(yīng)試者開始滿口行話了,讓他們停一停,然后你說,“能幫我個忙嗎?就是為了練習(xí)一下,你能把剛才說的用我老祖母也能理解的話說一遍嗎?”但即便如此, 有些人還是繼續(xù)用那些術(shù)語, 而且根本沒法讓人明白他們在說什么。天哪!
- 如果這個項目是一個團隊項目,看看他們是否在有承擔(dān)領(lǐng)導(dǎo)責(zé)任的跡象?一個應(yīng)試者可能會說:“我們用的是X方法,但是老板說應(yīng)該是Y,而客戶說應(yīng)該是Z。”我會問,“那么你怎么做的?”一個好的回答可能是“我設(shè)法和團隊中別的人開了個會,然后一起搞出個辦法...”壞的回答看起來象,“嗯,我什么也不能做。這樣的問題我解決不了。”記住,聰明并且能完成工作。要搞清楚某人是否能完成工作的一個辦法就是看看他(她)過去是否傾向于完成任務(wù)。事實上,你可以主動要求他們給你個例子證明他們能擔(dān)任領(lǐng)導(dǎo)作用,完成任務(wù)。-例如克服公司的陳規(guī)陋習(xí)。
現(xiàn)在我們談?wù)勄鍐紊系牡谌睿?em>無法回答的問題。這很有趣。這個主意的關(guān)鍵在于問一些不可能有答案的問題,就是想看一下應(yīng)試者怎么辦。“西雅圖有多少眼科醫(yī)生?”“華盛頓紀(jì)念碑有多重?”“洛杉機有多少加油站?”“紐約有多少鋼琴調(diào)音師?”
- 聰明的應(yīng)試者猜到你不是要測驗他們的專業(yè)知識,他們會積極地給出一個估計。“嗯,洛杉機的人口是七百萬;每個人平均擁有2.5輛轎車...”當(dāng)然如果他們的估計完全錯誤了也沒有關(guān)系。重要的是他們能積極地試著回答問題。他們可能會試著搞清楚每個加油站的儲量。“嗯,需要四分鐘給一個儲油罐加滿油,一個加油站有十個油泵每天運行十八個小時...”他們也可能試著從占地面積來估計。有時, 他們的想法的創(chuàng)造力讓你吃驚. 而有時, 他們直接要一個LA的黃頁去查。這都是好跡象。
- 不聰明的應(yīng)試者則被難住了。他們目瞪口呆地望著你,好像你來自火星。你不得不提示:“嗯,如果你想建立一個象洛杉機那么大的城市,你需要建立多少個加油站?”你還可以提示他們:“加滿一個儲油罐要多長時間?”不過,這些榆木疙瘩腦袋還是只會坐在那里發(fā)呆,你得拖著他們往前走才行。這類人不會解決問題,我們可不要這樣的人。
關(guān)于編程問題,我通常要求應(yīng)試者用C語言寫一些小函數(shù)。以下是我通常會出的題目:
- 將一個字符串逆序
- 將一個鏈表(linked list)逆序
- 計算一個字節(jié)(byte)里有多少bit被置1
- 搜索給定的字節(jié)(byte)
- 在一個字符串中找到可能的最長的子字符串,該字符串是由同一字符組成的
- 字符串轉(zhuǎn)換成整數(shù)
- 整數(shù)轉(zhuǎn)換成字符串(這個問題很不錯,因為應(yīng)試者要用到堆棧或者strev函數(shù))
注意,通常你不會希望他們寫的代碼多于5行,因為你沒有時間理解太長的代碼。
現(xiàn)在我們來詳細(xì)看一看其中幾個問題: 第一個問題: 逆序一個字符串。我這輩子還沒有見過那個面試者能把這題目一次做對。所有的應(yīng)試者都試圖動態(tài)生成緩沖區(qū),然后將逆序的字符串輸出到該緩沖區(qū)中。問題的關(guān)鍵在于,誰負(fù)責(zé)分配這個緩沖區(qū)?誰又負(fù)責(zé)釋放那個緩沖區(qū)?通過這個問題,我發(fā)現(xiàn)了一個有趣的事實,就是大多數(shù)認(rèn)為自己懂C的人實際上不理解指針和內(nèi)存的概念。他們就是不明白。這真叫人吃驚,無法想象這種人也能做程序員。但他們真的就是!這個問題可以從多個角度判斷應(yīng)試者:
- 他們的函數(shù)運行快嗎?看一下他們多少此調(diào)用了strlen函數(shù)。我曾經(jīng)看到應(yīng)試者寫的strrev的算法竟然只有O(n^2) 的效率,而標(biāo)準(zhǔn)的算法效率應(yīng)該是O(n),效率如此底下的原因是因為他們在循環(huán)中一次又一次調(diào)用strlen函數(shù)。
- 他們使用指針運算嗎(譯者按:原文為pointer arithmetic,指的是加減指針變量的值)?使用指針運算是個好現(xiàn)象。許多所謂的“C程序員”竟然不知道如何使用指針運算(pointer arithmetic)。當(dāng)然,我在前文說過我不會因為應(yīng)試者不掌握一種特定的技巧而拒絕他。但是,理解C語言中的指針不是一種技巧,而是一種與生俱來的才能。每年一所大學(xué)要招進200多個計算機系的新生,所有這些小孩子4歲就開始用BASIC語言在Atari 800s寫冒險游戲了。在大學(xué)里他們還學(xué)Pascal語言,學(xué)得也很棒。直到有一天他們的教授講了指針的概念,突然,他們開始搞不懂了。他們就是不能再理解C語言中的任何東西了。于是90%的計算機系學(xué)生轉(zhuǎn)系去學(xué)政治學(xué)。為了挽回面子,他們告訴朋友,他們之所以轉(zhuǎn)系是因為他們計算機系英俊貌美的異性太少。許多人注定腦子里就沒有理解指針的那根弦。所以說理解指針是一種與生俱來的品質(zhì),而不是一種單純的技巧。理解指針需要腦子轉(zhuǎn)好幾個彎,某些人天生不擅長轉(zhuǎn)這幾個彎。
第三個問題可以考考面試者對C的位運算的掌握,但這是一種技巧,不是一種品質(zhì),所以你可以幫助他們。有趣的等他們建立了一個子函數(shù)用來計算byte中為1的位的數(shù)目,然后你要求他們優(yōu)化這個子函數(shù),盡量加快這個函數(shù)的運行速度。聰明的應(yīng)試者會使用查表算法(畢竟這個表只有 256個元素,用不了多少內(nèi)存),整個表只需要建立一次。跟聰明的應(yīng)試者討論一下提高時間/空間效率的不同策略是十分有意思的事情. 進一步告訴他們你不想在程序啟動時初始化查詢表。聰明的面試者可能會建議使用緩沖機制,對于一個特定的byte,只有在第一次被查詢時進行計算,然后計算結(jié)果會被放入查詢表。這樣以后再被查詢時直接查表就行了。而特別特別聰明的面試這會嘗試有沒有建立查詢表的捷徑,如一個byte和它的置1的bit數(shù)之間有沒有規(guī)律可循?
當(dāng)你觀察應(yīng)試者寫C代碼時,以下一些技巧會對你有幫助:
- 事先向應(yīng)試者說明,你完全理解,沒有一個好的編輯器光在紙上寫代碼是困難的,所以你不在乎他們手寫的代碼是否看上去不整潔。你也完全明白沒有好的編譯器和調(diào)試器,很難第一次就寫出完全沒有bug的程序,所以請他們不必為此擔(dān)心。
- 好程序員的標(biāo)志:好程序員寫完“{”符號后,通常立刻跟上“}”符號,然后再在當(dāng)中填上代碼。他們也傾向于使用命名規(guī)則,雖然這個規(guī)則可能很原始。如果一個變量用作循環(huán)語句的索引,好程序員通常使用盡可能少的字符為它命名。如果他們的循環(huán)語句的索引變量的名字是CurrentPagePositionLoopCounter,顯而易見他們寫代碼的經(jīng)驗還不夠多。偶爾,你會看到一個C程序員寫下象if (0==strlen(x))一樣的代碼,常量被放在==的左邊。這是非常好的現(xiàn)象。這說明他因為總是把=和==搞混,已經(jīng)強迫自己養(yǎng)成這種習(xí)慣以避免犯錯。
- 好的程序員在寫代碼前會訂一個計劃,特別是當(dāng)他們的代碼用到了指針時。例如,如果你要求逆序一個鏈表,好程序員通常會在紙的一邊畫上鏈表的草圖,并表明算法中的索引指針當(dāng)前移動到的位置。他們不得不這樣做。正常人是不可能不借助草圖就開始寫一個逆序鏈表的程序的。差的程序員立刻開始寫代碼。
不可避免的,你會在他們的程序中發(fā)現(xiàn)bug,于是我們現(xiàn)在來到了第五個問題:你對代碼滿意嗎? 你可能想問,“好吧,bug在哪里?”這是來自地獄的一針見血的問題,要回答這個問題可要大費口舌。所有的程序員都會犯錯誤,這不是問題。但他們必須能找到錯誤。對于字符串操作的函數(shù),他們通常會忘記在輸出緩沖區(qū)加上字符串結(jié)束符。所有的函數(shù),他們都會犯off-by-one錯誤(譯者按:指的是某個變量的最大值和最小值可能會和正常值差1)。他們會忘掉正常的C語句結(jié)尾的分號。如果輸入是零長度字符串,他們的函數(shù)會運行錯誤。如果malloc調(diào)用失敗而他們沒有為此寫好錯誤處理代碼,程序會崩潰。一次就能把所有事情做對的程序員非常,非常,非常地少.不過要是真的碰上一個的話, 提問就更有意思了. 你說,"還有Bug"。他們會再仔細(xì)地檢查一遍代碼。這個時候, 觀察一下他們內(nèi)心是否開始動搖了, 只是表面上勉強堅持說代碼沒有問題。總之,在程序員寫完代碼后,問一下他們是否對代碼滿意是個好主意。就像Regis那樣問他們!(譯者按,Regis Philbin是美國ABC電視網(wǎng)的游戲電視節(jié)目主持人,他的口頭禪是“這是你的最后的答案嗎?”)
第六部分:關(guān)于設(shè)計的問題。讓應(yīng)試者設(shè)計某樣?xùn)|西。Jabe Blumenthal,Excel的原始設(shè)計者,喜歡讓應(yīng)試者設(shè)計房子。Jabe說,曾經(jīng)有一個應(yīng)試者跑到白板前,畫了一個方塊,這就是他的全部設(shè)計。天哪,一個方塊!立刻拒絕這樣的家伙。你喜歡問什么樣的設(shè)計問題?
- 好的程序員會問更多的信息。房子為誰造的?我們公司的政策是,我們不會雇傭那些在設(shè)計前不問為誰設(shè)計的人。通常,我會很煩惱我得打斷他們的設(shè)計,說“事實上,你忘記問這個房子是給誰設(shè)計的了。這個房子是給一群長頸鹿造的。”
- 笨笨的應(yīng)試者認(rèn)為設(shè)計就像畫畫,你想畫什么就畫什么。聰明的應(yīng)試者明白設(shè)計的過程是一系列艱難的權(quán)衡。一個很棒的設(shè)計問題是:設(shè)計一個放在街角的垃圾箱。想一想你得做多少權(quán)衡!垃圾箱必須易于清空,但是很難被偷走;易于放進垃圾,但是碰到狂風(fēng)大作,里面的垃圾不會被吹出來;垃圾箱必須堅固而便宜。在某些城市,垃圾箱必須特別設(shè)計,以防恐怖分子在里面藏一個定時炸彈。
- 有創(chuàng)造力的應(yīng)試者會給出有趣而獨特的設(shè)計。我最喜歡的問題之一是為盲人設(shè)計一個放調(diào)味品的架子(譯者按:原文為spice rack,老外的廚房里有個專門放調(diào)味品的架子,上面放了很多小罐罐,里面裝了各種各樣的調(diào)料)通常許多應(yīng)試者的建議是把布萊葉文(一種盲人使用的文字)刻在放調(diào)料的罐子上,這樣文字會卷起來而變形。我碰到一個應(yīng)試者,他的設(shè)計是把調(diào)料放在抽屜里,因為他覺得水平地感知布萊葉文比垂直地做更方便。(試試看!)這個答案這樣有創(chuàng)意,使我震驚!我面試了有一打得程序員,從來沒有人想到過類似的答案。這樣有創(chuàng)意的答案確實躍過了普通人考慮問題的條條框框。僅僅因為這個答案太有創(chuàng)意了,而且應(yīng)試者別的方面還過得去,我雇傭了這個應(yīng)試者,他現(xiàn)在已經(jīng)成為Excel團隊中一個優(yōu)秀的項目經(jīng)理了(譯者按,本文作者曾在微軟工作過)。
- 總是爭取一個確定的了結(jié)。這也是完成工作的特質(zhì)的一部分。有時候應(yīng)試者會猶猶豫豫不能作出一個決定,試圖回避困難的問題,留著困難的問題不作決定就直接向下進行,這很不好。好的應(yīng)試者有一種推動事情自然地前進的傾向,即使你有意把他們拖回來。如果關(guān)于某個話題的討論開始原地打轉(zhuǎn)變得沒有意義了,好的應(yīng)試者會說,“嗯,我們可以整天談?wù)撨@個,但是我們得做點什么。為什么我們不開始...”
于是我們來到了第七部分,挑戰(zhàn)。這部分很好玩。在面試中留心一下, 當(dāng)面試者的回答絕對的百分之百毫無爭議時, 你可以說: " 嗯, 等一下等一下." 然后花上兩分鐘玩一下魔鬼的游戲(譯者按,原文為devil's advocate,魔鬼代言人指的是違背自己的良知,為錯誤邪惡的觀點辯護). 記住一定要在你可以肯定他正確時和他爭論。
這個很有意思.
- 軟弱的應(yīng)試者會屈服。那我就和他說拜拜了。
- 堅定的應(yīng)試者會找到一個辦法說服你。他們會以肯尼迪總統(tǒng)的口才來說服你,“也許我誤會了你的意思,”他們這樣開頭,但是正文仍是堅定地站穩(wěn)立場。這樣的人我就雇傭。
不得不承認(rèn),面試雙方的地位并不是平等的。有可能應(yīng)試者由于害怕你的權(quán)力而不敢于你爭辯。但是,好的應(yīng)試者有足夠的熱情和勇氣堅持正確的觀點,他們由于熱切希望說服你而會暫時忘記正在被面試。這樣的人就是我們要雇傭的人。
最后,可以問一下應(yīng)試者有什么想問的。一些人喜歡看看應(yīng)試者這時是否會問一些聰明的問題。這是市面上流行的面試書籍的標(biāo)準(zhǔn)技巧。我個人不在乎應(yīng)試者問什么,因為這時我已經(jīng)做了決定。麻煩在于,應(yīng)試者也許已經(jīng)見了5、6個人,進行了好幾輪面試,他們可能很累了,以至于不能為每輪面試都準(zhǔn)備一個聰明而獨特的問題。所以如果他們沒有可問的,沒關(guān)系。
我總是留下面試的最后5分鐘來推銷我的公司。這很重要。即使我不打算雇傭眼前這個應(yīng)試者。如果你幸運的找到一個很棒的應(yīng)試者,你當(dāng)然愿意做任何事情說服他(她)來你的公司。即使他們不是好的應(yīng)試者,你也要盡力讓他們?yōu)镕og Creek公司激動,這樣面試結(jié)束時他們會對Fog Creek公司留下一個很好的印象。記住,應(yīng)試者并不僅僅是可能的雇員,他們也是顧客,也是我們公司的推銷員。如果他們覺得我們的公司很棒,他們也許會推薦朋友來面試。
啊哈,我記得我說過我會給出一些應(yīng)該避免的非常不好的反面的試題例子。
首先,避免不合法的問題。有關(guān)種族,宗教,性別,出生國,年齡,服役記錄,是否老兵,性取向,生理障礙的問題都是不合法的。即使他們的簡歷說他們1990年在軍中服役,也不要問有關(guān)問題。也許這會讓他們愉快地談?wù)撛诤硲?zhàn)爭中的經(jīng)歷。但是你的問題還是不合法的。如果簡歷上寫著他們上過Technion in Haifa, 不要問他們是否是以色列人, 即使只是為了閑談, 因為這是違法的. 下面有一個很好的不合法的例子。點擊這里有很多關(guān)于什么是違法的討論。(但是這個網(wǎng)站的其余問題夠愚蠢的。)
其次,不要在問題中給予應(yīng)試者暗示,我們公司喜歡或者不喜歡什么樣的員工。我能想到的一個例子是問應(yīng)試者是否有小孩或者是否結(jié)婚了。應(yīng)試者也許會想我們不喜歡有家庭拖累的員工。
最后,不要問那些腦筋急轉(zhuǎn)彎的題目,例如6根火柴怎么拼出4個三角形。象這樣的靈機一動的問題是不能看出應(yīng)試者是否有“有頭腦/完成工作”的品質(zhì)。
面試與其說是科學(xué)不如說是藝術(shù)。但是只要你記住有頭腦/完成工作這個原則,你就可以應(yīng)對自如。有機會就問問你的同事他們喜歡的面試問題和答案。這是我們公司員工午飯時熱衷的話題之一。
本文最先用英文出版,題為 The Guerrilla Guide to Interviewing
作者簡介 Joel Spolsky 是紐約市一家小軟件公司,Fog Creek Software, 的創(chuàng)始人。他畢業(yè)于耶魯大學(xué),曾在美國微軟公司,Viacom, Juno 任軟件設(shè)計師及經(jīng)理。
[顯示打印版本]
相關(guān)文章
posted @
2008-11-04 21:29 poower 閱讀(225) |
評論 (0) |
編輯 收藏
想成為嵌入式程序員應(yīng)知道的0x10個基本問題
簡述:
這是嵌入式C程序員的基本知識。作者在Embedded Systems Programming雜志上發(fā)表了很多嵌入式系統(tǒng)開發(fā)方面的文章。
作者:Jones Nigel 更新日期:2005-04-08
來源:internet 瀏覽次數(shù):
C語言測試是招聘嵌入式系統(tǒng)程序員過程中必須而且有效的方法。這些年,我既參加也組織了許多這種測試,在這過程中我意識到這些測試能為面試者和被面試者提供許多有用信息,此外,撇開面試的壓力不談,這種測試也是相當(dāng)有趣的。
從被面試者的角度來講,你能了解許多關(guān)于出題者或監(jiān)考者的情況。這個測試只是出題者為顯示其對ANSI標(biāo)準(zhǔn)細(xì)節(jié)的知識而不是技術(shù)技巧而設(shè)計嗎?這是個愚蠢的問題嗎?如要你答出某個字符的ASCII值。這些問題著重考察你的系統(tǒng)調(diào)用和內(nèi)存分配策略方面的能力嗎?這標(biāo)志著出題者也許花時間在微機上而不是在嵌入式系統(tǒng)上。如果上述任何問題的答案是"是"的話,那么我知道我得認(rèn)真考慮我是否應(yīng)該去做這份工作。
從面試者的角度來講,一個測試也許能從多方面揭示應(yīng)試者的素質(zhì):最基本的,你能了解應(yīng)試者C語言的水平。不管怎么樣,看一下這人如何回答他不會的問題也是滿有趣。應(yīng)試者是以好的直覺做出明智的選擇,還是只是瞎蒙呢?當(dāng)應(yīng)試者在某個問題上卡住時是找借口呢,還是表現(xiàn)出對問題的真正的好奇心,把這看成學(xué)習(xí)的機會呢?我發(fā)現(xiàn)這些信息與他們的測試成績一樣有用。
有了這些想法,我決定出一些真正針對嵌入式系統(tǒng)的考題,希望這些令人頭痛的考題能給正在找工作的人一點幫助。這些問題都是我這些年實際碰到的。其中有些題很難,但它們應(yīng)該都能給你一點啟迪。
這個測試適于不同水平的應(yīng)試者,大多數(shù)初級水平的應(yīng)試者的成績會很差,經(jīng)驗豐富的程序員應(yīng)該有很好的成績。為了讓你能自己決定某些問題的偏好,每個問題沒有分配分?jǐn)?shù),如果選擇這些考題為你所用,請自行按你的意思分配分?jǐn)?shù)。
預(yù)處理器(Preprocessor)
1 . 用預(yù)處理指令#define 聲明一個常數(shù),用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1) #define 語法的基本知識(例如:不能以分號結(jié)束,括號的使用,等等)
2)懂得預(yù)處理器將為你計算常數(shù)表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3) 意識到這個表達式將使一個16位機的整型數(shù)溢出-因此要用到長整型符號L,告訴編譯器這個常數(shù)是的長整型數(shù)。
4) 如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。
2 . 寫一個"標(biāo)準(zhǔn)"宏MIN ,這個宏輸入兩個參數(shù)并返回較小的一個。
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
這個測試是為下面的目的而設(shè)的:
1) 標(biāo)識#define在宏中應(yīng)用的基本知識。這是很重要的。因為在
嵌入(inline)操作符 變?yōu)闃?biāo)準(zhǔn)C的一部分之前,宏是方便產(chǎn)生嵌入代碼的唯一方法,對于嵌入式系統(tǒng)來說,為了能達到要求的性能,嵌入代碼經(jīng)常是必須的方法。
2)三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產(chǎn)生比if-then-else更優(yōu)化的代碼,了解這個用法是很重要的。
3) 懂得在宏中小心地把參數(shù)用括號括起來
4) 我也用這個問題開始討論宏的副作用,例如:當(dāng)你寫下面的代碼時會發(fā)生什么事?
least = MIN(*p++, b);
3. 預(yù)處理器標(biāo)識#error的目的是什么?
如果你不知道答案,請看參考文獻1。這問題對區(qū)分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種問題的答案。當(dāng)然如果你不是在找一個書呆子,那么應(yīng)試者最好希望自己不要知道答案。
死循環(huán)(Infinite loops)
4. 嵌入式系統(tǒng)中經(jīng)常要用到無限循環(huán),你怎么樣用C編寫死循環(huán)呢?
這個問題用幾個解決方案。我首選的方案是:
while(1)
{
}
一些程序員更喜歡如下方案:
for(;;)
{
}
這個實現(xiàn)方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應(yīng)試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的基本原理。如果他們的基本答案是:"我被教著這樣做,但從沒有想到過為什么。"這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應(yīng)試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領(lǐng)域的BASIC/FORTRAN程序員。
數(shù)據(jù)聲明(Data declarations)
5. 用變量a給出下面的定義
a) 一個整型數(shù)(An integer)
b)一個指向整型數(shù)的指針( A pointer to an integer)
c)一個指向指針的的指針,它指向的指針是指向一個整型數(shù)( A pointer to a pointer to an intege)r
d)一個有10個整型數(shù)的數(shù)組( An array of 10 integers)
e) 一個有10個指針的數(shù)組,該指針是指向一個整型數(shù)的。(An array of 10 pointers to integers)
f) 一個指向有10個整型數(shù)數(shù)組的指針( A pointer to an array of 10 integers)
g) 一個指向函數(shù)的指針,該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數(shù)組,該指針指向一個函數(shù),該函數(shù)有一個整型參數(shù)并返回一個整型數(shù)( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們經(jīng)常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當(dāng)我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。但是當(dāng)我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應(yīng)試者如果不知道所有的答案(或至少大部分答案),那么也就沒有為這次面試做準(zhǔn)備,如果該面試者沒有為這次面試做準(zhǔn)備,那么他又能為什么出準(zhǔn)備呢?
Static
6. 關(guān)鍵字static的作用是什么?
這個簡單的問題很少有人能回答完全。在C語言中,關(guān)鍵字static有三個明顯的作用:
1)在函數(shù)體,一個被聲明為靜態(tài)的變量在這一函數(shù)被調(diào)用過程中維持其值不變。
2) 在模塊內(nèi)(但在函數(shù)體外),一個被聲明為靜態(tài)的變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問。它是一個本地的全局變量。
3) 在模塊內(nèi),一個被聲明為靜態(tài)的函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用。那就是,這個函數(shù)被限制在聲明它的模塊的本地范圍內(nèi)使用。
大多數(shù)應(yīng)試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應(yīng)試者的嚴(yán)重的缺點,因為他顯然不懂得本地化數(shù)據(jù)和代碼范圍的好處和重要性。
Const
7.關(guān)鍵字const有什么含意?
我只要一聽到被面試者說:"const意味著常數(shù)",我就知道我正在和一個業(yè)余者打交道。去年Dan Saks已經(jīng)在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應(yīng)該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著"只讀"就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細(xì)的答案,仔細(xì)讀一下Saks的文章吧。)
如果應(yīng)試者能正確回答這個問題,我將問他一個附加的問題:
下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
/******/
前兩個的作用是一樣,a是一個常整型數(shù)。第三個意味著a是一個指向常整型數(shù)的指針(也就是,整型數(shù)是不可修改的,但指針可以)。第四個意思a是一個指向整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數(shù)的常指針(也就是說,指針指向的整型數(shù)是不可修改的,同時指針也是不可修改的)。如果應(yīng)試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關(guān)鍵字 const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關(guān)鍵字const呢?我也如下的幾下理由:
1) 關(guān)鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數(shù)為常量是為了告訴了用戶這個參數(shù)的應(yīng)用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學(xué)會感謝這點多余的信息。(當(dāng)然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2) 通過給優(yōu)化器一些附加的信息,使用關(guān)鍵字const也許能產(chǎn)生更緊湊的代碼。
3) 合理地使用關(guān)鍵字const可以使編譯器很自然地保護那些不希望被改變的參數(shù),防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現(xiàn)。
Volatile
8. 關(guān)鍵字volatile有什么含意?并給出三個不同的例子。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設(shè)這個變量的值了。精確地說就是,優(yōu)化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1) 并行設(shè)備的硬件寄存器(如:狀態(tài)寄存器)
2) 一個中斷服務(wù)子程序中會訪問到的非自動變量(Non-automatic variables)
3) 多線程應(yīng)用中被幾個任務(wù)共享的變量
回答不出這個問題的人是不會被雇傭的。我認(rèn)為這是區(qū)分C程序員和嵌入式系統(tǒng)程序員的最基本的問題。搞嵌入式的家伙們經(jīng)常同硬件、中斷、RTOS等等打交道,所有這些都要求用到volatile變量。不懂得volatile的內(nèi)容將會帶來災(zāi)難。
假設(shè)被面試者正確地回答了這是問題(嗯,懷疑是否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1)一個參數(shù)既可以是const還可以是volatile嗎?解釋為什么。
2); 一個指針可以是volatile 嗎?解釋為什么。
3); 下面的函數(shù)有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1)是的。一個例子是只讀的狀態(tài)寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應(yīng)該試圖去修改它。
2); 是的。盡管這并不很常見。一個例子是當(dāng)一個中服務(wù)子程序修該一個指向一個buffer的指針時。
3) 這段代碼有點變態(tài)。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數(shù),編譯器將產(chǎn)生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結(jié)果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)
9. 嵌入式系統(tǒng)總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設(shè)置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。
對這個問題有三種基本的反應(yīng)
1)不知道如何下手。該被面者從沒做過任何嵌入式系統(tǒng)的工作。
2) 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到 Infineon為其較復(fù)雜的通信芯片寫的驅(qū)動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現(xiàn)bit fields的。從道德講:永遠(yuǎn)不要讓一個非嵌入式的家伙粘實際硬件的邊。
3) 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應(yīng)該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1 << 3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜歡為設(shè)置和清除值而定義一個掩碼同時定義一些說明常數(shù),這也是可以接受的。我希望看到幾個要點:說明常數(shù)、|=和&=~操作。
訪問固定的內(nèi)存位置(Accessing fixed memory locations)
10. 嵌入式系統(tǒng)經(jīng)常具有要求程序員去訪問某特定的內(nèi)存位置的特點。在某工程中,要求設(shè)置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務(wù)。
這一問題測試你是否知道為了訪問一絕對地址把一個整型數(shù)強制轉(zhuǎn)換(typecast)為一指針是合法的。這一問題的實現(xiàn)方式隨著個人風(fēng)格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
A more obscure approach is:
一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。
中斷(Interrupts)
11. 中斷是嵌入式系統(tǒng)中重要的組成部分,這導(dǎo)致了很多編譯開發(fā)商提供一種擴展—讓標(biāo)準(zhǔn)C支持中斷。具代表事實是,產(chǎn)生了一個新的關(guān)鍵字 __interrupt。下面的代碼就使用了__interrupt關(guān)鍵字去定義了一個中斷服務(wù)子程序(ISR),請評論一下這段代碼的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf("\nArea = %f", area);
return area;
}
這個函數(shù)有太多的錯誤了,以至讓人不知從何說起了:
1)ISR 不能返回一個值。如果你不懂這個,那么你不會被雇用的。
2) ISR 不能傳遞參數(shù)。如果你沒有看到這一點,你被雇用的機會等同第一項。
3) 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應(yīng)該是短而有效率的,在ISR中做浮點運算是不明智的。
4) 與第三點一脈相承,printf()經(jīng)常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。
代碼例子(Code examples)
12 . 下面的代碼輸出是什么,為什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
這個問題測試你是否懂得C語言中的整數(shù)自動轉(zhuǎn)換原則,我發(fā)現(xiàn)有些開發(fā)者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是 ">6"。原因是當(dāng)表達式中存在有符號類型和無符號類型時所有的操作數(shù)都自動轉(zhuǎn)換為無符號類型。因此-20變成了一個非常大的正整數(shù),所以該表達式計算出的結(jié)果大于6。這一點對于應(yīng)當(dāng)頻繁用到無符號數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。
13. 評價下面的代碼片斷:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應(yīng)編寫如下:
unsigned int compzero = ~0;
這一問題真正能揭露出應(yīng)試者是否懂得處理器字長的重要性。在我的經(jīng)驗里,好的嵌入式程序員非常準(zhǔn)確地明白硬件的細(xì)節(jié)和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。
到了這個階段,應(yīng)試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應(yīng)試者不是很好,那么這個測試就在這里結(jié)束了。但如果顯然應(yīng)試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優(yōu)秀的應(yīng)試者能做得不錯。提出這些問題,我希望更多看到應(yīng)試者應(yīng)付問題的方法,而不是答案。不管如何,你就當(dāng)是這個娛樂吧...
動態(tài)內(nèi)存分配(Dynamic memory allocation)
14. 盡管不像非嵌入式計算機那么常見,嵌入式系統(tǒng)還是有從堆(heap)中動態(tài)分配內(nèi)存的過程的。那么嵌入式系統(tǒng)中,動態(tài)分配內(nèi)存可能發(fā)生的問題是什么?
這里,我期望應(yīng)試者能提到內(nèi)存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經(jīng)在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠(yuǎn)遠(yuǎn)超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應(yīng)試者進入一種虛假的安全感覺后,我拿出這么一個小節(jié)目:
下面的代碼片段的輸出是什么,為什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
這是一個有趣的問題。最近在我的一個同事不經(jīng)意把0值傳給了函數(shù)malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是"Got a valid pointer"。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。
Typedef
15 Typedef 在C語言中頻繁用以聲明一個已經(jīng)存在的數(shù)據(jù)類型的同義字。也可以用預(yù)處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結(jié)構(gòu)s指針。哪種方法更好呢?(如果有的話)為什么?
這是一個非常微妙的問題,任何人答對這個問題(正當(dāng)?shù)脑颍┦菓?yīng)當(dāng)被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展為
struct s * p1, p2;
.
上面的代碼定義p1為一個指向結(jié)構(gòu)的指,p2為一個實際的結(jié)構(gòu),這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。
晦澀的語法
16 . C語言同意一些令人震驚的結(jié)構(gòu),下面的結(jié)構(gòu)是合法的嗎,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
這個問題將做為這個測驗的一個愉快的結(jié)尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據(jù)最處理原則,編譯器應(yīng)當(dāng)能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當(dāng)作問題。我發(fā)現(xiàn)這個問題的最大好處是這是一個關(guān)于代碼編寫風(fēng)格,代碼的可讀性,代碼的可修改性的好的話題。
好了,伙計們,你現(xiàn)在已經(jīng)做完所有的測試了。這就是我出的C語言測試題,我懷著愉快的心情寫完它,希望你以同樣的心情讀完它。如果是認(rèn)為這是一個好的測試,那么盡量都用到你的找工作的過程中去吧。天知道也許過個一兩年,我就不做現(xiàn)在的工作,也需要找一個。
作者介紹:
Nigel Jones 是一個顧問,現(xiàn)在住在Maryland,當(dāng)他不在水下時,你能在多個范圍的嵌入項目中找到他。 他很高興能收到讀者的來信,他的email地址是: NAJones@compuserve.com
參考文獻
1) Jones, Nigel, "In Praise of the #error directive," Embedded Systems Programming, September 1999, p. 114.
2) Jones, Nigel, " Efficient C Code for Eight-bit MCUs ," Embedded Systems Programming, November 1998, p. 66.
[顯示打印版本]
相關(guān)文章
-
leslie [2005-04-08]
挺考驗基本知識的
posted @
2008-11-04 21:27 poower 閱讀(188) |
評論 (0) |
編輯 收藏
#include "stdio.h"
#include "conio.h"
#include <dos.h>
#include<graphics.h>
/*****
Key struct:
position:x1,y1,x2,y2
State:isdown
white or black key:iswhite
nearby key:lwbk,rwbk
*******/
typedef struct _Key{
int x1;
int y1;
int x2;
int y2;
int isdown;
int iswhite;
int lwbk;
int rwbk;
} Key;
/*****
static grobal Variables statement:
7 white keys:wk[7]
5 black keys:bk[5]
width,height:kw,kh
frequant:frequ[8]
pcfrequant:pcfre
keynum
count
*******/
static wknum=14;
static Key wk[14];
static bknum=10;
static Key bk[10];
static int kw=32,kh=160;
static int leftwidth=2*32;
int frequ[8]={262, 294, 330, 349, 392, 440, 494, 524};
static long int pcfre=1190000;
static int keyindex;
static int count;
/*****
function statement:
makesound():
initkey();
change key state:setup(),setdown()
initdrawkey();
*******/
void makesound();
Key set(int x1,int w,int h);
void setup(Key *k);
void setdown(Key *k);
void keyinit();
void initdrawkey();
/*initkey*/
Key initset(int x1,int w,int h,int isw,int lwbk,int rwbk){
Key k;
k.x1=x1;
k.y1=0;
k.x2=x1+w;
k.y2=h;
k.isdown=0;
k.iswhite=isw;
k.lwbk=lwbk;
k.rwbk=rwbk;
return k;
}
/*keyinit()*/
void keyinit(){
wk[0]=initset(leftwidth+0*kw,kw,kh,1,-1,0);
wk[1]=initset(leftwidth+1*kw,kw,kh,1,0,1);
wk[2]=initset(leftwidth+2*kw,kw,kh,1,1,-1);
wk[3]=initset(leftwidth+3*kw,kw,kh,1,-1,2);
wk[4]=initset(leftwidth+4*kw,kw,kh,1,2,3);
wk[5]=initset(leftwidth+5*kw,kw,kh,1,3,4);
wk[6]=initset(leftwidth+6*kw,kw,kh,1,4,-1);
wk[7]=initset(leftwidth+7*kw,kw,kh,1,-1,6);
wk[8]=initset(leftwidth+8*kw,kw,kh,1,6,7);
wk[9]=initset(leftwidth+9*kw,kw,kh,1,7,-1);
wk[10]=initset(leftwidth+10*kw,kw,kh,1,-1,8);
wk[11]=initset(leftwidth+11*kw,kw,kh,1,8,9);
wk[12]=initset(leftwidth+12*kw,kw,kh,1,9,10);
wk[13]=initset(leftwidth+13*kw,kw,kh,1,10,-1);
bk[0]= initset(leftwidth+kw-kw/4,kw/2,kh/2,0,0,1);
bk[1]= initset(leftwidth+kw*2-kw/4,kw/2,kh/2,0,1,2);
bk[2]= initset(leftwidth+kw*4-kw/4,kw/2,kh/2,0,3,4);
bk[3]= initset(leftwidth+kw*5-kw/4,kw/2,kh/2,0,4,5);
bk[4]= initset(leftwidth+kw*6-kw/4,kw/2,kh/2,0,5,6);
bk[5]= initset(leftwidth+kw*8-kw/4,kw/2,kh/2,0,7,8);
bk[6]= initset(leftwidth+kw*9-kw/4,kw/2,kh/2,0,8,9);
bk[7]= initset(leftwidth+kw*11-kw/4,kw/2,kh/2,0,10,11);
bk[8]= initset(leftwidth+kw*12-kw/4,kw/2,kh/2,0,11,12);
bk[9]= initset(leftwidth+kw*13-kw/4,kw/2,kh/2,0,12,13);
}
/*draw black key*/
void drawblackkey(Key *k){
if(!(*k).isdown){
setfillstyle(SOLID_FILL,DARKGRAY);
bar((*k).x1,(*k).y1,(*k).x2,(*k).y2);
}
else{
setfillstyle(SOLID_FILL,LIGHTBLUE);
bar((*k).x1,(*k).y1,(*k).x2,(*k).y2);
}
}
/*draw white key*/
void drawwhitekey(Key *k){
if(!(*k).isdown){
setfillstyle(SOLID_FILL,WHITE);
bar((*k).x1,(*k).y1,(*k).x2,(*k).y2);
setcolor(DARKGRAY);
rectangle((*k).x1,(*k).y1,(*k).x2,(*k).y2);
/*draw nearby key*/
if(!(*k).lwbk>-1){
drawblackkey(&bk[(*k).lwbk]);
}
if(!(*k).rwbk>-1){
drawblackkey(&bk[(*k).rwbk]);
}
}
else{
setfillstyle(SOLID_FILL,LIGHTBLUE);
bar((*k).x1,(*k).y1,(*k).x2,(*k).y2);
setcolor(DARKGRAY);
rectangle((*k).x1,(*k).y1,(*k).x2,(*k).y2);
/*draw nearby key*/
if(!(*k).lwbk>-1){
drawblackkey(&bk[(*k).lwbk]);
}
if(!(*k).rwbk>-1){
drawblackkey(&bk[(*k).rwbk]);
}
}
}
/*setdown() and setup()*/
void setdown(Key *k){
(*k).isdown=1;
if((*k).iswhite)
drawwhitekey(k);
else
drawblackkey(k);
}
void setup(Key *k){
(*k).isdown=0;
if((*k).iswhite)
drawwhitekey(k);
else
drawblackkey(k);
}
/*initdrawkey()*/
void initdrawkey(){
int gdriver,gmode,i;
gdriver=DETECT;
initgraph(&gdriver,&gmode,"c:\\tc");
setbkcolor(YELLOW);
cleardevice();
for(i=0;i<wknum;i++){
setfillstyle(SOLID_FILL,WHITE);
bar(wk[i].x1,wk[i].y1,wk[i].x2,wk[i].y2);
setcolor(DARKGRAY);
rectangle(wk[i].x1,wk[i].y1,wk[i].x2,wk[i].y2);
}
for(i=0;i<bknum;i++){
setfillstyle(SOLID_FILL,DARKGRAY);
bar(bk[i].x1,bk[i].y1,bk[i].x2,bk[i].y2);
}
}
/*makesound()*/
void makesound(){
count=(int)(pcfre/frequ[keyindex]);
asm mov al, 10110110b
asm out 43h, al
asm mov ax,count
asm out 42h, al
asm mov al, ah
asm out 42h, al
asm in al, 61h
asm or al, 03h
asm out 61h, al
delay(0xfffff000);
asm in al, 61h
asm and al, 11111100b
asm out 61h, al
}
void main(){
keyinit();
initdrawkey();
settextstyle(4, 0, 10);
outtextxy(200, 230, "Interface Principle Course Design");
outtextxy(200, 250, "------------Piano----------------");
outtextxy(200, 270, "-------Input 1...7 Play----------");
outtextxy(200, 290, "-------Input CTRL+C Exit---------");
outtextxy(200, 310, "-------Auther:Ying Wenjie--------");
outtextxy(200, 340, "---------Date:2008-7-3-----------");
while(1){
keyindex=getch();
if(keyindex==3)
break;
keyindex=keyindex-0x30;
if(keyindex<1||keyindex>8)
continue;
keyindex--;
setdown(&wk[keyindex]);
makesound();
setup(&wk[keyindex]);
}
}
posted @
2008-07-02 16:35 poower 閱讀(221) |
評論 (0) |
編輯 收藏
author: scruffybear
release time: 28/10/2006
company: Watchdata
如有轉(zhuǎn)載,請注明出處,并保持文章的完整性,謝謝!
做java卡開發(fā)需要用到JNI,也就是本地化接口,說白了java程序要用到其它語言代碼,這樣就可以使用JNI來進行達到調(diào)用的目的,暫時理解到這里,還沒有時間細(xì)究。網(wǎng)上看到了一篇很好的介紹JNI的文章《JNI入門介紹上》、《JNI入門介紹下》,只研究了前一篇,進行了實踐,后一篇實踐覺得寫得不好,沒有花時間進行實踐。
1.簡介
JNI是Java Native Interface的縮寫,它的設(shè)計目的是:
The standard Java class library may not support the platform-dependent features needed by your application.
You may already have a library or application written in another programming language and you wish to make it accessible to Java applications.
You may want to implement a small portion of time-critical code in a lower-level programming language, such as assembly, and then have your Java application call these functions
2.JNI的書寫步驟
編寫帶有native聲明的方法的java類
使用javac命令編譯所編寫的java類
使用javah -jni java類名生成擴展名為h的頭文件
使用C/C++實現(xiàn)本地方法
將C/C++編寫的文件生成動態(tài)連接庫
ok
1) 編寫java程序:
這里以HelloWorld為例。
代碼1:
class HelloWorld {
public native void displayHelloWorld();
static {
System.loadLibrary("hello");
}
public static void main(String[] args) {
new HelloWorld().displayHelloWorld();
}
}
聲明native方法:如果你想將一個方法做為一個本地方法的話,那么你就必須聲明改方法為native的,并且不能實現(xiàn)。其中方法的參數(shù)和返回值在后面講述。
Load動態(tài)庫:System.loadLibrary("hello");加載動態(tài)庫(我們可以這樣理解:我們的方法displayHelloWorld()沒有實現(xiàn),但是我們在下面就直接使用了,所以必須在使用之前對它進行初始化)這里一般是以static塊進行加載的。同時需要注意的是System.loadLibrary();的參數(shù)“hello”是動態(tài)庫的名字。
main()方法
2) 編譯沒有什么好說的了
javac HelloWorld.java
這里是運行不了的,因為這時候目錄下面沒有hello.dll文件供調(diào)用,如果輸入java HelloWorld會出現(xiàn)以下錯誤:
C:\Documents and Settings\huilin.xiong\桌面>java HelloWorld
Exception in thread "main" java.lang.UnsatisfiedLinkError: no hello in java.library.path
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.loadLibrary0(Unknown Source)
at java.lang.System.loadLibrary(Unknown Source)
at HelloWorld.<clinit>(HelloWorld.java:5)
下面就用c語言生成hello.dll文件供java語言來調(diào)用。如下:
3) 生成擴展名為h的頭文件
javah -jni HelloWorld
在這里自動生成文件名為HelloWorld.h的頭文件。
頭文件的內(nèi)容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplu*
**tern "C" {
#endif
/*
* Class: HelloWorld
* Method: displayHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
(這里我們可以這樣理解:這個h文件相當(dāng)于我們在java里面的接口,這里聲明了一個Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);方法,然后在我們的本地方法里面實現(xiàn)這個方法,也就是說我們在編寫C/C++程序的時候所使用的方法名必須和這里的一致)。
4) 編寫本地方法
實現(xiàn)和由javah命令生成的頭文件里面聲明的方法名相同的方法。
代碼2:
#include <jni.h>
#include "HelloWorld.h"
#include <stdio.h>
JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env,jobject obj)
{
printf("Hello world!\n");
return;
}
注意代碼2中的第1行,需要將jni.h(該文件可以在%JAVA_HOME%/include文件夾下面找到)文件引入,因為在程序中的JNIEnv、jobject等類型都是在該頭文件中定義的;另外在第2行需要將HelloWorld.h頭文件引入(我是這么理解的:相當(dāng)于我們在編寫java程序的時候,實現(xiàn)一個接口的話需要聲明才可以,這里就是將HelloWorld.h頭文件里面聲明的方法加以實現(xiàn)。當(dāng)然不一定是這樣)。然后保存為HelloWorldImpl.c就ok了。
5) 生成動態(tài)庫
這里以在Windows中為例,需要生成dll文件。在保存HelloWorldImpl.c文件夾下面,使用VC的編譯器cl成。
cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll
注意:生成的dll文件名在選項-Fe后面配置,這里是hello,因為在HelloWorld.java文件中我們loadLibary的時候使用的名字是hello。當(dāng)然這里修改之后那里也需要修改。另外需要將-I%java_home%\include -I%java_home%\include\win32參數(shù)加上,因為在第四步里面編寫本地方法的時候引入了jni.h文件。
這里在實際的操作中找不到頭文件<stdio.h>,找不出原因,檢查了我的環(huán)境變量設(shè)置也沒有錯,沒辦法,只有從目錄D:\Software\Microsoft Visual Studio .NET\Vc7\include目錄下拷貝過來,另外又找不到stdarg.h,一并拷貝過來。頭文件都找不到后,又出現(xiàn)了找不到好幾個LIB文件,分別為libcmt.lib,oldnames.lib,kernel32.lib,檢查環(huán)境變量也沒錯,沒辦法,只能從D:\Software\Microsoft Visual Studio.NET\Vc7\lib拷貝過來。最后在目錄下生成了這樣幾個文件:HelloWorldImpl.obj,hello.dll,hello.exp,hello.lib,當(dāng)然,只有hello.dll是有用的,它是被HelloWorld.class所調(diào)用。
6) 運行程序
java HelloWorld,輸出了碩大的“Hello world!”。:-)
/************************************************************************
轉(zhuǎn)至:http://blog.csdn.net/scruffybear/archive/2007/12/01/1910418.aspx
posted @
2008-06-30 10:00 poower 閱讀(630) |
評論 (0) |
編輯 收藏
/*sound.h*/
/*http://blog.csdn.net/scs2000/archive/2006/09/14/1221079.aspx*/
#include <stdio.h>
#include <conio.h>
#define NOO 1450
int music1[8][6]={
{262,262,294,262,349},
{330,262,262,294,262},
{392,349,262,262,523},
{440,349,262,262,466},
{466,440,262,392,349}
};
int time1[8][6]={
{100,100,200,200,200},
{200,200,100,100,200},
{200,200,200,200,200},
{200,200,200,200,200},
{100,200,200,200,200}
};
int music2[3][10]={
{330,392,330,294,330,392,330,294,330,330},
{330,392,330,294,262,294,330,392,294,294},
{262,262,220,196,196,220,262,294,332,262}
};
int time2[3][11]={
{200,200,200,100,100,200,100,100,200,200},
{200,200,100,100,200,200,100,100,200,200},
{200,100,100,200,100,100,200,100,100,400}
};
int music3[4][11]={
{441,393,330,393,131,441,393,441,441,1450,1450},
{330,393,441,393,330,262,882,393,330,294,294},
{294,330,393,393,441,330,294,262,262,1450,1450},
{393,330,294,262,882,262,786,786,786,1450,1450}
};
int time3[4][11]={
{200,100,200,200,200,100,100,200,200,0,0},
{200,100,100,200,200,100,100,100,100,200,200},
{200,100,200,100,100,200,200,200,200,0,0},
{200,100,100,100,100,100,200,200,200,0,0}
};
const char * process_file[]={
" ",
"┌───┬┬┐┌┬┐┌┬┐┌┬┐┌┬┐┌┬┐┌┬┐┌┬┬───┐",
"│ ├接┤├口┤├技┤├術(shù)┤├課┤├程┤├設(shè)┤├計┤ │",
"├───┴┴┘└┴┘└┴┘└┴┘└┴┘└┴┘└┴┘└┴┴───┤",
"│ 《音樂發(fā)生器》 │",
"├──────────────────────────────┤",
"│ │",
"├──────────────────────────────┤",
"│ │",
"│ A.播放第一首音樂 │",
"│ B.播放第二首音樂 │",
"│ C.播放第三首音樂 │",
"│ Q.退出該程序 │",
"│ │",
"├──────────────────────────────┤",
"│ 制作:王康 │",
"│ 日期:年月日 │",
"│ │",
"└──────────────────────────────┘"};
const char * end_file[]={
" ",
"┌─────┐ ┌─┐┌─┐ ┌─┐┌─┐ ┌─────┐",
"│★★★★★├☆☆┤個││人├☆☆☆┤簡││介├☆☆┤★★★★★│",
"├─────┘ └─┘└─┘☆└─┘└─┘ └─────┤",
"├───────────────☆───────────────┤",
"│姓名:王康 │",
"├───────────────────────────────┤",
"│性別:男 │",
"├───────────────────────────────┤",
"│班級: │",
"├───────────────────────────────┤",
"│電話:--XXXXXXXX │",
"├───────────────────────────────┤",
"│QQ號: │",
"├───────────────────────────────┤",
"│郵箱:wkjs@163.com │",
"├───────────────────────────────┤",
"│ 謝謝您的使用 │",
"└───────────────────────────────┘"};
void light(int a)
{
asm MOV DX,283H
asm MOV AL,80H
asm OUT DX,AL
asm MOV DX,280H
asm MOV AL,0H;
asm OUT DX,AL
switch(a)
{
case 262:
case 131:
asm MOV AL,01H
asm OUT DX,AL
break;
case 294:
asm MOV AL,03H
asm OUT DX,AL
break;
case 330:
asm MOV AL,07H
asm OUT DX,AL
break;
case 350:
asm MOV AL,0FH
asm OUT DX,AL
break;
case 393:
case 786:
asm MOV AL,1FH
asm OUT DX,AL
break;
case 441:
case 882:
asm MOV AL,3FH
asm OUT DX,AL
break;
case 495:
asm MOV AL,7FH
asm OUT DX,AL
break;
};
}
void sound_A(int a,int b)
{
int i=0;
asm MOV DI,a /*選擇號計數(shù)器,先寫低字節(jié),后寫高字節(jié),選擇工作方式,二進制*/
asm MOV AL,10110110B
asm MOV DX,12H
asm MOV AX,34DEH
asm DIV DI
asm OUT 42H,AL /*先送低字節(jié)到號計數(shù)器*/
asm MOV AL,AH /*取高字節(jié)送AL*/
asm OUT 42H,AL /*后送高字節(jié)到號計數(shù)器*/
asm IN AL,61H /*讀入的PB口原輸出值*/
asm MOV AH,AL
asm OR AL,3 /*8255的PB0口PB1口輸出有效*/
asm OUT 61H,AL
/*調(diào)用燈亮函數(shù)*/
light(a);
/*時間延遲*/
for(i=0;i<b;i++)
{
delay(2000);
}
/*關(guān)閉揚聲器*/
asm MOV AL,AH
asm OUT 61H,AL
}
void sound(char a)
{
int i,j;
if(a=='a')
{
for(i=0;i<5;i++)
{
for(j=0;j<5;j++)
{
sound_A(music1[i][j],time1[i][j]);
}
}
}
else if(a=='b')
{
for(i=0;i<3;i++)
{
for(j=0;j<11;j++)
{
sound_A(music2[i][j],time2[i][j]);
}
}
}
else if(a=='c')
{
for(i=0;i<4;i++)
{
for(j=0;j<11;j++)
{
sound_A(music3[i][j],time3[i][j]);
}
}
}
else
{
printf("\t對不起,沒有該音樂!\n");
}
}
void face()
{
int i;
system("cls");
for(i=0;i<19;i++)
{
printf("\t%s\n",process_file[i]);
delay(20000);
}
printf("\t請輸入您的指令:");
}
void endface()
{
int i;
system("cls");
for(i=0;i<19;i++)
{
printf("\t%s\n",end_file[i]);
delay(20000);
}
printf("\t");
}
void operate()
{
char a;
face();
while(1)
{
a=getche();
switch(a)
{
case 'a':
case 'A':
sound('a');
printf("\n\t音樂播放結(jié)束,謝謝你的收聽,點任意鍵繼續(xù)!");
getch();
face();
break;
case 'b':
case 'B':
sound('b');
printf("\n\t音樂播放結(jié)束,謝謝你的收聽,點任意鍵繼續(xù)!");
getch();
face();
break;
case 'c':
case 'C':
sound('c');
printf("\n\t音樂播放結(jié)束,謝謝你的收聽,點任意鍵繼續(xù)!");
getch();
face();
break;
case 'q':
case 'Q':
endface();
exit(1);
break;
default:
printf("\t您的輸入有誤,請核實,謝謝!");
getch();
face();
break;
}
}
}
/*sound.c*/
void main()
{
operate();
}
posted @
2008-06-30 09:58 poower 閱讀(671) |
評論 (0) |
編輯 收藏
public class bigadd {
/**
* @param args
*/
//***********************兩個相等數(shù)相加**********************************88
public static String add(String s){
char c[]=s.toCharArray();
for(int i=0;i<c.length/2;i++){
char t=c[i];
c[i]=c[c.length-1-i];
c[c.length-1-i]=t;
}
int r[]=new int[c.length+1];
int carry=0;
for(int i=0;i<c.length;i++){
int t=Integer.parseInt(c[i]+"")+Integer.parseInt(c[i]+"")+carry;
carry=t/10;
r[i]=t%10;
}
r[c.length]=carry;
String sr;
if(r[c.length]==0)
sr="";
else
sr="1";
for(int j=r.length-2;j>=0;j--){
sr+=r[j];
}
return sr;
}
//*******************************兩個不同數(shù)相加*****************************************
public static String adddif(String s1,String s2){
char c1[] ;
char c2[] ;
if (s1.length() >= s2.length()) {
c1 = s1.toCharArray();
c2 = s2.toCharArray();
}
else{
c2 = s1.toCharArray();
c1 = s2.toCharArray();
}
for(int i=0;i<c1.length/2;i++){
char t=c1[i];
c1[i]=c1[c1.length-1-i];
c1[c1.length-1-i]=t;
}
for(int i=0;i<c2.length/2;i++){
char t=c2[i];
c2[i]=c2[c2.length-1-i];
c2[c2.length-1-i]=t;
}
int r[]=new int[c1.length+1];
int carry=0;
for(int i=0;i<c2.length;i++){
int t=Integer.parseInt(c1[i]+"")+Integer.parseInt(c2[i]+"")+carry;
carry=t/10;
r[i]=t%10;
}
for(int i=c2.length;i<c1.length;i++){
int t=Integer.parseInt(c1[i]+"")+carry;
carry=t/10;
r[i]=t%10;
}
r[c1.length]=carry;
String sr;
if(r[c1.length]==0)
sr="";
else
sr="1";
for(int j=r.length-2;j>=0;j--){
sr+=r[j];
}
return sr;
}
//*************************求2的N次冪*********************************8
public static String pow(int n){
if(n==0)
return "1";
else if(n==1)
return "2";
else
return add(pow(n-1));
}
public static String sumpow(int p1,int n,int q){
String sum="";
String pre=pow(p1);
for(int i=p1;i<=n;i=i+q){
sum=adddif(sum,pre);
for(int j=1;j<=2;j++)
pre=add(pre);
}
return sum;
}
//************************************相減***********************************
public static String minus(String s1,String s2){
char c1[] ;
char c2[] ;
if (s1.length() >= s2.length()) {
c1 = s1.toCharArray();
c2 = s2.toCharArray();
}
else{
c2 = s1.toCharArray();
c1 = s2.toCharArray();
}
for(int i=0;i<c1.length/2;i++){
char t=c1[i];
c1[i]=c1[c1.length-1-i];
c1[c1.length-1-i]=t;
}
for(int i=0;i<c2.length/2;i++){
char t=c2[i];
c2[i]=c2[c2.length-1-i];
c2[c2.length-1-i]=t;
}
int r[]=new int[c1.length];
int carry=0;
for(int i=0;i<c2.length;i++){
int t=Integer.parseInt(c1[i]+"")-Integer.parseInt(c2[i]+"")-carry;
if(t<0){
r[i]=Integer.parseInt(c1[i]+"")-Integer.parseInt(c2[i]+"")+10-carry;
carry=1;
}
else{
r[i]=t;
carry=0;
}
}
for(int i=c2.length;i<c1.length;i++){
int t=Integer.parseInt(c1[i]+"")-carry;
if(t<0){
carry=1;
r[i]=9;
}
else{
r[i]=t;
carry=0;
}
}
String str;
if(r[c1.length-1]==0){
str="";
}
else{
str=""+r[c1.length-1];
}
for(int j=r.length-2;j>=0;j--){
str+=r[j];
}
return str;
}
//**********************************減1****************************************
public static String minus1(String s){
char c[]=s.toCharArray();
int i;
for(i=0;i<c.length/2;i++){
char t=c[i];
c[i]=c[c.length-1-i];
c[c.length-1-i]=t;
}
int t=0;
for(i=0;i<c.length;i++){
t=Integer.parseInt(c[i]+"");
if(t>0){
t=t-1;
break;
}
}
c[i]=(t+"").charAt(0);
System.out.println(t+" "+i);
String str;
if(c[c.length-1]=='0')
str="";
else
str=c[c.length-1]+"";
for(int j=c.length-2;j>=i;j--)
str=str+c[j];
for(int j=i-1;j>=0;j--)
str+=9;
return str;
}
//*************************************加1************************************
public static String add1(String s){
char c[]=s.toCharArray();
int i;
for(i=0;i<c.length/2;i++){
char t=c[i];
c[i]=c[c.length-1-i];
c[c.length-1-i]=t;
}
int t=0;
for(i=0;i<c.length;i++){
t=Integer.parseInt(c[i]+"");
if(t<9){
t=t+1;
break;
}
}
String str;
if(i==c.length)
str="1";
else{
c[i]=(t+"").charAt(0);
str="";
}
for(int j=c.length-1;j>=i;j--)
str=str+c[j];
for(int j=i-1;j>=0;j--)
str+=0;
return str;
}
public static void main(String[] args) {
// TODO 自動生成方法存根
//System.out.println(pow(1000));
//System.out.println(pow(999));
//System.out.println(adddif(pow(999),pow(999)));
//System.out.println(adddif("19","999"));
System.out.println(sumpow((4-3),(1000-3),2));
//System.out.println(minus(pow(1000),pow(999)));
//System.out.println(add1(pow(1000)));
}
}
posted @
2008-06-13 13:36 poower 閱讀(441) |
評論 (0) |
編輯 收藏
中學(xué)就學(xué)過排列,組合 ,比如 C5,2 = 10; C6,2 = 15
如果用算法實現(xiàn)的話,難道也要先做一連串的乘法,然后再相除嗎? 比如: C5,2 = (5*4*3*2)/(3*2)
如果數(shù)很大的話,又是乘又做除的,多牛的計算機才能搞定呢?
先看看簡單的:
2個數(shù)選2個,共有1種方法
3個數(shù)選2個,共有3種方法
4個數(shù)選2個,共有6種方法
n個數(shù)選2個,共有多少種方法?
F(n,2) = F(n-1,2) + F(n-1,1) //這樣寫看的更清楚些.
那么N個數(shù)選M出來,有多少種選擇呢?
F(N,M) = F(N-1,M) + F(N-1,M-1)
到此處,DP的遞歸解空間已經(jīng)出來了.
F(N,M) = 1 M=0 or N=0 or N=M
F(N,M) = N M=1
F(N,M) = F(N-1,M) + F(N-1,M-1) M!=N 當(dāng)然N>M
剩下的工作就是程序?qū)崿F(xiàn)了.但是還有個小問題,就是在DP迭代的過程中是否需要記憶.在這個算法當(dāng)然需要記憶.
實現(xiàn)的過程中,可以做些小優(yōu)化,比如N=5 M=3 可以求C5,2的組合數(shù)就是要少遞歸幾次.
#include "stdafx.h"
#include <iostream>
using namespace std;
__int64 Aug[200][200] = {0};
__int64 getComposite(int m,int n)
{
__int64 preResult0;
__int64 preResult1;
if (m==0 || n== 0 || m== 1 || m == n)
return 1;
if (Aug[m][n] != 0)
return Aug[m][n];
else
{
preResult0 = getComposite(m-1,n);
Aug[m-1][n] = preResult0;
preResult1 = getComposite(m-1,n-1);
Aug[m-1][n-1] = preResult1;
}
return preResult0 + preResult1;
}
int main()
{
int count;
int m,n;
int m0,n0;
cin >> n >> m;
m0 = m >= n ? m : n;
n0 = m >= n ? n : m;
n0 = m0 - n0 >= n0 ? n0 : m0-n0;
cout << getComposite(m0,n0) << endl;
return 0;
}
//*********************************************************************************
注:其實我沒看明白......
posted @
2008-06-11 00:58 poower 閱讀(840) |
評論 (0) |
編輯 收藏