1. 程式人生 > >C專家程式設計上的一些習題

C專家程式設計上的一些習題

第一題: 關於連結串列問題;

問題:怎樣才能檢測到連結串列中是否存在迴圈?

對訪問過的每個元素做個標記,繼續遍歷這個連結串列,如果遇到某個已經做過標記的元素,說明連結串列中存在迴圈。

(如何做標記呢?思考之如果連結串列中的元素全部都是正整數的話,我們可以這樣做,對訪問過的每個元素加上個負號,然後我們繼續遍歷連結串列,如果訪問到負數了,就說明連結串列裡存在迴圈。如果既有正數又有負數呢?那麼我們可以先遍歷連結串列以此找出其中的最大值,然後重新遍歷連結串列,並改變遍歷過的元素值,使之大於最大值。然後繼續遍歷並和最大值進行比較。如果我們遍歷到一個值大於最大值了,那麼就證明連結串列中存在迴圈。如果連結串列中不是數字,是字母或是字串呢

? 對此我們可以同樣也可以做些標誌,如在我們訪問過的字串後面加上一個數字或是字元等.)

進一步限制: 這個連結串列位於只讀記憶體區域,無法在元素上做標記.

當訪問每個元素時,把它儲存在一個數組中。檢查每一個後繼元素,看看它是否已經存在陣列中。

(汗,竟然這樣卡人,我認為應該首先遍歷一遍連結串列看看連結串列中是否存在相同的元素值。若不存在我們可以使用上面的方法,若存在我們可以首先儲存住相同元素的小標。然後再進行判斷。)

再進一步限制: 噢,記憶體空間有限,無法建立一個足夠長度的陣列,然而,可以假定如果連結串列中存在迴圈,它出現在前n個元素之中.

設定一個指標指向連結串列的頭部,然後依次比較該值和後

n-1個元素,若存在相同的則說明有迴圈,否則,指標後移,比較它後面的n-i個元素。

(噢,這樣的方法確實節省記憶體空間,僅使用了一個指標的記憶體空間。不過可能需要多次遍歷連結串列。不過這也是沒辦法了)

繼續限制:噢!不!連結串列的長度是任意的,而且迴圈可能出現在任意的位置。

首先排除一種特殊情況,就是3個元素的連結串列中第2個元素的後面是第1個元素。設定兩個指標p1p2p1指向第一個元素,p2指向第三個元素,看看他們是否相等,如果相等就屬於上述這種情況,如果不等,把p1後移一個元素,p2向後移兩個元素。檢查兩個指標的值,如果相等,說明連結串列中存在迴圈,如果不等,繼續按照前述方法進行。如果出現兩個指標都為

NULL,說明連結串列中不存在迴圈。如果連結串列中存在迴圈,用這種方法肯定能夠檢測出來,因為其中一個指標一定能夠追上另一個。

(這種方法的關鍵點就在,p1後移一個元素,p2後移兩個元素,這樣我們讓p2p1,如果存在迴圈,p2肯定能夠追上p1.

2 系統呼叫和庫函式呼叫

通過這個問題,可以判斷候選人是否具有豐富的程式設計經驗以及是否具有找出這類問題答案的敏銳感覺。

簡明的回答是:函式庫呼叫是語言或應用程式的一部分,而系統呼叫是作業系統的一部分。你要確保弄懂“trap(自陷)”這個關鍵字的含義。系統呼叫是在作業系統核心發現一個“trap”或中斷後進行的。

※函式庫呼叫 VS 系統呼叫

函式庫呼叫系統呼叫

在所有的ANSI C編譯器版本中,C庫函式是相同的各個作業系統的系統呼叫是不同的

它呼叫函式庫中的一段程式(或函式)它呼叫系統核心的服務

與使用者程式相聯絡是作業系統的一個入口點

在使用者地址空間執行在核心地址空間執行

它的執行時間屬於“使用者時間”它的執行時間屬於“系統”時間

屬於過程呼叫,呼叫開銷較小需要在使用者空間和核心上下文環境間切換,開銷較大

C函式庫libc中有大約300個函式UNIX中大約有90個系統呼叫

典型的C函式庫呼叫:system fprintf malloc 典型的系統呼叫:chdir fork write brk

庫函式呼叫通常比行內展開的程式碼慢,因為它需要付出函式呼叫的開銷。但系統呼叫比庫函式呼叫還要慢很多,因為它需要把上下文環境切換到核心模式。

其他解答:

庫函式一般完成常見的特定功能,一般由某個組織製作釋出,並形成一定的標準,庫函數一般可以應用於不同的平臺而不需要做任何修改。例如,C 函式庫能夠被絕大多數 C 編譯器支援。系統呼叫函式一般與作業系統相關,不同的作業系統所使用的系統呼叫可能不太一樣。一般來說,如果兩個作業系統差異很大,系統呼叫函式的可移植性就不高。例如 Windows 用了系統呼叫的應用程式不能直接在 Linux 下編譯執行。系統呼叫函式很多情況下需要訪問系統特殊資源,使用系統呼叫時,該程式的狀態將從使用者態切換到核心態。圖 1-1 所示為 Linux
函式庫呼叫和系統呼叫示意圖。

1-1 庫函式呼叫和系統呼叫示意圖庫函式在實現中也有可能需要使用系統呼叫,但它封裝了系統呼叫部分的操作。使用者不必關心它使用了哪些系統呼叫。另外,進行上層應用程式開發時也沒有必要深入研究系統調用函式的具體實現過程。

3 編寫一些程式碼,確定一個變數是有符號數還是無符號數

顯然這個問題我們無法使用函式來進行實現,因為函式我們都是直接定義好了的變數,他們的型別都是已知了的。故我們選擇使用巨集來實現這個。

我們都明白,一個無符號數在任何時候都不會是負數的,但是它的補碼是負數。所以我們可以使用這樣的巨集來進行判斷:

#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)

如果是型別的話,我們可使用下面你的巨集:

#define ISUNSIGNED(type) ((type)0 - 1 > 0)

這個巨集的意思是,將0轉換為相應的型別,如果是無符號數,-1之後就會得到最大的那個數值,負責將會得到-1.從而我們就可以判斷所給的型別到底是無符號的還是有符號的了。