柯達面試題詳解

學識都 人氣:2.05W

一個朋友參加柯達的面試,被幾個問題鬱悶了,回來整理一下,鞏固一下基礎知識,順便分享給大家,下次可不能再讓面試官給難住了:)

柯達面試題詳解

系統調用與函數的區別

從程序完成的功能來看,函數庫提供的函數通常是不需要操作系統的服務,函數是在用戶空間內執行的,除非函數涉及到I/O操作等,一般是不會切到核心態的。系統調用是要求操作系統爲用戶提供進程,提供某種服務,通常是涉及系統的硬件資源和一些敏感的軟件資源等。函數庫的函數,尤其與輸入輸出相關的函數,大多必須通過Linux的系統調用來完成。因此我們可以將函數庫的函數當成應用程序設計人員與系統調用程序之間的一箇中間層,通過這個中間層,我們可以用一致的接口來安全的調用系統調用。這樣程序員可以只要寫一次代碼就能夠在不同版本的linux系統間使用積壓種具體實現完全不同的系統調用。至於如何實現對不同的系統調用的兼容性問題,那是函數庫開發者所關心的問題。

從程序執行效率來看,系統調用的執行效率大多要比函數高,尤其是處理輸入輸出的函數。當處理的數據量比較小時,函數庫的函數執行效率可能比較好,因爲函數庫的作法是將要處理的數據先存入緩衝區內,等到緩衝區裝滿了,再將數據一次寫入或者讀出。這種方式處理小量數據時效率比較高,但是在進行系統調用時,因爲用戶進程從用戶模式進入系統核心模式,中間涉及了許多額外的任務的切換工作,這些操作稱爲上下文切換,此類的額外工作會影響系統的執行效率。但是當要處理的數據量比較大時,例如當輸入輸出的數據量超過文件系統定義的盡寸時,利用系統調用可獲得較高的效率。

從程序的可移植性的角度來看,相對於系統調用,C語言的標準備函數庫(ANSI C) 具備較高的可移植性,在不同的系統環境下,只要做很少的修改,通常情況是不需要修改的。

可重入函數概念: 主要用於多任務環境中,一個可重入的函數簡單來說就是可以被中斷的函數,也就是說,可以在這個函數執行的任何時刻中斷它,轉入OS調度下去執行另外一段代碼,而返回控制時不會出現什麼錯誤;而不可重入的函數由於使用了一些系統資源,比如全局變量區,中斷向量表等,所以它如果被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。

也可以這樣理解,重入即表示重複進入,首先它意味着這個函數可以被中斷,其次意味着它除了使用自己棧上的變量以外不依賴於任何環境(包括static),這樣的函數就是purecode(純代碼)可重入,可以允許有該函數的多個副本在運行,由於它們使用的是分離的棧,所以不會互相干擾。如果確實需要訪問全局變量(static),一定要注意實施互斥手段。可重入函數在並行運行環境中非常重要,但是一般要爲訪問全局變量付出一些性能代價。 編寫可重入函數時,若使用全局變量,則應通過關中斷、信號量(即P、V操作)等手段對其加以保護。 說明:若對所使用的全局變量不加以保護,則此函數就不具有可重入性,即當多個進程調用此函數時,很有可能使有關全局變量變爲不可知狀態。 堆和棧的區別 一個由c/C++編譯的程序佔用的內存分爲以下幾個部分 1、棧區(stack) 由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。 2、堆區(heap) 一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似於鏈表,呵呵。 3、全局區(靜態區)(static),全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束後有系統釋放 4、文字常量區 常量字符串就是放在這裏的。 程序結束後由系統釋放 5、程序代碼區存放函數體的二進制代碼。 在什麼情況下析構函數必須是虛函數若類中有虛函數,這個類的析構函數應該是虛的析構函數,否則會有錯假設夫類爲a,子類爲b,當a* p = new b();要析構p指向的對象時,若a中的析構函數不是virtual則調用 p時,不會調用b的析構函數,這樣在b中分配的資源就無法釋放了。你可以在a和b的析構函數中輸出看一下,或者用調試工具跟蹤看一下 在全局變量和全局靜態變量有什麼區別變量可以分爲:全局變量、靜態全局變量、靜態局部變量和局部變量。

按存儲區域分,全局變量、靜態全局變量和靜態局部變量都存放在內存的靜態存儲區域,局部變量存放在內存的棧區。

按作用域分,全局變量在整個工程文件內都有效;靜態全局變量只在定義它的文件內有效;靜態局部變量只在定義它的函數內有效,只是程序僅分配一次內存,函數返回後,該變量不會消失;局部變量在定義它的函數內有效,但是函數返回後失效。

全局變量和靜態變量如果沒有手工初始化,則由編譯器初始化爲0。局部變量的值不可知。靜態全局變量,只本文件可以用。全局變量是沒有定義存儲類型的外部變量,其作用域是從定義點到程序結束.省略了存儲類型符,系統將默認爲是自動型.

靜態全局變量是定義存儲類型爲靜態型的外部變量,其作用域是從定義點到程序結束,所不同的是存儲類型決定了存儲地點,靜態型變量是存放在內存的數據區中的, 它們在程序開始運行前就分配了固定的字節,在程序運行過程中被分配的字節大小是不改變的.只有程序運行結束後,才釋放所佔用的內存.自動型變量存放在堆棧區中.堆棧區也是內存中一部分,該部分內存在程序運行中是重複使用的.聲明變量與定義變量有什麼區別聲明是向編譯器介紹名字--標識符。它告訴編譯器“這個函數或變量在某處可找到,它的模樣象什麼”。而定義是說:“在這裏建立變量”或“在這裏建立函數”。它爲名字分配存儲空間。無論定義的是函數還是變量,編譯器都要爲它們在定義點分配存儲空間。對於變量,編譯器確定變量的大小,然後在內存中開闢空間來保存其數據,對於函數,編譯器會生成代碼,這些代碼最終也要佔用一定的內存。在C和C++中,可以在不同的`地方聲明相同的變量和函數,但只能有一個定義(有時這稱爲ODR,單一定義規則)。。。

定義也可以是聲明,如果有int x;,之前編譯器未發現標識符x,編譯器則把這一標識符看成是定義並立即爲它分配存儲空間。 。。。。。 對“變量聲明”的解釋向來模糊且自相矛盾。。。 函數聲明包括函數類型、函數名、參數列表和一個分號,這些信息足以編譯器認出它是一個函數聲明並可識別出這個函數的外部特徵。由此推斷,變量聲明應是類型標識後面跟一個標識符。如int a;但這產生了一個矛盾,這段代碼有足夠的信息讓編譯器爲之分配存儲空間,而且編譯器也確實給之分配了存儲空間。要解決這個問題,對於C和C++需要一個關鍵字來說明“這是一個聲明,它的定義在別的地方”,這個關鍵字就是extern,它表示變量是在文件以外定義的,或在文件後面定義的。

在變量定義前加extern表示聲明一個變量但不定義它,如: extern int a; extern也可用於函數聲明,如: extern int func1(int length,int width); 但由於沒有函數體,編譯器必把它當成聲明而非定義,extern對於函數來說是多餘的、可選的。C語言的設計者並不要求函數聲明使用extern,這可能有些令人遺憾,如果函數聲明也要求用extern,那麼形式上與變量聲明更加一致了,從而減少了混亂(但這就需要更多的輸入,這也許能解釋爲什麼不要求函數聲明使用extern的原因)。。。 什麼是字節對齊,爲什麼要對齊?

現代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經常在特定的內存地址訪問,這就需要各種類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。

對齊的作用和原因:各個硬件平臺對存儲空間的處理上有很大的不同。一些平臺對某些特定類型的數據只能從某些特定地址開始存取。比如有些架構的CPU在訪問一個沒有進行對齊的變量的時候會發生錯誤,那麼在這種架構下編程必須保證字節對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對數據存放進行對齊,會在存取效率上帶來損失。比如有些平臺每次讀都是從偶地址開始,如果一個int型(假設爲32位系統)如果存放在偶地址開始的地方,那麼一個讀週期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果的高低字節進行拼湊才能得到該32bit數據。顯然在讀取效率上下降很多。