《EDA前端软件开发工程师面试指南》
2023屆EDA領域校招總結,完結撒花!!!
目錄
前言
一、EDA公司介紹
二、項目面試
1.自我介紹
2.項目深入
3.專業經驗
4.成果和技能
5.對面試官有什么問題
三、C++面試
1、高頻考點
2、其他知識點
3、算法題
四、邏輯綜合面試
1.邏輯綜合知識詳解
2.開源邏輯綜合ABC
五、簡歷制作
總結
前言
- 2022/08/26:
本人2023年6月畢業,于2022年7-10月參加秋招,面試總結純屬個人經驗,僅供參考
面試的是EDA前端軟件開發崗位,也會摻雜一些EDA其他流程的面試
在面試過程中發現自己準備的很亂,沒有一個清晰的思路,現在把自己面試的所有經歷和題型整理出來,在這里做一個小的總結,不僅幫助自己整理思路,也給大家做個參考
- 2022/08/27:
對于基本算法而言,推薦去LeetCode力扣進行刷題學習,主要分為easy/medium/hard三種難度系數,保證自己在medium難度下可以做出題目即可。
- 2022/09/05:
自9月份來看,2022年國內EDA行業的就業形勢是沒有2021年好的。
主要原因有二,一是22年互聯網寒冬,導致很多計算機專業的學生選擇到EDA這個工資高、跟互聯網差不多的行業就業,競爭加劇。
二是因為20年美國發布EDA禁令,今年正好是20屆入學的研究生的擇業年。
目前的形式是22年的最高工資沒有21年的平均工資高,且競爭壓力極其卷。
本人目前收到2家公司的offer,在工資層面分別比去年低了20%和15%,在競爭層面是與985的學生進行競爭,<211的基本都很難。
可以預見的是,23年校招和24年校招將會一年比一年難。
- 2022/09/18
陸陸續續接到好幾家公司的offer,觀察了今年的局勢,個人理解是找一家具有良好人員體系培養的公司是最關鍵的。
首先需要明確,國產EDA的路至少還有10年,才能逐漸追趕上S/C這些國際大廠。
如果我們以每三年為界,至少需要在第一份合同結束前,變成可以獨擋一面的R&D,才能在未來的EDA市場中站住腳。
在收到offer后,一定要明確了解到該公司是否具有良好的人員培養體系,而不是一味地追求高薪水和好公司履歷,畢竟EDA市場是以實力為準的。
一、EDA公司介紹
自2020年起,國內的EDA公司猶如雨后春筍般出現,各路公司爭相在市場上搶奪人才,并購、對抗等不計其數,因此對就業者來說,選擇一家適合自己的且有發展前景的公司是首要的。
據可靠消息稱,到2025年左右,國內EDA行業的就業形式依舊是一片良好,工資可以隨便開到比互聯網大廠還要高的水平,但是再往后看,似乎就不一定了,面試最重要的就是對自己未來發展有一個良好的規劃。
在這里,首先介紹一些我熟知的、目前比較火熱的國內EDA公司,給大家做個不成熟的行業介紹。
1.合見工軟(上海)
公司目前融資30億左右,簡單來說就是財大氣粗,同時吸引了很多Synopsys和Cadence的專家,可以學到很多東西,工資也是目前開的很高的之一,其主營業務目前有simulation工具和prototype工具。
本人面試的是prototyping工具下emulation frontend組,做的是FPGA原型驗證,將ASIC的RTL設計轉換成FPGA的RTL,通過EDA工具生成多塊FPGA板,已達到驗證ASIC設計正確性的目的。
2.華大九天(上海)
剛上市的EDA公司,首日股價暴漲3倍,足以看出業界對他的信心。前身是國內第一個EDA工具——熊貓系統,也算是國企,目前的模擬電路設計EDA是行業領先的水平,公司正在開展數字電路設計EDA的全流程開發。
目前是全國最火,最火的EDA公司,無限被拔高,面試人都擠破腦袋想往公司進。
本人面試的是SYN組和STA組,做的是ASIC電路的邏輯綜合。
3.鴻芯微納(上海)
國內較早開始的EDA公司,有前端和后端兩大產品線的ASIC EDA公司,其中后端產品Aguda是基于國外某EDA巨頭的布局布線產品前身開發的,屬于行業領先水平。目前尚未融資過多,因此工資開的不屬于最高的那批,公司秉持著先拿產品說話,再搞資本運作的理念。
個人評價——國內目前最干實事的公司,員工基本都是科研型,俗稱EDA黃埔軍校,本人面試的是前端綜合組,以ABC為基準進行開發。
4.芯華章(南京)
億元俱樂部的億元,為數不多的在南京的EDA公司,可見與南京市政府等關系十分緊密。主要產品是驗證工具,包括simulation和emulation等,據傳其產品背后有某國際EDA公司支持,因此實力十分雄厚。
- 2022/09/27
最近芯華章剛剛收購了瞬曜(Emulation)公司,實力又進一步。
5.安路(上海)
國內目前最大的FPGA上市公司,以國產化替代Xilinx為目標,主要做硬件設計,輔以相應的EDA軟件,目前在低端市場可以有一席之地,生存下去是沒問題的,工資是開的十分的高,屬于會眼紅的狀態。
本人面試的是Synthesis組,即設計EDA工具的前端(邏輯綜合),可以適用于安路自己生產的FPGA上。
6.立芯(上海)
復旦大學陳建利教授創立的公司,其在ICCAD Contest上多次獲得PR 1st的成績,其主要做ASIC芯片布局布線的EDA工具,同時也在開展前端EDA工具的設計。
本人面試的是Syn組,其考驗程度可以稱為極高,需要在線做LeetCode多題,并且還有眾多C++細節以及邏輯綜合的面試內容。
7.中微億芯(無錫)
中國電子旗下的FPGA公司,簡稱國企。在江蘇無錫,小城市福利很不錯。主要做的是基于FPGA(Xilinx)開發EDA工具,團隊人很好,因為是國企,沒有什么壓力。
本人面試的是EDA綜合組,面試不算很難。
8.Synopsys(新思)(上海)
雖說是EDA三巨頭的頭把交椅,實際上就是全世界最“牛逼”的EDA公司,沒有之一。產品線覆蓋EDA全流程且效率極其高,目前國內的技術大牛基本都是從S出來的。
目前S的前端工具DC就是TOP1,但是后端工具ICC2似乎不是很給力。
由于國內S很少招聘EDA前端的崗位,本人面試的是驗證崗位,面試更多與Verilog相關的知識點。
9.Cadence(楷登)(上海)
EDA三巨頭之一,萬年老二,但是同S一樣依舊覆蓋EDA全流程,且效率極高。
C的后端工具Innovus有反超S的ICC2的架勢,且C較S少了很多傲慢,畢竟是追趕者的角色。
同樣,國內C也是很少招聘EDA前端的崗位,本人依舊是面試的驗證崗位,但是面試官的和善度稍微比S高一些些(S輕噴,純純個人意見,雖然但是,能去S的還是一定去S的)。
二、項目面試
1.自我介紹
問: 請簡單的介紹一下你自己和你在學校里研究的項目
// 雖說是簡單介紹,但不是隨便介紹,需要用少的語言(1~2分鐘)概括自己簡歷上的全部內容
答:個人條件+研究課題及成果+專業知識和技能
- 個人條件:畢業于某大學電子科學與技術專業,目前的研究方向是EDA前端設計,主要研究內容是邏輯綜合。
- 研究生研究課題和成果:我的課題是基于布爾可滿足性求解器(SAT)的精確邏輯綜合,已知一個待實現電路,對其約束條件進行SAT求解,并得到對應最優電路結構,實現了精確綜合,相關內容共發表了5篇論文,其中在CCFDAC2021會議上得到了大會唯一最佳論文獎。
- 專業知識和技能:目前在合見工軟實習,熟悉EDA前端的流程,熟練使用C++開發,較熟悉腳本語言、Linux系統、gdb調試以及基本Verilog。
2.項目深入
問: 能仔細說說你課題具體做了什么嗎?
// !!!將科研內容以最通俗易懂的語言進行全方面的講解,記住三字口訣(是什么、為什么、怎么做)
答: 主要分為三部分回答:
1、是什么:做的課題叫什么?主要實現了什么應用?
- 最重要的是,一定要強調跟面試的公司的項目組有哪些共性問題!
- 面試官可能并不知道你做的課題是什么,他需要對你有一個評估,其標準就是你做的東西難不難,你做的東西是不是跟公司的業務有聯系,即你是否有相應的背景。
- 同時,可以適當的做一些功課,了解其公司業務,在相關領域看看一些文獻或實際工具(邏輯綜合強烈推薦要提前看看ABC),這是一個大的加分項!!
2、為什么:為什么要開展這個研究?
- 介紹做這個項目的意義和實際應用,突出自己解決一個問題時的思路
- 重點對比有這個項目和沒有這個研究的區別!
3、怎么做:你是怎么實現的?C++代碼!!!!
- EDA公司目前無非是C++寫代碼、verilog寫case、tcl寫自動化腳本、GDB進行Debug
- 需要強調自己在實現過程中,是怎么用到這些技能的,這是基本盤,也是你在面試官眼里的第一印象。
3.專業經驗
問: 我看到你這里有一些實習經驗,可以簡單說說你干了什么嗎?
//如果沒有實習,這個問題也可以改成“你在學校里參與了什么課題,可以展開說說嗎?”
//這個問題主要是考驗你如何從0到1解決一個問題,或者看你在一個項目中的參與度。
//在公司中是需要解決實際問題的,如果有類似實踐經驗,會上手很快,這是加分項!
答:類似問題可以總結為三類經驗,導師課題(面上項目、縱向課題、企業橫向課題)/?研究生科研項目(省市級項目、校科研項目、比賽項目)/?實習
1、導師課題:突出你負責的內容,劃水的項目不如不寫!
2、研究生科研項目:以本人為負責人的項目
- 課題內容簡介(參考項目經驗)
- 如何把課題從0到1完成,如何分配你的任務給項目成員(考驗你對一個問題如何進行劃分和實現)
3、實習:具體做了哪些事情,參與了什么/負責了什么要寫清楚
- 參與:淺淺的在一個project中做了什么事
- 負責:需要一五一十的全部講清楚
4.成果和技能
1、盡量有體系的寫明自己的成果
- 從GPA到獎學金 = 學習能力
- 科研項目-比賽 = 實踐能力
- 論文 = 科研能力
2、依次從熟練-熟悉-較熟悉-了解說明自己的技能,在這里列舉個人認為在EDA前端需要掌握技能的熟練程度
- 熟練:C++/C, 算法(LeetCode)
- 熟悉:基本Verilog語法, GDB調試(Debug能力), 前端設計流程, 綜合常用算法
- 較熟悉:腳本語言(不限于TCL/Shell/Python)
- 了解:Git/gerrit等版本控制系統
5.對面試官有什么問題
問: 那你對我們公司和部分有什么想問的嗎?
//看似是沒話找話,但如果回答的好,實際上讓面試官對你情商以及態度的印象很好
//!!!!千萬不要問“你們公司是做什么的?”等此類問題,實際上這種問題在面試前可以在網上自己獲得的,很容易讓現場變得尷尬,尤其是技術面的時候,面試官是公司的研發大佬,你問這種低級問題,至少不會加分了。
答: 這里我分類成提問三部曲,結合各位自己的實際情況,供大家參考
1、“我在入職貴公司之后,可能會負責的項目大概會是哪個方向?我可以提前學習一下。”
? ? ? “請問,假如我入職了,您對我未來幾年的規劃是什么?”
- 這個問題實際上是給面試官一個信號,你對未來的職業規劃有一個清晰的認識,同時有一個希望解決的問題的態度。
- 同時,如果面試官告訴你具體的方向的話,你也可以評估自己是否喜歡這部分內容,或直接開始學習這部分知識,以為入職后打算。
2、“冒昧的問一句,我這次面試下來,有哪些不足和缺點?”
- 首先,你會給面試官一個態度,表決心也好、突出自己的上進心也罷,這是一個積極的信號!
- 其次,可以旁敲側擊出公司對你這種崗位和水平的人有什么要求,比如EDA前端軟開就必備C++、算法和GDB等基礎知識,可以了解到面試官更重視哪方面,可以針對性的進行學習。
- 哪怕這次面試失敗了,從外人的角度看出很多問題,發現自己還有哪些不足,可以快速提升自己,以備下次面試。
3、“感謝您的面試,這次對我收獲很大,對于我的不足我也會盡力去補足,也希望可以通過面試加入貴公司!”
- 簡單來說,最后一句話就可以總結成一個字 —— “舔”。
- 但是要“舔”的有深度,“舔”的含蓄,“舔”出風格~
- 以下步驟僅供參考,如有雷同或冒犯,純屬巧合
三、C++面試
1、高頻考點
首先,總結一些高頻考點,下述問題都是必須要懂的,且要了解的很透徹。
1.智能指針
- 避免申請的空間在函數結束時忘記釋放 ——?可以減少野指針(wild)和懸空指針(dangling),即內存泄漏的產生
- 野指針:沒有被初始化的指針
- 懸空指針:指針最初指向的內存已經被釋放的指針
? - unique_ptr:保證同一時間只有一個智能指針可以指向該對象,避免空間泄露
- shared_ptr:強引用。多個智能指針可以指向相同對象,但該對象僅在“最后一個引用被銷毀時”釋放
- weak_ptr:弱引用,用的不多。協助shared_ptr工作,其構造和析構不會引起引用計數的增加或減少,防止兩個shared_ptr互相引用導致的死鎖問題(資源永遠不釋放)
? - 因為智能指針是一個類,類會自動調用析構函數,自動釋放內存。
2.參數傳遞
- 形參:在函數定義時出現的參數
- 實參:函數實際調用的參數
? - 指針:形參為指向實參地址的指針。對指針進行地址操作時,會改變實參。編譯時將“指針變量名-指針變量的地址”添加到符號表中
- 引用:形參是實參的別名,不可修改,必須初始化。在棧中開辟形參的空間,存放的是實參的地址。編譯時將“引用變量名-引用對象的地址”添加到符號表中
- 值:形參是實參的拷貝。在棧中開辟了空間,將實參復制給形參,改變形參不會修改實參
? - 指針參數傳遞&引用參數傳遞:指針傳參的本質是值傳遞,傳遞地址值?/ 引用傳參是地址傳遞,傳遞引用變量
- 編譯的角度:指針在符號表上對應的地址值為指針變量的地址(可修改指向的對象),引用在符號表上對應的地址值為引用對象的地址(不可修改被引用的對象)
例:int &ref= var;
引用:ref作var的別名,不單獨占用內存空間,即ref和var的地址是相同的
指針:程序將開辟一塊內存空間,存儲“ref指向var”這一信息
但仔細思考,如果沒有內存空間存儲“ref指向var”,計算機是怎么知道ref是var的別名?
解:在部分編譯器中,ref在內存中是占用了一塊空間的。
但編譯器處理后,使計算機認為ref不單獨占用內存空間,且取ref地址時直接取到var的地址。
在內存空間中,ref本身也占用一塊內存,里面存儲著var的地址,實際上大小與指針相同,只是經過編譯器處理后,訪問這塊ref內存時將直接轉而訪問var的內存。
綜上:引用實際上是占用內存空間的,是在程序運行過程中聲明的,但程序把它按照不占用內存空間來處理。
3.const關鍵字
- 基本數據類型:修飾符為const的變量定義為常量
- const應用到函數的參數中:
1、參數const:調用函數時對參數const初始化const,在函數體中保護了對應對象的屬性(通常用于參數為指針或引用)
2、返回值const:返回值分為引用返回(返回引用對象)和非引用返回(返回對象的副本)
對于引用返回,需要使用const增加其常量屬性,則不能被修改,保護對象的屬性。 - const應用到類(class)中:
1、const成員變量:在對象的生命周期中是const,但類可以創建多個對象,具有多個const值。
不能在類的聲明中初始化const常量,只能在構造函數的初始化列表中初始化
2、const成員函數:防止成員函數修改成員變量的內容
使用mutable可以強行修改某個變量,static const成員變量可以建立整個類都恒定的常量
3、const類對象:定義常量對象,只能調用常量函數。
非常量對象既可以調用const函數,也可以調用非const函數
4.class的定義和基本概念
- 類
C++是面向對象的程序設計。類(class)是C++的核心特性,包含了數據表示法(類成員對象)和處理數據的方法(類成員函數)。類似于C的struct,但比struct更豐富。
類的公共數據成員可以使用直接成員訪問運算符 . 實現
創建對象: 類名稱 對象 (如 Name A)
- 類對象
對象有三個訪問修飾符:public, private, protected,默認訪問修飾符是private。
- 構造函數(默認是無參構造函數)
在每次創建類的新對象時執行
構造函數的名稱和類的名稱是完全相同的,并且不會返回任何類型,也不會返回void。
- 一般構造函數:構造函數可以帶參數,用于為某些成員變量設置初始值,一個類可以有多個一般構造函數,前提是參數的個數或者類型不同
- 拷貝構造函數:函數參數為對象本身的引用(在創建對象時使用同一類中之前創建的對象來初始化新創建的對象)
使用場景:通過使用另一個同類型的對象(本類型的一個引用變量)來初始化新創建的對象,為深拷貝??
- 類型轉換構造函數:一般構造函數的一種,根據一個指定類的對象創造一個本類的對象(不允許默認轉換的話,提前聲明explict)
- 執行順序:已知存在多態,與析構順序相反
- 析構函數
類似于構造函數,在每次刪除所創建的對象時執行
析構函數的名稱和類名稱完全相同,只是在前面加了一個波浪號(~)作為前綴, 它也不會返回任何值,也不能帶有任何參數。析構函數有助于在調出程序(如關閉文件,釋放資源等)前釋放資源
- 執行順序:已知存在多態,與構造順序相反
- 友元函數
友元函數定義在類的外部,其特點是有權訪問類的所有私有(private)成員和保護(protected)成員。友元既可以是一個函數又可以是一個類。友元類中,整個類及其所有的成員都是友元。定義友元函數需要在函數原型前使用關鍵字friend。
- 內聯函數
通常與類一起使用。定義內聯函數需要在函數名的前面加關鍵字 inline,與宏類似,但更高級。
特點:編譯器在編譯時會把函數的代碼副本放置在每個調用該函數的地方。在類中定義的函數都是內聯函數,可以不使用inline限定。
5.類的三大特性
- 封裝:把對象的屬性和動作封裝成抽象的類
封裝性是通過訪問控制來實現的(public,protected,默認模式private)
- 繼承:讓某個類的對象可以獲得另一個類的對象的屬性(多態的前提)
繼承性是實現軟件可重用性的一種手段,即A類被B類繼承,A類為父類,B類為子類,B類繼承A類的所有public和protected成員數據和成員函數
!子類可以重新定義父類某些屬性,重寫父類的某些方法,即覆蓋父類的某些屬性和方法,使其獲得與父類不同的功能(重寫 override)
- 多態:不同對象對同一個信息產生不同的行為(同一個接口,不同實現)
動態多態:基于封裝和繼承的實現,多個子類對繼承于一個父類的虛函數進行重寫
靜態多態:在同一個作用域對函數進行重載(函數名相同,函數參數列表不同) / 對函數進行模板化,忽略數據類型強調數據操作
體現:父類引用或指針指向子類對象 / 父類的引用或指針可以接受子類對象
前提:存在父類與子類的繼承關系/父類中必須有虛函數/子類必須重寫父類的虛函數/父類引用或指針指向子類對象
6.虛函數
- 虛函數:在基類的函數前加virtual關鍵字,并在一個或多個派生類中重新定義的成員函數,實現同一個接口不同的實現,即多態
- 作用:實現動態聯編 /?實現統一接口但不同執行過程
-
虛函數主要通過虛函數表來實現,其保存該類中虛函數的地址,并且為派生類對象生成一個虛函數指針,指向該類型的虛函數表(可解決繼承、覆蓋的問題,虛函數表就像一個地圖一樣,可指明實際所應該調用的函數)
每一個有虛函數的類都有一個虛函數表,虛函數是整個類所共有的,虛函數表存儲在對象內存最開始的位置。如果子類繼承了多個父類,并且父類有虛函數,則子類要存儲多個虛函數指針。如圖所示,如果繼承了n個父類,并且每個父類都有虛函數,那子類會有n個虛函數表指針。
-
聯編:將函數名和函數體的程序連接到一起
靜態聯編(靜態綁定,早綁定) :在編譯的時候就確定了函數的地址(效率高)動態聯編(動態綁定,晚綁定) :首先取對象的首地址,再解引用取到虛函數表的首地址,再加上偏移量才能找到要調的虛函數
-
虛函數的意義:
1、同一個類的多個對象的虛函數表是同一個,可以節省空間
2、一個類自己的虛函數和繼承的虛函數還有重寫父類的虛函數都會存在自己的虛函數表
3、虛函數表本質是一個地圖導航,可以告訴想要操作子類的父類指針到底該使用哪個函數
- 析構函數為什么是虛函數:在用基類操作派生類時,為了防止執行基類的析構函數,不執行派生類的析構函數。因為這樣的刪除只能夠刪除基類對象,而不能刪除子類對象,會造成內存泄漏
- 構造函數為什么不是虛函數:創建一個對象需要知道對象的完成信息,虛函數調用僅需要函數的接口,不需要知道對象的具體類型,因此不能實例化。
- 純虛函數:在成員函數的形參后面加 = 0 (virtual 函數類型 函數名 (參數表列) = 0)1、為了讓派生類只繼承函數的接口,不能實例化
2、一個基類包含一個以上純虛函數,就是抽象基類。抽象基類不能也沒必要定義對象。?
3、在類的層次結構中,頂層或最上面幾層可以是抽象基類。抽象基類體現了本類族中各類的共性,把各類中共有的成員函數集中在抽象基類中聲明。?4、抽象基類是本類族的共公共接口,即就是從同一基類中派生出的多個類有同一接口
- 總結
7.深拷貝&淺拷貝
- 淺拷貝:系統默認的拷貝函數
- 深拷貝:在堆內存中另外申請空間來存儲數據
當數據成員中有指針時,必須用深拷貝,否則會造成野指針等空間泄露的問題。
8.強制轉換
- static_cast:靜態轉換。主要執行非多態的轉換操作。
- dynamic_cast:動態轉換。專門用于派生類之間的轉換操作。
靜態轉換中,上行轉換(子類->父類)安全,下行轉換(父類->子類)不安全,因為當類型不一致時,轉換的是錯誤意義的指針,容易造成非法訪問等現象。
動態轉換會檢查轉換類型,可以實現下行轉換(父類->子類)轉換
- const_cast:常量轉換。用于const屬性的轉換。
- reinterpret_cast:強制轉換(高危操作,慎用!!!)。從底層對數據進行重新解釋,肆無忌憚的轉換。
9.內存泄漏
- 申請了一塊內存空間,使用完畢后沒有釋放
- 使用智能指針可以有效避免內存泄漏
10.C++11的新特性
- 空指針nullptr:替代NULL(0),類型為nullptr_t,隱式轉換為任何指針或成員指針的類型
- Lambda表達式:類似匿名函數(需要函數體,不需要函數名),可以理解為簡易版函數
基本格式:?[捕獲方式] (參數)?->?返回值類型 {函數體}(可以忽略返回類型,lambda自動推斷出返回類型)
[ ]:閉包形式(closure type),即定義一個lambda表達式后,編譯器會自動生成一個重載()運算符的匿名類。優勢在于可以通過傳值或引用的方式捕獲其封裝作用域內的變量(即捕獲方式中存在的變量)
捕獲方式:
- 右值引用:從右值中直接拿數據初始化或修改左值,不需要重新構造左值后在析構右值
如:int a(左值) = hanshu(B);(右值) - 常量表達式泛化:變量初始化后,可以以常量使用。
如:int n = 5; int arr[n]; - 初始化列表:{變量1,...,變量n}。 如int a{1};
- 類型推導:auto關鍵字和decltype關鍵字
auto:靜態推導出任意類型 - decltype:不對表達式進行求值,就可以獲取其類型
- 構造函數委托:在同一個類中,一個構造函數可以調用另一個構造函數
- final和override:final禁止虛函數被重寫或禁止類被繼承 / override重寫虛函數
- 哈希表:設置哈希函數F,輸入一個key,通過哈希函數F制造獨一無二的key1,然后找到存在哈希表key1對應的val。
11.STL容器
序列式容器:元素在容器中的位置順序存儲和訪問(array/vector/list/deque)
vector:高效的隨機存取
list:大量的插入和刪除
deque:即要求高效的隨機存取,有需要數據兩端的插入和刪除
vector:連續空間的容器
- 迭代器:數據的頭(start)、數據的尾(finish)和數組的尾(end)
- 屬性獲取:
1、begin() = 數據的頭(start)、end() = 數據的尾(finish)
2、front() = *start、back() = *(finish-1)
3、size() = 數組元素的個數? ?max_size() = 數組最大能存儲的元素個數? capacity() = 數組的實際大小(容量)
4、empty() = 判斷vector是否為空 - 數據操作:
1、push_back():首先檢查是否還有備用空間,有,則調用迭代器finish,向尾部插入元素。沒有,則擴充空間(復制2倍大小的vector ->復制數據 -> 釋放原空間),再向其尾部插入元素。
2、pop_back():放棄尾部元素
3、erase(iterator first, iterator last):刪除first到last之間的元素,實際上是將last后面的元素向前移動
4、insert():插入元素。實際分為三種情況,
備用空間夠且插入點后面的元素多于新增元素:先構造出finish-n的空間在后面 -> 將插入點后的元素拷貝到備用空間 -> 從插入元素位置插入新值;
備用空間夠且插入點后面的元素少于等于新增元素:先判斷是否還有備用空間 -> 在finish后構造一個元素finish1 -> 將原先插入點后的元素移到finish1后 -> 在插入元素位置插入新值;
備用空間不夠:先復制兩倍大小的vector -> 分段拷貝元素(插入點前、插入點后) -> 填充新值; - 優缺點:在頻率較高的插入和刪除時盡量不使用
1、優點:在內存的一段連續空間中操作,動態擴容 / 隨機訪問,支持下標訪問?/ 節省空間
2、缺點:?插入刪除元素的時間復雜度為O(n) / 只能在末端進行push和pop / 長度太長后,需要重新分配、拷貝和釋放空間??????
list:雙向鏈表
- 數據結構:存儲了前后指針的雙向鏈表
- 屬性獲取:
1、begin() = 返回指向頭的指針? end() = 返回最后一個元素的后一個地址
2、rbegin() = 返回最后一個地址? ? rend() = 返回第一個地址
3、empty() = 是否為空鏈表
4、size() = 鏈表長度 max_size() = 鏈表最大長度
5、front() = 第一個元素的值? back() = 最后一個元素的值 - 數據操作:list是循環的雙向鏈表,必須確認是在頭或者尾進行刪除或插入(插入是在指定位置前一個地址進行插入的)
內部提供transfer()函數,將某連續范圍的元素遷移到某個指定位置之前
1、push_front():在頭插入? ? push_back():在尾插入
2、pop_front():在頭刪除? ? pop_back():在尾刪除
3、erase():先保留前驅和后繼節點,刪除后,再調整指針位置
4、splice():將兩個鏈表合并,調用transfer()函數
5、merge():將傳入的list鏈表x與原鏈表按從小到大合并到原鏈表中(前提是兩個鏈表已經是排序過的,調用的也是transfer()函數)
6、reverse():鏈表反轉,將每個元素一個一個插入到begin之前
7、sort():排序,調用的是merge()函數
8、賦值:原鏈表大(復制完后刪除原鏈表多余的元素)、原鏈表小(復制完后將被復制鏈表的剩余元素插入到原鏈表中)
9、resize():重新修改list大小,如果原鏈表長度大于新長度,則刪除后面多余的節點
10、clear():清除所有節點(遍歷每個節點,析構并釋放)
11、remove():清除指定值的元素(遍歷每個節點,找到就刪除)
12、unique():清除數值相同的連續元素(連續且相同) - 優缺點:
1、優點:不適合連續內存完成動態操作 / 在內部方便進行插入和刪除?/ 兩端push和pop
2、缺點:不支持隨機訪問 / 相對于vector內存多
deque:雙向隊列(list+vector ≈ deque)
- 元素操作:finish是指向最后一個元素的后一個地址,但first是指向第一個元素的地址
1、push_back():先執行構造再移動節點
2、push_front():先移動節點再進行構造 - 優缺點:
1、優點:隨機訪問方便,支持下標和at() / 在內部方便進行插入和刪除操作 / 兩端進行push和pop??
2、缺點:采用分段連續空間,占用內存多
關聯式容器:通過鍵值(key)存儲和讀取元素
紅黑樹:set/map/multiset/multimap
哈希表:unordered_set/unordered_map/unordered_multiset/unordered_multimap
帶unordered = 無序,帶multi = 允許鍵(key)重復
insert_equal:允許鍵值重復
insert_unique:不允許鍵值重復
set:所有元素都會根據元素的鍵值自動被排序
- 元素就是鍵值(key),不允許有兩個元素具有相同的key
- 不允許通過迭代器改變set的元素值
- 元素操作:
1、begin():返回set容器的第一個元素
2、end():返回set容器的最后一個元素
3、clear():刪除set容器中的所有的元素
4、empty():判斷set容器是否為空
5、max_size():返回set容器可能包含的元素最大個數
6、size():返回當前set容器中的元素個數
7、rbegin:返回的值和end()相同
8、rend():返回的值和rbegin()相同
9、count():用來查找set中某個key出現的次數,即判斷某一key是否出現過
10、equal_range()?:返回一對迭代器(pair),分別表示第一個大于或等于給定key的元素和第一個大于給定key的元素,如果返回失敗則返回end()11、erase(iterator) :刪除迭代器iterator指向的值
erase(first,second):刪除迭代器first和second之間的值
erase(key_value):刪除鍵值key的值
12、find() :返回給定key的迭代器,沒找到則返回end()13、insert(key_value):將key插入到set中?,返回值是pair<set<int>::iterator,bool>,bool標志著插入是否成功,而iterator代表插入的位置,若key已經在set中,則iterator表示的key在set中的位置。
inset(first,second):將迭代器first到second之間的元素插入到set中
14、lower_bound(key_value)?:返回第一個大于等于key的迭代器
15、upper_bound(key_value):返回最后一個大于等于key的迭代器
multiset與set完全一致,除了允許key重復
unordered_set基于哈希表,是關鍵字和key相同的容器
| 性質 | set | multiset | unordered_set | unordered_multiset |
| 底層實現 | 紅黑樹 | 紅黑樹 | 哈希表 | 哈希表 |
| key | 不重復 | 可重復 | 不重復 | 可重復 |
| 插入元素 | insert_unique | insert_equal | insert_unique | insert_equal |
| 元素順序 | 有序 | 有序 | 無序 | 無序 |
| 是否支持[] | 不 | 不 | 不 | 不 |
| 迭代器性質 | const_iterator | const_iterator | const_iterator | const_iterator |
map:以pair為基礎的關聯容器?= key?+ value
multimap允許key重復,且不支持[]運算符
unordered_map基于哈希表
| 性質 | map | multimap | unordered_map | unordered_multimap |
| 底層實現 | 紅黑樹 | 紅黑樹 | 哈希表 | 哈希表 |
| key | 不重復 | 可重復 | 不重復 | 可重復 |
| 插入元素 | insert_unique | insert_equal | insert_unique | insert_equal |
| 元素順序 | 有序 | 有序 | 無序 | 無序 |
| 是否支持[] | 支持 | 不 | 支持 | 不 |
| 迭代器性質 | 非const_iterator | 非const_iterator | 非const_iterator | 非const_iterator |
12.排序算法
| 排序算法 | 平均時間復雜度 | 最好情況 | 最壞情況 | 空間復雜度 | 排序方式 | 穩定性 |
| 冒泡 | 原址 | 穩定 | ||||
| 選擇 | 原址 | 不穩定 | ||||
| 插入 | 原址 | 穩定 | ||||
| 希爾 | 原址 | 不穩定 | ||||
| 歸并 | 額外空間 | 穩定 | ||||
| 快速 | 原址 | 不穩定 | ||||
| 堆 | 原址 | 不穩定 | ||||
| 計數 | 額外空間 | 穩定 | ||||
| 桶 | 額外空間 | 穩定 | ||||
| 基數 | 額外空間 | 穩定 |
- 冒泡排序:選擇相鄰的元素A/B,如果A>B,則交換。從0-n -> 1-n -> 2-n -> ... -> n-1-n進行循環 void BubbleSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) { // 相鄰元素比較int temp = arr[j]; arr[j] = arr[j + 1];arr[j + 1] = temp; //交換兩個元素}}} }
- 選擇排序:分為已排序和未排序區間。遍歷未排序區間找到最小元素,將其放到已排序區間。同時由于亂序了,選擇排序是不穩定的(相同大小的元素在排序后順序可能會改變)。
- 插入排序:同樣分為已排序和未排序區間。對未排序區間的每一個元素,將其插入到已排序區間的合適位置。 void SelectSort(int arr[], int n) {int i, j, min; //min = 最小元素int t = 1;for (i = 0; i < m - 1; i++) { //共有m個元素,只需要比m-1次min = i;for (j = i + 1; j < m; j++) {if (arr[j] < arr[min])min = j; //下標變化,一直是比較后目前最小的數的下標}if (min != i) { //如果最小數下標改變了,進行數的交換t = arr[min];arr[min] = arr[i];arr[i] = t;}} }void InsertSort(int arr[], int n) {int i, j;int low, high, mid; // 對已排序空間進行二分法進行查找for (i = 2; i <= n; i++) {arr[0] = arr[i];low = 1;high = i - 1;while (low <= high) {mid = (low + high) / 2;if (A[mid] > A[0])high = mid - 1;elselow = mid + 1; }for (j = i - 1; j >= low; j--)A[j + 1] = A[j];A[low] = A[0];} }
- 歸并排序:分治算法。
1、首先遞歸地將數組分成兩段(如果數組長度是奇數時,則一半長一半短),直到分成長度為1的 n 個數列,即n個數
2、將數列兩兩合并,每次合并時進行比較和排序,直到完成排序 void MergeSort(int arr[], int left, int right) {if (left == right)return;int mid = left + (right - left) / 2;MergeSort(arr, left, mid); //左側排序MergeSort(arr, mid + 1, right); //右側排序int *help = new int[right - left + 1]; //開辟一個新的數組空間O(N)int i = 0;int p1 = left; //左側的指針int p2 = mid + 1; //右側的指針while (p1 <= mid && p2 <= right) //數組的左側和右側的元素不斷進行對比,較小的元素賦值進數組help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];while (p2 <= right) //將兩側剩余的較大的元素全部賦值進去help[i++] = arr[p2++];while (p1 <= mid) //和上一個while循環一樣,這兩個while循環只能發生一個help[i++] = arr[p1++];for (int i = 0; i < right - left + 1; i++) //將help數組賦值給arr數組arr[left + i] = help[i];delete[] help; // c++需要手動刪除,進行內存管理 } - 快速排序:是目前最快的排序算法
找到一個參照對象,比這個參照對象小的排左邊,比這個參照對象大的排右邊。對兩部分遞歸快速排序,直到有序。
選擇左端(Left),右端(Right),中間值(Center)的中值為參照對象,效率最好 void QuickSort(int arr[], int left, int right) {if (right >= left + 3) //至少存在3個值{int center = (left + right) / 2;if (arr[left] > arr[center])std::swap(arr[left], arr[center]);if (arr[left] > arr[right])std::swap(arr[left], arr[right]);if (arr[center] > arr[right])std::swap(arr[center], arr[right]);std::swap(arr[center], arr[right - 1]);int pivot = arr[right - 1]; //確定找到參照對象auto i = left;auto j = right - 1;while (true) {while (arr[++i] < pivot);while (arr[--j] > pivot);if (i < j) //與參照對象進行比較std::swap(arr[i], arr[j]); elsebreak;}std::swap(arr[i], arr[right - 1]);QuickSort(arr, left, i - 1); //分別對兩個子序列遞歸QuickSort(arr, i + 1, right);}elseInsertSort(arr, right - left + 1); //若小于3個值,直接插入排序 } - ???堆排序:利用“堆”數據結構(子節點的key總小于父節點——大堆頂)進行排序,同樣可以用小堆頂進行排序(子節點的key總大于父節點)
1、通過數組創建堆,輸出堆頂元素(最大/小)
2、以最后一個元素代替堆頂,重新生成堆,輸出堆頂元素(第二大/小)
3、遞歸,直到堆的大小=1 void HeapGenerate(int arr[], int i, int length) //堆生成 { int leftChild = 2 * i + 1; //定義左右子節點int rightChild = 2 * i + 2;int max = i; //初始化,假設左右孩子的雙親節點就是最大值if (leftChild < length && arr[leftChild] > arr[max])max = leftChild;if (rightChild < length && arr[rightChild] > arr[max])max = rightChild;if (max != i) { //若最大值不是雙親節點,則交換值std::swap(arr[max], arr[i]);HeapGenerate(arr, max, length); //遞歸,使其子樹也為堆} } void HeapSort(int arr[], int length) //堆排序 { for (int i = length / 2 - 1; i >= 0; i--) //從最后一個非葉子節點開始向上遍歷,建立堆HeapGenerate(arr, i, length);for (int j = length - 1; j > 0; j--) { //調整堆 ,此處不需要j=0std::swap(arr[0], arr[j]);HeapGenerate(arr, 0, j); //因為每交換一次之后,就把最大值拿出(不再參與調整堆),第三個參數應該寫j而不是length} } - 希爾排序:將比較的元素分為幾個區域,提升插入排序的性能
- 計數排序:待排序的數作為計數數組的下標,統計每個數字的個數,然后輸出(僅針對一定范圍內的整數)
- 桶排序:把整型數組分到有限數量的桶里,再對每個桶進行排序
- 基數排序:需要位比較,進行多次桶排序
2、其他知識點
- C++內存分配情況:棧(編譯器管理,局部變量和函數參數)/堆(程序員管理,new/malloc/delete/free)/全局(靜態)存儲區(全局變量和靜態變量)/常量存儲區(常量)/代碼區
棧由編譯器管理,自動分配空間,程序出錯經常為數據出棧;
堆由程序員管理,手動分配空間(malloc/free/new/delete),內存泄漏常發生在此。 - static:修飾局部變量(修改生命周期,為靜態變量)/修飾全局變量(修改作用域范圍,僅本文件可見)/修飾函數(同修飾全局變量)/修飾類函數(該函數屬于一個類而不屬于此類的任何特定對象)/修飾類變量(該變量在存儲空間僅有一個副本,!類內聲明類外定義!)
- 重載:overload,具有不同參數列表的同名函數
- 重寫:override,子類中重定義父類中除函數體外完全相同的虛函數
- 重定義(隱藏):子類中重定義父類中相同名字的非虛函數
- malloc/free:庫函數。malloc(分配未初始化的內存空間)/free(回收內存空間)
- new/delete:操作符。new(malloc -> 構造函數)/ delete (析構函數 -> free)
- 內存對齊:CPU是個字節為單位來存取內存,將所有對象的最小公倍數設置為內存存取粒度,以下是32位編譯器的內存大小:
char(1)/short(2)/int(4)/指針(4)/float(4)/long(4)/double(8)/long long(8)
內存對齊之后,CPU的內存訪問速度大大提升。 - 二叉樹:二叉樹、二叉搜索樹、平衡二叉樹、高度平衡二叉樹(AVL)
- 紅黑樹:每個節點是紅/黑、根節點為黑、葉子節點(最低的節點)為黑、每層節點紅黑交替、任意一結點到每個葉子節點的路徑都包含數量相同的黑節點。
3、算法題
可以參考我的另一篇博客:算法教程,里面有很詳細的說明。
四、邏輯綜合面試
1.邏輯綜合知識詳解
2.開源邏輯綜合ABC
https://github.com/berkeley-abc/abc
讀取一個門級網表(例如AIG),進行優化,映射后輸出帶單元庫信息的門級網表
術語:
-
AIG(AND-Inverter-Graph,與非圖):ABC的基本數據結構
-
BLIF: Berkeley Logic Interchange Format, a format traditionally used by SIS, VIS, and MVSIS to represent logic networks, Berkeley邏輯交換格式,是SIS、VIS和MVSIS用來表示邏輯網絡的一種傳統格式
-
BDD: Binary Decision Diagram, a canonical graph-based representation of Boolean functions, 二元決策圖,布爾函數的一種基于規范圖的表示
-
CEC: Combinational equivalence checking, 組合等價性檢查
-
BAF: Binary Aig Format, 二進制AIG格式
-
CI: Primary input and latch outputs
-
CO: Primary output and latch inputs
-
FRAIG: (a) Functionally Reduced AIG and (b) AIG package with on-the-fly detection of functionally equivalent AIG nodes, 功能簡化AIG和功能等效AIG節點動態檢測的AIG包
-
FRAIGing: Transforming an AIG into a Functionally Reduced AIG (using the FRAIG package)
-
IWLS: International Workshop on Logic and Synthesis, and annual research-oriented workshop
-
LI: Latch input
-
LO: Latch output
-
LUT: Look-up table, a programmable logic component that can implement an arbitrary Boolean function up to a fixed number of inputs
-
PI: Primary input
-
PO: Primary output
-
SAT: Boolean satisfiability
-
SEC: Sequential equivalence checking
-
SOP: Sum-Of-Products, a non-canonical representation of Boolean functions
-
TFI: Transitive fanin
-
TFO: Transitive fanout
-
CNF:?conjunctive normal form
概念:
- CUT:割集
- boolean-matching(布爾匹配):對于網絡中的每個節點或對于每個CUT,從庫中選擇一個適當的門
- MFFC(maximum fanout free cone,最大扇出自由錐):一個節點n的MFFC是指該節點n的扇入CONE的一個子集,該子集中包含了一些節點,這些節點滿足的條件是每一條經過這些節點到最終輸出PO的路徑都需要經過該節點n,這樣一個FI CONE的子集才能被稱作為MFFC
- structural hashing:結構化哈希,確保所有常量都被傳播,消除冗余項
- NPN class:NPN類。N(Negative,輸入取反)-P(Permutation,對輸入進行置換)-N(Negative,對輸出取反),若已知一個函數,經過以上三種操作得到的全部函數可以分為一個NPN類
- choice:“記住”在綜合流中看到的每個網絡,并在工藝映射過程中選擇每個網絡中最好的部分
組合邏輯優化指令(Command):
- rewrite:一種快速的貪婪算法,通過迭代選擇一個節點上的AIG子圖,并用更小的預先計算的子圖替換它們,以最小化AIG節點的數量,同時保留節點的功能。
- refactor:對使用10-20個輸入的邏輯錐進行折疊和重構
- resubstitution :對于當前節點,重新使用網絡中已經出現過的節點進行表示(真值值相同),如果在某些方面,例如節點數或者depth取得更好的效果,就會被接受。
- balance:得到的AIG是通過對原始AIG中包含的多輸入和門進行代數平衡得到的。根據拓撲順序進行平衡,選擇每個多輸入與門的最小延遲樹分解。平衡考慮了主要輸入的到達時間
- multi:將兩輸入aig改為多輸入與門
- strash:使用結構化哈希將初始SOP邏輯網絡轉換為AIG
- renode:根據AIG重新創建SOP
時序邏輯優化指令:
時序綜合通過修改當前網絡的邏輯以及存儲元素(鎖存器或觸發器)來變換當前網絡。
與原始網絡相比,得到的網絡可能具有不同的狀態編碼和可達狀態空間,但這兩個網絡是時序等效的(即從初始狀態開始,對于相同的輸入向量序列,它們產生相同的輸出向量序列)
- retiming:保持網絡結構不變,但移動鎖存器,使得每個PI/PO路徑和每個循環上的鎖存器數量不變。更復雜的時序變換會修改鎖存器的邏輯結構和位置。集成時序優化在時序變換中占有特殊的位置,它通過一系列簡單的局部變換,如局部重構和retiming,就能使電路的延時達到全局最優。
- if -s:集成時序優化命令,通過探索邏輯綜合過程中看到的所有邏輯結構的組合空間、所有可能的工藝映射和所有可能的retiming,來查找電路的最小延遲
- cycle:模擬隨機輸入的順序網絡,并更新其當前狀態
- init:重置當前網絡所有鎖存器的初始狀態(請注意一個有用的命令print_latch,可用于查看當前網絡中所有鎖存器的初始狀態)
- retime
-
在不增加寄存器個數的前提下,通過改變寄存器的位置來優化關鍵路徑
-
實現多種類型的retiming:最前向、最后向、最小寄存器、啟發式最小延遲
-
當電路從時序AIG轉換為邏輯網絡時,鎖存器在扇出梗之間得到最佳共享。將重定時后的初始狀態計算歸結為SAT問題,利用MiniSat算法進行求解
-
五、簡歷制作
可以參考這個視頻制作CV,
超級簡歷
總結
校招面試是一個很玄妙的東西,原因有二:
1.你很難在有限的時間內,將你所有的能力表達出來
2.面試官很難在有限的時間內,充分了解你的能力
因此,適當的包裝和準備會大大提高自己面試成功的概率,這里并不是強調我們要進行“詐騙”,而是盡可能的展現自己的實力,畢竟面試都沒通過,連被“拆穿”的可能都沒有!
本文主要結合作者自己的親身經歷,給出EDA前端軟件開發面試必備的一些知識,所以,不論你是剛畢業,剛培訓完,或者是想去大廠,都可以浪費幾分鐘,看看我對面試這件事的心得。
所以說,程序員技術能力是一切的前提,不論你是剛要找工作,還是要面試大廠,如果失敗太多次,你一定要先找自己的問題,沉下心來,多學習,等到實力夠了,屬于你的“大廠”自然會給你發來offer。
最后,祝大家都能找到一個nice的領導,心儀的公司,迎娶白富美!
總結
以上是生活随笔為你收集整理的《EDA前端软件开发工程师面试指南》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MVC3快速搭建Web应用(二)
- 下一篇: 2017年html5行业报告,云适配发布