『壹』 進入IT企業必讀的200個 .NET面試題的目 錄
第1章 應聘開發職位的技巧和禁忌 1
程序員在准備面試的過程中,有時會過分注重技術上的准備工作,事實上,一些非技術的准備工作也相當重要。掌握好應聘技術職位過程中的軟技巧、准備一份出色的簡歷、提高警惕避免在應聘中觸犯一些禁忌,可以大大地提高面試的成功率。在開始面試時,還要把握一些強勢的招聘網站,給自己一個展現自我的平台。本章從了解、准備、開始,層層漸進,讓讀者對整個程序員面試的過程有個全局性的掌握。
1.1 技術職位需要怎樣的人才——了解大環境 1
1.1.1 對技術的執著和熱情 1
1.1.2 對編程始終抱有認真的態度 1
1.1.3 實事求是的態度和謙遜的品質 1
1.1.4 適合應聘公司的文化 1
1.2 一份出色的個人簡歷——面試准備 2
1.2.1 一份簡歷不宜超過一頁 2
1.2.2 永遠准備中文簡歷 2
1.2.3 不要在個人簡歷上註明希望薪水 2
1.2.4 簡歷模板 2
1.3 展現自我——開始發簡歷 3
1.3.1 應聘渠道 3
1.3.2 應聘流程 7
1.4 真正的面試——開始面試 8
1.4.1 筆試 8
1.4.2 面試 10
1.4.3 電話面試 10
1.4.4 網路考試 10
1.5 面試中的一些禁忌 11
1.6 小結 12
第2章 .NET框架基礎 13
本章覆蓋了.NET面試筆試中常見的.NET框架技術題。此類題目側重於考查應聘者對於.NET機制的深入了解,徹底理解.NET的運行機制,並且熟悉一個.NET系統常用的管理部署方法。
2.1 .NET基礎概念 13
2.1.1 什麼是CTS、CLS和CLR 13
2.1.2 開發和運行.NET程序需要的
最基本環境是什麼 15
2.1.3 .NET是否支持多編程語言開發 15
2.1.4 CLR技術和COM技術的比較 17
2.1.5 什麼是程序集和應用程序域 18
2.2 .NET運行機制 20
2.2.1 .NET程序被編譯成什麼形式的代碼 20
2.2.2 JIT是如何工作的 22
2.2.3 簡述程序集的載入機制 23
2.2.4 如何配置程序集的版本策略 25
2.3 生成、部署和管理 27
2.3.1 如何生成強簽名的程序集 27
2.3.2 如何把程序集放入GAC中 29
2.3.3 延遲簽名及其作用 30
2.3.4 程序集的版本分哪幾部分 32
2.4 名企面試真題 32
2.5 小結 33
第3章 .NET類型語法基礎 34
本章覆蓋了.NET面試筆試中最基礎的語法和類型題。縱觀.NET的面試題,此類題目涉及了最基礎的知識點,其難度也相對最小。但是應聘者如果對此類的面試題回答得不正確或者不完整,將會給面試官留下技術水平較差的印象,建議讀者對本章的題目做到深刻理解和掌握。
3.1 基礎類型和語法 34
3.1.1 .NET中所有內建類型的基類是什麼 34
3.1.2 System.Object中包含哪些方法,哪些是虛方法 35
3.1.3 值類型和引用類型的區別 37
3.1.4 簡述裝箱和拆箱原理 40
3.1.5 C#中是否有全局變數 43
3.1.6 struct和class的區別,
struct適用哪些場合 43
3.1.7 類型的初始化器何時被調用 44
3.1.8 C#中方法的參數可以有哪幾種傳遞方式 47
3.1.9 C#中string和String有什麼區別 50
3.1.10 .NET支持哪幾種可訪問性級別,C#實現了其中的哪幾種 50
3.1.11 簡述屬性的特點及屬性和方法的異同 51
3.1.12 簡述C#中的淺復制和深復制 54
3.1.13 簡述C#中的循環語法和各自的特點 57
3.1.14 C#中的using語句有什麼作用 60
3.2 內存管理和垃圾回收 62
3.2.1 簡述.NET中堆棧和堆的特點和差異 62
3.2.2 執行string abc=aaa+bbb+ccc共分配了多少內存 64
3.2.3 .NET中GC的運行機制 66
3.2.4 Dispose方法和Finalize方法在何時被調用 67
3.2.5 GC中代(Generation)是什麼,一共分幾代 70
3.2.6 GC機制中如何判斷一個對象是否仍在被使用 71
3.2.7 .NET的託管堆中是否可能出現內存泄漏現象 72
3.3 面向對象的實現 75
3.3.1 C#中類可以有多個父類、可以實現多個介面嗎 75
3.3.2 簡述C#中重寫、重載和隱藏的概念 76
3.3.3 為什麼在構造方法中調用虛方法會導致問題 78
3.3.4 在C#中如何聲明一個類不能被繼承 82
3.4 異常的處理 82
3.4.1 如何針對不同的異常進行捕捉 82
3.4.2 如何使用Conditional特性 84
3.4.3 如何避免類型轉換時的異常 86
3.5 名企面試真題 88
3.6 小結 89
第4章 字元串、集合和流的使用 90
字元串、集合和流在程序中處理數據時經常被用到,這些代碼的編寫將直接影響到系統的正確性和效率。本章將包含關於字元串、集合和流的常見面試題,並且通過分析這些題目和知識點,幫助讀者梳理這些方面的知識。
4.1 字元串處理 90
4.1.1 System.String是值類型還是引用類型 90
4.1.2 StringBuilder類型有何作用 91
4.1.3 如何在String和Byte[]對象之間進行轉換 92
4.1.4 簡述BASE64編碼的作用以及C#中對其的支持 94
4.1.5 SecureString的實例如何被分配和釋放 96
4.1.6 什麼是字元串池機制 98
4.2 常用集合和泛型 99
4.2.1 Int[]是引用類型還是值類型 99
4.2.2 數組之間如何進行轉換 100
4.2.3 解釋泛型的基本原理 102
4.2.4 什麼是泛型的主要約束和次要約束 104
4.2.5 .NET中是否可用標准模板庫(STL) 105
4.3 流和序列化 106
4.3.1 什麼是流,.NET中有哪些常見的流 106
4.3.2 如何使用壓縮流 109
4.3.3 Serializable特性有何作用 111
4.3.4 .NET提供了哪幾種可進行序列化操作的類型 113
4.3.5 如何自定義序列化和反序
列化的過程 116
4.4 名企面試真題 119
4.5 小結 119
第5章 常用類和介面 120
.NET除了提供運行引擎之外,還提供了豐富的內建類型。理解這些類型的作用和機制,能夠幫助程序員減少代碼工作,編寫高效簡潔的代碼。而有時候誤用類型,則會導致性能的降低,更嚴重時則會為系統帶了潛伏的bug。本章將介紹一些經常出現在.NET面試中的類型和介面。
5.1 類型的基類System.Object 120
5.1.1 是否存在不繼承自System.Object類型的類 120
5.1.2 在System.Object中定義的三個比較方法有何異同 122
5.1.3 如何重寫GetHashCode方法 125
5.2 時間的操作System.DateTime 127
5.2.1 DateTime如何存儲時間 127
5.2.2 如何在DateTime對象和
字元串對象之間進行轉換 127
5.2.3 什麼是UTC時間,如何轉換到UTC時間 130
5.3 IFormattable和IformatProvider的使用 131
5.3.1 如何使用IFormattable介面實現格式化輸出 131
5.3.2 如何告訴類型格式化輸出的方式 133
5.4 管理文件和文件夾的類型 135
5.4.1 如何操作文件和文件夾 135
5.4.2 如何實現文件和文件夾的監控功能 139
5.5 .NET中的定時器 141
5.5.1 .NET提供了哪幾個定時器類型 141
5.5.2 .NET的內建定時器類型
是否會發生回調方法重入 146
5.6 名企面試真題 151
5.7 小結 151
第6章 .NET中的高級特性 152
本章的內容覆蓋了諸如委託、事件、反射和特性等.NET框架中的高級特性。對這些特性的掌握和成熟運用,往往成為.NET程序員從入門級進階到中級的判斷標准。也正因為如此,此類題目在.NET技術筆試、面試中被大量採用。讀者在閱讀本章時,應力求做到知其然更知其所以然,充分理解各種特性在.NET框架下是如何實現的,這樣的設計如何提高了程序的靈活性和可擴展性。
6.1 委託 152
6.1.1 請解釋委託的基本原理 152
6.1.2 委託回調靜態方法和實例方法有何區別 154
6.1.3 什麼是鏈式委託 154
6.1.4 鏈式委託的執行順序是怎麼樣的 156
6.1.5 可否定義擁有返回值的方法的委託鏈 157
6.1.6 委託通常可以應用在哪些場合 159
6.2 事件 165
6.2.1 請解釋事件的基本使用方法 165
6.2.2 事件和委託有何聯系 167
6.2.3 如何設計一個帶有很多事件的類型 169
6.2.4 用代碼表示如下情景:貓叫、老鼠逃跑、主人驚醒 173
6.3 反射 175
6.3.1 請解釋反射的基本原理和其實現的基石 176
6.3.2 .NET提供了哪些類型來實現反射 179
6.3.3 如何實現動態地發射程序集 184
6.3.4 如何利用反射來實現工廠模式 188
6.3.5 如何以較小的內存代價保存
Type、Field和Method信息 194
6.4 特性 196
6.4.1 什麼是特性,如何自定義一個特性 196
6.4.2 .NET中特性可以在哪些元素上使用 198
6.4.3 有哪幾種方法可以獲知一個元素是否申明某個特性 200
6.4.4 一個元素是否可以重復申明同一個特性 202
6.5 名企面試真題 204
6.6 小結 204
第7章 .NET多線程編程 205
多線程編程是每個技術框架下都需要面對的問題,在多CPU、多核的硬體架構逐漸普及的今天,多線程編程也漸漸變得更加重要。本章將集中覆蓋關於.NET中多線程編程的面試題。
7.1 多線程編程的基本概念 205
7.1.1 請解釋操作系統層面上的線程和進程 205
7.1.2 多線程程序在操作系統里是並行執行的嗎 206
7.1.3 什麼是纖程 207
7.2 .NET中的多線程編程 208
7.2.1 如何在.NET程序中手動控制多個線程 208
7.2.2 如何使用.NET的線程池 212
7.2.3 如何查看和設置線程池的上下限 215
7.2.4 如何定義線程獨享的全局數據 217
7.2.5 如何使用非同步模式讀取一個文件 221
7.2.6 如何阻止線程執行上下文的傳遞 223
7.3 多線程程序的線程同步 227
7.3.1 什麼是同步塊和同步塊索引 227
7.3.2 C#中的lock關鍵字有何作用 229
7.3.3 可否使用值類型對象來
實現線程同步 232
7.3.4 可否對引用類型對象自身進行同步 233
7.3.5 什麼是互斥體,Mutex類型和Monitor類型的功能有何區別 235
7.4 名企面試真題 238
7.5 小結 238
第8章 ASP NET應用開發 239
ASP NET是微軟公司提供的編寫動態網站的技術框架,其特點是基於.NET框架基礎,所有ASP NET程序都可以使用針對.NET的語言編寫。在微軟公司的Visual Studio開發平台中,實現了拖放控制項等便捷的功能,使得ASP NET應用程序的開發效率得到了較大的提高,近些年來ASP NET技術逐漸成為網站開發的主流技術之一,本章將覆蓋一些常見的關於ASP NET開發的面試題。
8.1 ASP NET應用開發基礎 239
8.1.1 請解釋ASP NET以什麼形式運行 239
8.1.2 常見的HTTP Code有哪些 242
8.1.3 GET請求和POST請求有何區別 245
8.1.4 介紹ASP NET的頁面生存周期 247
8.2 控制項和頁面 249
8.2.1 什麼是靜態頁面,什麼是動態頁面 250
8.2.2 請簡述ViewState的功能和實現機制 251
8.2.3 Session有哪幾種存儲方式,之間有何區別,如何進行設置 255
8.2.4 如何嵌套使用GridView控制項 259
8.2.5 列舉幾種實現頁面跳轉的方法,並說明其實現機制 263
8.2.6 請解釋<%# Eval(source)%>的功能和實現機制 270
8.2.7 ObjectDataSource控制項有何作用 273
8.3 驗證和安全 277
8.3.1 如何使用正則表達式來驗證一個
上海市電話號碼 277
8.3.2 介紹ASP NET驗證控制項的功能和
使用方法 280
8.3.3 如何防止SQL注入式攻擊 287
8.4 名企面試真題 289
8.5 小結 289
第9章 .NET中的資料庫開發 290
大部分系統都會包含資料庫應用。資料庫應用設計往往成為系統設計中最重要的組成之一,這其中不止包括資料庫的架構、庫結構的設計,也包括了程序訪問資料庫策略的設計。在.NET的程序開發中,ADO NET已經成為訪問資料庫最主要的組件框架。本章將覆蓋和資料庫訪問及ADO NET有關的常見面試題,具體會覆蓋ADO NET基本概念、資料庫的鏈接、資料庫讀寫等主題。
9.1 ADO NET和資料庫程序基礎 290
9.1.1 什麼是關系型資料庫 290
9.1.2 如何通過SQL語句來實現行列轉換 291
9.1.3 ADO NET支持哪幾種數據源 293
9.2 ADO NET和資料庫的連接 295
9.2.1 請簡要敘述資料庫連接池的機制 295
9.2.2 如何提高連接池內連接的重用率 298
9.2.3 一個連接字元串可以包含哪些屬性 300
9.2.4 CommandBehavior.CloseConnection有何作用 302
9.3 使用ADO NET讀寫資料庫 305
9.3.1 ADO NET支持哪兩種方式來訪問關系資料庫 305
9.3.2 什麼是強類型的DataSet 309
9.3.3 請解釋SqlDataAdapter的
基本工作機制 312
9.3.4 如何自動生成SqlDataAdapter的
更新命令 316
9.3.5 如何實現批量更新的功能 319
9.4 名企面試真題 321
9.5 小結 321
第10章 XML的應用和處理 322
XML可算是近10年來最炙手可熱的技術之一,由於其跨平台的特性,很多技術應用都選擇基於XML來進行發展。在.NET中,對XML的支持和應用隨處可見。例如配置文件的格式、數據結構的表示、Web Service應用等,都是以XML語法為基礎的。本章將詳細覆蓋常見的關於XML本身及其在.NET中應用的面試題。
10.1 XML的基本特性 322
10.1.1 什麼是XML 322
10.1.2 簡述XML的常用領域及其優勢 323
10.1.3 XML中<![CDATA[ ]]>標簽的作用 324
10.1.4 XML規范是否允許空的屬性值 325
10.1.5 XML中如何處理諸如「<」的字元 326
10.1.6 XML中的命名空間如何使用 328
10.2 使用.NET組件讀寫XML 330
10.2.1 .NET中操作XML的基本類型有哪些 330
10.2.2 如何使用XmlDocument類型操作XML文檔的節點和屬性 334
10.2.3 如何使用XPath來指向帶有屬性的節點 337
10.2.4 .NET中如何驗證一個XML文檔的格式 338
10.2.5 .NET中XML文檔和關系模式如何轉換 340
10.3 利用XSLT處理XML文檔 344
10.3.1 什麼是XSLT,XSLT有何作用 344
10.3.2 如何使用XSLT中的模板 346
10.3.3 如何在XSLT文檔中調用
其他XSLT文檔 349
10.3.4 如何在代碼中使用XSLT文檔 351
10.4 名企面試真題 353
10.5 小結 353
第11章 Web Service的開發與應用 354
Web Service是一種網路服務,形式非常類似於當前智能手機上的應用。通過通用的規范,Web Service技術允許使用者訪問網路上每一個Web Service所提供的服務。在網路快速發展的今天,這種基於網路的分布式服務已經被廣泛地應用。本章將討論關於.NET中如何應用Web Service的面試題。
11.1 SOAP和Web Service的基礎概念 354
11.1.1 請簡述SOAP協議 354
11.1.2 什麼是WSDL,它有何作用 356
11.1.3 Web Service中如何處理附件 357
11.2 使用.NET開發Web Service 360
11.2.1 如何在.NET中創建Web Service 360
11.2.2 WebMethod特性包含哪些屬性,各有何用處 363
11.2.3 如何生成Web Service代理類型 367
11.2.4 請簡述.NET中Web Service的異常機制 368
11.3 Web Service的安全機制 371
11.3.1 請簡要介紹WS-Security的簽名機制 371
11.3.2 WS-Security規范申明了哪幾種身份驗證的方法 373
11.4 名企面試真題 375
11.5 小結 375
第12章 .NET Remoting分布式應用開發 376
在企業級應用開發中,分布式開發占據了越來越重要的地位。.NET Remoting是一種可擴展性很高的分布式開發技術,相對於DCOM、CORBA、RMI等分布式開發技術而言,.NET Remoting擁有著眾多獨特的優勢。.NET Remoting是一個龐大的技術話題,如果詳細展開的話可能要佔據一本書的篇幅。本章主要針對那些經常出現在.NET面試中的、與Remoting基礎相關的面試題。
12.1 .NET Remoting框架基礎 376
12.1.1 請簡要介紹.NET Remoting的運行機制 376
12.1.2 請列舉.NET Remoting機制中有哪些組件可以擴展替換 379
12.1.3 請簡述.NET Remoting生存周期機制 384
12.2 使用.NET Remoting進行分布式應用開發 387
12.2.1 請介紹服務端激活模式和客戶端激活模式的區別 387
12.2.2 請簡述Remoting中有哪幾種遠程調用方式 390
12.2.3 Remoting機制中如何處理以ObjRef為參數的方法調用 393
12.2.4 請簡述Remoting中配置文件的使用 397
12.2.5 如何在客戶端和伺服器端共享遠程對象類型 400
12.3 名企面試真題 404
12.4 小結 404
第13章 代碼和演算法 405
無論是面試還是筆試,演算法和代碼的問題都是必不可少的,其區別僅在於筆試中更側重於應聘者書寫代碼的能力,而面試中則更注重於應聘者的設計能力和演算法思路。本章著重覆蓋了一些在.NET面試中經常出現的和代碼、演算法有關的面試題,並且給出了解答思路和實現示例。
13.1 基礎演算法題 405
13.1.1 請實現一個快速排序演算法 405
13.1.2 請實現一個二分查找演算法 406
13.1.3 請實現一棵二叉樹的中序、後序遍歷 408
13.1.4 請寫出一個奇偶分割演算法 413
13.1.5 請實現一個簡單的最短路徑演算法 414
13.2 程序設計題 423
13.2.1 請編程實現斐波拉契數列問題 423
13.2.2 請設計窗口程序演示八皇後問題 425
13.3 名企面試真題 432
13.4 小結 432
第14章 .NET中的單元測試 433
單元測試是軟體開發中必不可少的一個環節,單元測試的優劣直接影響到集成測試、系統測試的效果,甚至會影響到最終產品的質量。大多數開發團隊對單元測試非常重視,並且要求程序員掌握相應的知識。本章將覆蓋在.NET面試中經常出現的關於單元測試的面試題。
14.1 單元測試基礎概念 433
14.1.1 請簡述單元測試的作用和其優點 433
14.1.2 請舉例說明TDD開發方式的流程 434
14.1.3 請編寫實現階乘功能模塊的測試用例 437
14.2 使用NUNIT進行單元測試 439
14.2.1 如何使用NUNIT來進行單元測試 439
14.2.2 如何對NUNIT的測試用例進行分類 442
14.2.3 請解釋SetUp、TearDown、TestFixtureSetUp和
TestFixtureTearDown 446
14.3 名企面試真題 448
14.4 小結 448
『貳』 java基礎面試題有哪些
下面是10道java基礎面試題,後附答案
1.什麼是 Java 虛擬機?為什麼 Java 被稱作是「平台無關的編程語言」?
Java 虛擬機是一個可以執行 Java 位元組碼的虛擬機進程。Java 源文件被編譯成能被 Java 虛擬機執行的位元組碼文件。
Java 被設計成允許應用程序可以運行在任意的平台,而不需要程序員為每一個平台單獨重寫或者是重新編譯。Java 虛擬機讓這個變為可能,因為它知道底層硬體平台的指令長度和其他特性。
2.「static」關鍵字是什麼意思?Java 中是否可以覆蓋(override)一個 private 或者是static 的方法?
「static」關鍵字表明一個成員變數或者是成員方法可以在沒有所屬的類的實例變數的情況下被訪問。
Java 中 static 方法不能被覆蓋,因為方法覆蓋是基於運行時動態綁定的,而 static 方法是編譯時靜態綁定的。static 方法跟類的任何實例都不相關,所以概念上不適用。
3.JDK 和 JRE 的區別是什麼?
Java 運行時環境(JRE)是將要執行 Java 程序的 Java 虛擬機。它同時也包含了執行 applet 需要的瀏覽器插件。Java 開發工具包 (JDK)是完整的 Java 軟體開發包,包含了 JRE,編譯器和其他的工具(比如:JavaDoc,Java 調試器),可以讓開發者開發、編譯、執行 Java 應用程序。
4.是否可以在 static 環境中訪問非 static 變數?
static 變數在 Java 中是屬於類的,它在所有的實例中的值是一樣的。當類被 Java 虛擬機載入的時候,會對 static 變數進行初始化。如果你的代碼嘗試不用實例來訪問非 static 的變數,編譯器會報錯,因為這些變數還沒有被創建出來,還沒有跟任何實例關聯上。
5.Java 支持的數據類型有哪些?什麼是自動拆裝箱?
Java 語言支持的 8 中基本數據類型是:
byte
short
int
long
float
double
boolean
char
自動裝箱是 Java 編譯器在基本數據類型和對應的對象包裝類型之間做的一個轉化。比如:把 int 轉化成 Integer,double 轉化成 double,等等。反之就是自動拆箱。
6.Java 支持多繼承么?
不支持,Java 不支持多繼承。每個類都只能繼承一個類,但是可以實現多個介面。
7.Java 中,什麼是構造函數?什麼是構造函數重載?什麼是復制構造函數?
當新對象被創建的時候,構造函數會被調用。每一個類都有構造函數。在程序員沒有給類提供構造函數的情況下,Java 編譯器會為這個類創建一個默認的構造函數。
Java 中構造函數重載和方法重載很相似。可以為一個類創建多個構造函數。每一個構造函數必須有它自己唯一的參數列表。
Java 不支持像 C++中那樣的復制構造函數,這個不同點是因為如果你不自己寫構造函數的情況下,Java 不會創建默認的復制構造函數。
8.Java 中的方法覆蓋(Overriding)和方法重載(Overloading)是什麼意思?
Java 中的方法重載發生在同一個類裡面兩個或者是多個方法的方法名相同但是參數不同的情況。與此相對,方法覆蓋是說子類重新定義了父類的方法。方法覆蓋必須有相同的方法名,參數列表和返回類型。覆蓋者可能不會限制它所覆蓋的方法的訪問。
9.介面和抽象類的區別是什麼?
Java 提供和支持創建抽象類和介面。它們的實現有共同點,不同點在於:
介面中所有的方法隱含的都是抽象的。而抽象類則可以同時包含抽象和非抽象的方法。
類可以實現很多個介面,但是只能繼承一個抽象類
類如果要實現一個介面,它必須要實現介面聲明的所有方法。但是,類可以不實現抽象類聲明的所有方法,當然,在這種情況下,類也必須得聲明成是抽象的。
抽象類可以在不提供介面方法實現的情況下實現介面。
Java 介面中聲明的變數默認都是 final 的。抽象類可以包含非 final 的變數。
Java 介面中的成員函數默認是 public 的。抽象類的成員函數可以是 private, protected 或者是 public。
介面是絕對抽象的,不可以被實例化。抽象類也不可以被實例化,但是,如果它包含 main 方法的話是可以被調用的。
10.什麼是值傳遞和引用傳遞?
對象被值傳遞,意味著傳遞了對象的一個副本。因此,就算是改變了對象副本,也不會影響源對象的值。
對象被引用傳遞,意味著傳遞的並不是實際的對象,而是對象的引用。因此,外部對引用對象所做的改變會反映到所有的對象上。
最後祝你面試順利!
『叄』 c++經典面試題及答案
1. C++的類和C裡面的struct有什麼區別?
struct成員默認訪問許可權為public,而class成員默認訪問許可權為private
2. 析構函數和虛函數的用法和作用
析構函數是在對象生存期結束時自動調用的函數,用來釋放在構造函數分配的內存。
虛函數是指被關鍵字virtual說明的函數,作用是使用C++語言的多態特性
3. 全局變數和局部變數有什麼區別?是怎麼實現的?操作系統和編譯器是怎麼知道的?
1) 全局變數的作用用這個程序塊,而局部變數作用於當前函數
2) 前者在內存中分配在全局數據區,後者分配在棧區
3) 生命周期不同:全局變數隨主程序創建和創建,隨主程序銷毀而銷毀,局部變數在局部函數內部,甚至局部循環體等內部存在,退出就不存在
4) 使用方式不同:通過聲明後全局變數程序的各個部分都可以用到,局部變數只能在局部使用
4. 有N個大小不等的自然數(1–N),請將它們由小到大排序.要求程序演算法:時間復雜度為O(n),空間復雜度為O(1)。
void sort(int e[], int n)
{
int i;
int t;
for (i=1; i {
t = e[e[i]];
e[e[i]] = e[i];
e[i] = t;
}
}
5. 堆與棧的去區別
A. 申請方式不同
Stack由系統自動分配,而heap需要程序員自己申請,並指明大小。
B. 申請後系統的響應不同
Stack:只要棧的剩餘空間大於申請空間,系統就為程序提供內存,否則將拋出棧溢出異常
Heap:當系統收到程序申請時,先遍歷操作系統中記錄空閑內存地址的鏈表,尋找第一個大於所申請空間的堆結點,然後將該結點從空間結點鏈表中刪 除,並將該結點的空間分配給程序。另外,大多數系統還會在這塊內存空間中的首地址處記錄本次分配的大小,以便於delete語句正確釋放空間。而且,由於 找到的堆結點的大小不一定正好等於申請的大小,系統會自動將多餘的那部分重新放入空閑鏈表。
C. 申請大小限制的不同
Stack:在windows下,棧的大小是2M(也可能是1M它是一個編譯時就確定的常數),如果申請的空間超過棧的剩餘空間時,將提示overflow。因此,能從棧獲得的空間較小。
Heap:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
D. 申請效率的比較:
棧由系統自動分配,速度較快。但程序員是無法控制的。
堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活。
E. 堆和棧中的存儲內容
棧:在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,然後是函數的各個參數,在大多數的C編譯器 中,參數是由右往左入棧的,然後是函數中的局部變數。注意靜態變數是不入棧的。當本次函數調用結束後,局部變數先出棧,然後是參數,最後棧頂指針指向最開 始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
堆:一般是在堆的.頭部用一個位元組存放堆的大小。堆中的具體內容有程序員安排。
6. 含參數的宏與函數的優缺點
宏: 優點:在預處理階段完成,不佔用編譯時間,同時,省去了函數調用的開銷,運行效率高
缺點:不進行類型檢查,多次宏替換會導致代碼體積變大,而且由於宏本質上是字元串替換,故可能會由於一些參數的副作用導致得出錯誤的結果。
函數: 優點:沒有帶參數宏可能導致的副作用,進行類型檢查,計算的正確性更有保證。
缺點:函數調用需要參數、返回地址等的入棧、出棧開銷,效率沒有帶參數宏高
PS:宏與內聯函數的區別
內聯函數和宏都是在程序出現的地方展開,內聯函數不是通過函數調用實現的,是在調用該函數的程序處將它展開(在編譯期間完成的);宏同樣是;
不同的是:內聯函數可以在編譯期間完成諸如類型檢測,語句是否正確等編譯功能;宏就不具有這樣的功能,而且宏展開的時間和內聯函數也是不同的(在運行期間展開)
7. Windows程序的入口是哪裡?寫出Windows消息機制的流程
Windows程序的入口是WinMain()函數。
Windows應用程序消息處理機制:
A. 操作系統接收應用程序的窗口消息,將消息投遞到該應用程序的消息隊列中
B. 應用程序在消息循環中調用GetMessage函數從消息隊列中取出一條一條的消息,取出消息後,應用程序可以對消息進行一些預處理。
C. 應用程序調用DispatchMessage,將消息回傳給操作系統。
D. 系統利用WNDCLASS結構體的lpfnWndProc成員保存的窗口過程函數的指針調用窗口過程,對消息進行處理。
8. 如何定義和實現一個類的成員函數為回調函數
A.什麼是回調函數?
簡而言之,回調函數就是被調用者回頭調用調用者的函數。
使用回調函數實際上就是在調用某個函數(通常是API函數)時,將自己的一個函數(這個函數為回調函數)的地址作為參數傳遞給那個被調用函數。而該被調用函數在需要的時候,利用傳遞的地址調用回調函數。
回調函數,就是由你自己寫的,你需要調用另外一個函數,而這個函數的其中一個參數,就是你的這個回調函數名。這樣,系統在必要的時候,就會調用你寫的回調函數,這樣你就可以在回調函數里完成你要做的事。
B.如何定義和實現一個類的成員函數為回調函數
要定義和實現一個類的成員函數為回調函數需要做三件事:
a.聲明;
b.定義;
c.設置觸發條件,就是在你的函數中把你的回調函數名作為一個參數,以便系統調用
如:
一、聲明回調函數類型
typedef void (*FunPtr)(void);
二、定義回調函數
class A
{
public:
A();
static void callBackFun(void) //回調函數,必須聲明為static
{
cout<<"callBackFun"<
}
virtual ~A();
};
三、設置觸發條件
void Funtype(FunPtr p)
{
p();
}
void main(void)
{
Funtype(A::callBackFun);
}
C. 回調函數與API函數
回調和API非常接近,他們的共性都是跨層調用的函數。但區別是API是低層提供給高層的調用,一般這個函數對高層都是已知的;而回調正好相反, 他是高層提供給底層的調用,對於低層他是未知的,必須由高層進行安裝,這個安裝函數其實就是一個低層提供的API,安裝後低層不知道這個回調的名字,但它 通過一個函數指針來保存這個回調函數,在需要調用時,只需引用這個函數指針和相關的參數指針。
其實:回調就是該函數寫在高層,低層通過一個函數指針保存這個函數,在某個事件的觸發下,低層通過該函數指針調用高層那個函數。
『肆』 C++面試題匯總
某個文件中定義的靜態全局變數(或稱靜態局部變數)作用域是------本文件內
①:默認繼承許可權:
class的繼承按照private繼承處理,struct的繼承按照public繼承處理
②:成員的默認訪問許可權
class的成員默認是private許可權, struct默認是public許可權
註:C++有內置的宏__cplusplus -------有個習慣帶「__」表示內部變數,只供內部使用;不帶雙下劃線的,表示外部介面的變數(標識符)
C++函數的三種傳遞方式為:值傳遞。指針傳遞 和 引用傳遞
註:值傳遞和指針傳遞,本質上就是指針傳遞。
在A類中fun1是虛函數;B類中fun2是虛函數。
①:機制上:c是面向過程的(c也可以是面向對象發的程序); C++是面向對象,提供了類。C++的面向對象的程序比c容易。
②:使用方向:c適合代碼體積小的,效率高的場合,如嵌入式;C++更適合上層的,復雜的;Linux核心大部分是c寫的,因為他是系統軟體,效率要求極高
③:C++是c的超集;
④:C語言是結構化編程語言,C++是面向對象編程語言。
⑤:C++側重於對象而不是過程,側重於類的設計而不是邏輯設計。
C中struct主要提供的是自定義類型,和構造一種新的類型出來;
一致的地方:
不一致的地方:
C語言: 無Protection行為; 不能定義函數,但可以有函數指針;
C++: 有Procetion行為,默認是private; 可以定義函數。
註: 就是訪問許可權,struct對於外部是完全訪問的,C++是有訪問 許可權 設置的;
正確, sizeof 是編譯時運算符,編譯時就確定了 可以看成是和及其有關的常量
註:定義數組的時候,數組的長度必須是一個確定的常量;
形參:是在定義函數時指定的參數,在未調用時他們並不佔用內存中的存儲單元。只有在調用的時候才會被分配內存,調用結束後,形參所佔用的內存單元會被釋放
實參:即你調用函數時傳遞的參數;
重載: 同一個名字空間--- -函數名相同,參數列表不同 ; 注釋:理解成一個類裡面的多個同名函數
重寫/覆蓋: 不同名字空間-----用於繼承,子類重新定義父類中 函數名相同,參數列表也相同 虛函數 的方法
重定義/隱藏:重定義(隱藏)是指派生類的函數屏蔽了與其同名的基類函數,規則如下:
a 如果派生類的函數和基類的 函數同名,但是參數不同 ,此時,不管有無virtual,基類的函數被隱藏。
b 如果派生類的函數與基類 的函數同名,並且參數也相同 ,但是基類函數沒有vitual關鍵字,此時,基類的函數被隱藏。
①: 隱藏 實現 細節 ,使得代碼能夠模塊化;擴展代碼模塊, 實現代碼重寫
②: 介面重用 :為了使用多個派生類中某個派生類的屬性正確調用
用sizeof的方法:
定義一個指針P,列印出sizeof(P),如果結果是4,怎麼標識改操作系統是32位,如果列印結果是2,則標識是16位、。
虛函數 表 ,是在 編譯 期就建立了。各個虛函數被組織成一個虛函數的入口地址的數組(簡而言之,就是組成了一個存放虛函數地址的數組)
虛函數表 指針 是在 運行 時建立的,也就是構造函數被調用時進行初始化的。
封裝,繼承,多態 是什麼?怎那麼用?為什麼使用它?
封裝:將客觀事物抽象成類,每個類對自身的 數據 和 方法 實行 protection ; 注釋 : 保護內部成員
繼承:廣義的繼承有三種實現形式:
實現繼承:指使用基類的屬性和方法,而無需額外編碼的能力;
可視繼承:子窗體使用父窗體的外觀和實現代碼
介面繼承:僅使用屬性和方法,實現之後到子類實現
前兩種和後一種構成了功能復用的兩種方法
多態: 主要是為了抽象
只要是函數都會做類型檢查。
這是內聯函數跟宏觀比的優勢。
①:靜態存儲區域分配; 內存在編譯的時候就已經分配好了,這塊內存在程序的整個運行期間都存在。例如全局變數。
②:在展區創建;在執行函數時,函數內局部變數的存儲單元都可以在棧上創建,函數執行結束時,自動被釋放。效率高,但是內存容量有限。
③:從堆上分配:或者叫:動態內存分配。程序員自己負責在何時用free或delete釋放內存。
C語言中用帶參數的宏定義,C++中用inline
生命周期不同 空間 周期
局部變數 函數調用時創建,結束時銷毀。static除外
局部變數不具有外部鏈接,全局變數
全局變數 : 靜態數據區
局部變數: 放在棧區
malloc、free是C++/C語言標准庫,new、delete是C++運算符。
注意:new、delete不是庫函數;
malloc/free 無法 滿足 對象在創建的時候要自動 執行 構造函 數,對象消亡之前要自動執行 析構函數 。他們是庫函數,而不是運算符,不在編譯器的控制許可權內,。
new、delete 能完成內存的分配和釋放,已經初始化和清理工作。
判斷指針是否為空,如果空,則列印錯誤log,並且return,終止本函數。
不是,兩個不同類型的指針可以強制轉換。
動態申請;
知道運行時才知道一個對象需要多少存儲空間,不需要知道對象的生存周期有多長。
Debug調試版本,它包含調試信息,比如assert的適用,並且不作任何優化,便於程序員調試程序。
Release稱為發布版本,他往往時進行了各種優化,
析構函數時特殊的類成員函數,沒有返回類型,沒有參數,不能隨意調用,也沒有重載,只有在類對象的生命周期結束時,有系統自己調用。優勢方內存空間的作用。
虛函數是C++多態的一種表現,使用虛函數,我們可以靈活的進行動態綁定,當然是以一定的開銷為代價
(這里虛函數的適用還是不太懂,需要進一步學習,比如怎麼調用子類的一切啊)
導致文件描述符結構中指針指向的內存背重復釋放,進而導致一些不可預期的異常。
比如全局變數的初始化,就不是有main函數引起的。例如:
全局對象的構造函數,會在main函數之前執行。
多態,純虛函數,抽象類
內聯函數
虛函數的特點:如果希望派生類能夠重新定義基類的方法,則在基類中將該方法定義為虛方法,這樣可以啟用動態聯編。
內聯函數的特點:使用內聯函數的目的屎我了提高函數的運行俠侶。內聯函數的代碼不能過長,因為內聯函數省去調用函數的時間是以代碼膨脹為代價的。內聯函數不能包含循環語句。因為執行循環語句要比調用函數的開銷大。
函數模板的實例化是由編譯程序在處理函數嗲用時自動完成的,
類模板的實例化必須由程序員在程序中顯示的指定
函數名和參數列表
不能被重載的運算符:
①:不能改變C++內部數據類型(如int float 等)的運算符
②:不能重載「.」,因為.在類中對任何成員都有意義,已經成為標准用法
③:不能重載目前C++運算符集合中沒有的符號,如:@, 等。願意:一是難以理解,二是無法確定優先順序
④:對已經存在的運算符重載不能改變優先順序規則,否則將引起混亂。
有可能是派生類無法調用析構函數
模板可以說比較古老了,但是當前的泛型編程實質上就是模板編程。他體現了一種通用和泛化的思想。
STL有7中容器:
vector(零食進行存儲數據的訪問),list(經常進行數據的增刪改查),deque(隊列結構),map,multimap,set(構造棧形的數據使用),multiset.
容器是一種特定用途的類;
淺拷貝 知識拷貝了指針沒有拷貝資源
深拷貝進行了資源的拷貝
三元表達式「?:」問好後面的兩個操作數必須為同一個類型。否則會出問題。
總的來說,堆是C語言和操作系統的術語,是操作系統維護的一塊動態分配內存;自由存儲是C++中通過 new與delete動態分配和釋放對象的抽象概念。 他們並不是完全一樣。
從技術上來說,堆(heap)是C語言和操作系統的術語。堆是操作系統所維護的一塊特殊內存,它提供了動態分配的功能,當運行程序調用malloc()時就會從中分配,稍後調用free可把內存交還。而自由存儲是C++中通過new和delete動態分配和釋放對象的抽象概念,通過new來申請的內存區域可稱為自由存儲區。基本上,所有的C++編譯器默認使用堆來實現自由存儲,也即是預設的全局運算符new和delete也許會按照malloc和free的方式來被實現,這時藉由new運算符分配的對象,說它在堆上也對,說它在自由存儲區上也正確。
程序編譯的過程中就是將用戶的文本形式的源代碼(c/c++)轉化成計算機可以直接執行的機器代碼的過程。主要經過四個過程:預處理、編譯、匯編和鏈接。具體示例如下。
一個hello.c的c語言程序如下。
其編譯過程如下:
『伍』 2018年的JAVA面試題及答案
Java知識點很多,每個知識點都可能會有面試題,而且不同的企業的考察點是不一樣的。下面給你整理了幾個Java面試題可以參考:
1、面向對象的特徵有哪些方面?
封裝:通常認為封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的介面。
多態性:多態性是指允許不同子類型的對象對同一消息作出不同的響應。簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。多態性分為編譯時的多態性和運行時的多態性。方法重載(overload)實現的是編譯時的多態性(也稱為前綁定),而方法重寫(override)實現的是運行時的多態性(也稱為後綁定)。
2、獲得一個類的類對象有哪些方式?
答:
-方法1:類型.class,例如:String.class
-方法2:對象.getClass(),例如:"hello".getClass()
-方法3:Class.forName(),例如:Class.forName("java.lang.String")
3、如何通過反射創建對象?
答:
-方法1:通過類對象調用newInstance()方法,例如:String.class.newInstance()
-方法2:通過類對象的getConstructor()或getDeclaredConstructor()方法獲得構造器
(Constructor)對象並調用其newInstance()方法創建對象,例如:
String.class.getConstructor(String.class).newInstance("Hello");
『陸』 C/C++經典面試題
C/C++經典面試題
面試題 1:變數的聲明和定義有什麼區別
為變數分配地址和存儲空間的稱為定義,不分配地址的稱為聲明。一個變數可以在多個地方聲明,
但是只在一個地方定義。加入 extern修飾的是變數的聲明,說明此變數將在文件以外或在文件後面部分
定義。
說明:很多時候一個變數,只是聲明不分配內存空間,直到具體使用時才初始化,分配內存空間,
如外部變數。
面試題 2:寫出 bool 、int、 float、指針變數與「零值」比較的 if 語句
bool型數據:
if( flag )
{
A;
}
else
{
B;
}
int型數據:
if( 0 != flag )
{
A;
}
else
{
B;
}
指針型數:
if( NULL == flag )
{
A;
}
else
{
B;
}
float型數據:
if ( ( flag >= NORM ) && ( flag <= NORM ) )
{
A;
2
}
注意:應特別注意在 int、指針型變數和「零值」比較的時候,把「零值」放在左邊,這樣當把「==」
誤寫成「=」時,編譯器可以報錯,否則這種邏輯錯誤不容易發現,並且可能導致很嚴重的後果。
面試題 3:sizeof 和strlen的區別
sizeof和 strlen 有以下區別:
sizeof是一個操作符,strlen是庫函數。
sizeof的參數可以是數據的類型,也可以是變數,而 strlen只能以結尾為『 『的字元串作參數。
編譯器在編譯時就計算出了 sizeof 的結果。而 strlen 函數必須在運行時才能計算出來。並且 sizeof
計算的是數據類型占內存的大小,而 strlen計算的是字元串實際的長度。
數組做sizeof的參數不退化,傳遞給strlen就退化為指針了。
注意:有些是操作符看起來像是函數,而有些函數名看起來又像操作符,這類容易混淆的名稱一定
要加以區分,否則遇到數組名這類特殊數據類型作參數時就很容易出錯。最容易混淆為函數的操作符就
是 sizeof。
面試題 4:C 語言的關鍵字 static 和 C++ 的關鍵字 static 有什麼區別
在C 中static 用來修飾局部靜態變數和外部靜態變數、函數。而 C++中除了上述功能外,還用來定
義類的成員變數和函數。即靜態成員和靜態成員函數。
注意:編程時 static的記憶性,和全局性的特點可以讓在不同時期調用的函數進行通信,傳遞信息,
而 C++的靜態成員則可以在多個對象實例間進行通信,傳遞信息。
面試題 5:C中的 malloc 和C++中的 new有什麼區別
malloc和 new有以下不同:
(1)new、 是操作符,可以重載,只能在 C++中使用。
(2)malloc、free是函數,可以覆蓋,C、C++中都可以使用。
(3)new 可以調用對象的構造函數,對應的 調用相應的析構函數。
(4)malloc僅僅分配內存,free 僅僅回收內存,並不執行構造和析構函數
(5)new、 返回的是某種數據類型指針,malloc、free 返回的是void指針。
注意:malloc申請的內存空間要用 free釋放,而 new申請的內存空間要用 釋放,不要混用。
因為兩者實現的機理不同。
面試題 6:寫一個「標准」宏 MIN
#define min(a,b)((a)<=(b)?(a):(b))
注意:在調用時一定要注意這個宏定義的副作用,如下調用:
((++*p)<=(x)?(++*p):(x)。
p指針就自加了兩次,違背了 MIN的本意。
面試題 7:一個指針可以是 volatile 嗎
可以,因為指針和普通變數一樣,有時也有變化程序的不可控性。常見例:子中斷服務子程序修改
一個指向一個 buffer的指針時,必須用 volatile來修飾這個指針。
說明:指針是一種普通的變數,從訪問上沒有什麼不同於其他變數的特性。其保存的數值是個整型
數據,和整型變數不同的是,這個整型數據指向的是一段內存地址。
面試題 8:a 和&a 有什麼區別
請寫出以下代碼的列印結果,主要目的是考察 a和&a的區別。
#include
void main( void )
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf(「%d,%d」,*(a+1),*(ptr-1));
return;
}
輸出結果:2,5。
注意:數組名 a可以作數組的首地址,而&a是數組的指針。思考,將原式的 int *ptr=(int *)(&a+1);
改為 int *ptr=(int *)(a+1);時輸出結果將是什麼呢?
面試題 9:簡述 C、C++程序編譯的.內存分配情況
C、C++中內存分配方式可以分為三種:
(1)從靜態存儲區域分配:
內存在程序編譯時就已經分配好,這塊內存在程序的整個運行期間都存在。速度快、不容易出錯,
因為有系統會善後。例如全局變數,static變數等。
(2)在棧上分配:
在執行函數時,函數內局部變數的存儲單元都在棧上創建,函數執行結束時這些存儲單元自動被釋
放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。
(3)從堆上分配:
即動態內存分配。程序在運行的時候用 malloc 或 new 申請任意大小的內存,程序員自己負責在何
時用free 或 釋放內存。動態內存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,
就有責任回收它,否則運行的程序會出現內存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會產生
堆內碎塊。
一個C、C++程序編譯時內存分為 5大存儲區:堆區、棧區、全局區、文字常量區、程序代碼區。
4
面試題 10:簡述 strcpy、sprintf 與 memcpy的區別
三者主要有以下不同之處:
(1)操作對象不同,strcpy的兩個操作對象均為字元串,sprintf的操作源對象可以是多種數據類型,
目的操作對象是字元串, memcpy 的兩個對象就是兩個任意可操作的內存地址,並不限於何種數據類型。
(2)執行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
(3)實現功能不同,strcpy主要實現字元串變數間的拷貝,sprintf 主要實現其他數據類型格式到字
符串的轉化,memcpy主要是內存塊間的拷貝。
說明:strcpy、sprintf 與memcpy都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來
選擇合適的函數實現拷貝功能。
『柒』 嵌入式開發—C語言面試題
嵌入式開發—C語言面試題
隨著醫療電子、智能家居、物流管理和電力控制等方面的不斷風靡,嵌入式系統利用自身積累的底蘊經驗,重視和把握這個機會,想辦法在已經成熟的平台和產品基礎上與應用感測單元的結合,擴展物聯和感知的支持能力,發掘某種領域物聯網應用。下面是關於嵌入式開發—C語言面試題,希望大家認真閱讀!
1. 用預處理指令#define 聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1). #define 語法的基本知識(例如:不能以分號結束,括弧的使用,等等)
2). 懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3). 意識到這個表達式將使一個16位機的整型數溢出-因此要用到長整型符號L,告訴編譯器這個常數是的長整型數。
4). 如果你在你的表達式中用到UL(表示無符號長整型),那麼你有了一個好的起點。記住,第一印象很重要。
2. 寫一個“標准”宏MIN,這個宏輸入兩個參數並返回較小的一個。
#define MIN(A,B) ((A) <= (B) (A) : ))
這個測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變為標准C的一部分,宏是方便產生嵌入代碼的唯一方法,對於嵌入式系統來說,為了能達到要求的性能,嵌入代碼經常是必須的方法。
2). 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比if-then-else更優化的代碼,了解這個用法是很重要的。
3). 懂得在宏中小心地把參數用括弧括起來
4). 我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發生什麼事?
least = MIN(*p++, b);
3. 預處理器標識#error的目的是什麼?
如果你不知道答案,請看參考文獻1。這問題對區分一個正常的伙計和一個書獃子是很有用的。只有書獃子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書獃子,那麼應試者最好希望自己不要知道答案。
死循環(Infinite loops)
4. 嵌入式系統中經常要用到無限循環,你怎麼樣用C編寫死循環呢?
這個問題用幾個解決方案。我首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
這個實現方式讓我為難,因為這個語法沒有確切表達到底怎麼回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的
基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什麼。”這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。
數據聲明(Data declarations)
5. 用變數a給出下面的定義
a) 一個整型數(An integer)
b) 一個指向整型數的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer)
d) 一個有10個整型數的數組(An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針(A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數並返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數( 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
人們經常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。
但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道
所有的答案(或至少大部分答案),那麼也就沒有為這次面試做准備,如果該面試者沒有為這次面試做准備,那麼他又能為什麼出准備呢?
Static
6. 關鍵字static的作用是什麼?
這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1). 在函數體,一個被聲明為靜態的變數在這一函數被調用過程中維持其值不變。
2). 在模塊內(但在函數體外),一個被聲明為靜態的變數可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變數。
3). 在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地范圍內使用。
大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數據和代碼范圍的好處和重要性。
Const
7.關鍵字const是什麼含意?
我只要一聽到被面試者說:“const意味著常數”,我就知道我正在和一個業余者打交道。去年Dan Saks已經在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什麼和不能做什麼.
如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什麼意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。最後一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那麼他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字 const,也還是能很容易寫出功能正確的程序,那麼我為什麼還要如此看重關鍵字const呢?我也如下的幾下理由:
1). 關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數為常量是為了告訴了用戶這個參數的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多餘的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2). 通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。
Volatile
8. 關鍵字volatile有什麼含意 並給出三個不同的例子。
一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用保存在寄存器里的備份。下面是volatile變數的幾個例子:
1). 並行設備的硬體寄存器(如:狀態寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變數(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變數
回答不出這個問題的人是不會被僱傭的。我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬體、中斷、RTOS等等打交道,所用這些都要求volatile變數。不懂得volatile內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這傢伙是不是直正懂得volatile完全的重要性。
1). 一個參數既可以是const還可以是volatile嗎?解釋為什麼。
2). 一個指針可以是volatile 嗎?解釋為什麼。
3). 下面的函數有什麼錯誤:
int square(volatile int *ptr)
{ return *ptr * *ptr;
} 下面是答案:
1). 是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這並不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由於*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int *ptr)
{ int a,b;
a = *ptr;
b = *ptr;
return a * b;
} 由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{ int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)
9. 嵌入式系統總是要用戶對變數或寄存器進行位操作。給定一個整型變數a,寫兩段代碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。
對這個問題有三種基本的反應
1). 不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
2). 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到 Infineon為其較復雜的通信晶元寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現bit fields的。從道德講:永遠不要讓一個非嵌入式的傢伙粘實際硬體的邊。
3). 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{ a |= BIT3;
} void clear_bit3(void)
{ a &= ~BIT3;
} 一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的。我希望看到幾個要點:說明常數、|=和&=~操作。
10. 嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變數的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
這一問題測試你是否知道為了訪問一絕對地址把一個整型數強制轉換(typecast)為一指針是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的.品味更接近第二種方案,但我建議你在面試時使用第一種方案。
中斷(Interrupts)
11. 中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標准C支持中斷。具代表事實是,產生了一個新的關鍵字 __interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。
__interrupt double compute_area (double radius)
{ double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
這個函數有太多的錯誤了,以至讓人不知從何說起了:
1). ISR 不能返回一個值。如果你不懂這個,那麼你不會被僱用的。
2). ISR 不能傳遞參數。如果你沒有看到這一點,你被僱用的機會等同第一項。
3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4). 與第三點一脈相承,printf()經常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到後兩點,那麼你的被僱用前景越來越光明了。
代碼例子(Code examples)
12 . 下面的代碼輸出是什麼,為什麼?
void foo(void)
{ unsigned int a = 6;
int b = -20;
(a+b > 6) puts("> 6") : puts("<= 6");
}
這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型。因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大於6。這一點對於應當頻繁用到無符號數據類型的嵌入式系統來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。
13. 評價下面的代碼片斷:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
對於一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:
unsigned int compzero = ~0;
這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗里,好的嵌入式程序員非常准確地明白硬體的細節和它的局限,然而PC機程序往往把硬體作為一個無法避免的煩惱。
到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那麼這個測試就在這里結束了。但如果顯然應試者做得不錯,那麼我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…
動態內存分配(Dynamic memory allocation)
14. 盡管不像非嵌入式計算機那麼常見,嵌入式系統還是有從堆(heap)中動態分配內存的過程的。那麼嵌入式系統中,動態分配內存可能發生的問題是什麼?
這里,我期望應試者能提到內存碎片,碎片收集的問題,變數的持行時間等等。這個主題已經在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺後,我拿出這么一個小節目:下面的代碼片段的輸出是什麼,為什麼?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指針之後,我才想到這個問題。這就是上面的代碼,該代碼的輸出是“Got a valid pointer”。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫常式這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。
Typedef
15. Typedef 在C語言中頻繁用以聲明一個已經存在的數據類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什麼?
這是一個非常微妙的問題,任何人答對這個問題(正當的原因)是應當被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展為
struct s * p1, p2;
上面的代碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。
晦澀的語法
16. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什麼?
int a = 5, b = 7, c;
c = a+++b;
這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行後a = 6, b = 7, c = 12。
如果你知道答案,或猜出正確答案,做得好。如果你不知道答案,我也不把這個當作問題。我發現這個問題的最大好處是:這是一個關於代碼編寫風格,代碼的可讀性,代碼的可修改性的好的話題
What will print out?
main()
{ char *p1=“name”;
char *p2;
p2=(char*)malloc(20);
memset (p2, 0, 20);
while(*p2++ = *p1++);
printf(“%sn”,p2);
}
Answer:empty string.
What will be printed as the result of the operation below:
main()
{ int x=20,y=35;
x=y++ + x++;
y= ++y + ++x;
printf(“%d%dn”,x,y);
}
Answer : 5794
What will be printed as the result of the operation below:
main()
{ int x=5;
printf(“%d,%d,%dn”,x,x< <2,x>>2);
}
Answer: 5,20,1
What will be printed as the result of the operation below:
#define swap(a,b) a=a+b;b=a-b;a=a-b;
void main()
{ int x=5, y=10;
swap (x,y);
printf(“%d %dn”,x,y);
swap2(x,y);
printf(“%d %dn”,x,y);
}
int swap2(int a, int b)
{ int temp;
temp=a;
b=a;
a=temp;
return 0;
}
Answer: 10, 5
10, 5
What will be printed as the result of the operation below:
main()
{ char *ptr = ” Cisco Systems”;
*ptr++; printf(“%sn”,ptr);
ptr++;
printf(“%sn”,ptr);
}
Answer:Cisco Systems
isco systems
What will be printed as the result of the operation below:
main()
{ char s1[]=“Cisco”;
char s2[]= “systems”;
printf(“%s”,s1);
} Answer: Cisco
What will be printed as the result of the operation below:
main()
{ char *p1;
char *p2;
p1=(char *)malloc(25);
p2=(char *)malloc(25);
strcpy(p1,”Cisco”);
strcpy(p2,“systems”);
strcat(p1,p2);
printf(“%s”,p1);
}
Answer: Ciscosystems
The following variable is available in file1.c, who can access it?:
static int average;
Answer: all the functions in the file1.c can access the variable.
WHat will be the result of the following code?
#define TRUE 0 // some code
while(TRUE)
{
// some code
}
Answer: This will not go into the loop as TRUE is defined as 0.
What will be printed as the result of the operation below:
int x;
int modifyvalue()
{ return(x+=10);
} int changevalue(int x)
{ return(x+=1);
}
void main()
{ int x=10;
x++;
changevalue(x);
x++;
modifyvalue();
printf("First output:%dn",x);
x++;
changevalue(x);
printf("Second output:%dn",x);
modifyvalue();
printf("Third output:%dn",x);
}
Answer: 12 , 13 , 13
What will be printed as the result of the operation below:
main()
{ int x=10, y=15;
x = x++;
y = ++y;
printf(“%d %dn”,x,y);
}
Answer: 11, 16
What will be printed as the result of the operation below:
main()
{ int a=0;
if(a==0)
printf(“Cisco Systemsn”);
printf(“Cisco Systemsn”);
}
Answer: Two lines with “Cisco Systems” will be printed.
;