- 自己整理了一些常見的面試題,頻率挺高的都是,而且感覺這里這些基礎的東西都會問,自己過幾天也要面試了,所以發上來讓大家一起看看,有什么錯誤的地方望提醒我糾正。
- 32位數據類型以及sizeof大小、
- char:1; short int:2; int:4; long:4; long long:8; float:4; double:8;bool:1
- 指針類型的都是4(虛函數也是4,因為它是靠虛指針管理的)。sizeof結構體的運算都是內存對其的知識點。(或者可以按照8的倍數去算)。
- 對類來說:空類:1個。若類中有虛函數:保存了一個指針(vptr)指向虛表,所以虛函數四個。繼承的時候要把基類的內存分配的大小也算上。
- int arr[6]={};sizeof(arr)=4*6;sizeof (array) / sizeof (int) 這個是返回數組元素個數
詳見:http://blog.csdn.net/Hello_Hwc/article/details/41170719 - 這里也可能考到繼承中的sizeof的大小
-
#include<iostream> #include<stdio.h> #include<cstring> using namespace std; class A { public: A() : p1(1), p2(2), p3(3) {} //當基類中含有虛函數的時候,含有多個虛函數都是通過一個虛函數指針來控制一個大的虛函數表的,所以所有的虛函數合起來就都是4的大小(32位的操作系統中) virtual int a(){} virtual int b(){} public: int p1; private: int p2; protected: int p3; }; class B: public A {}; int main() { A a; std::cout << *(int *)&a << std::endl; std::cout << *((int *)&a + 1) << std::endl; std::cout << *((int *)&a + 2) << std::endl; B b; std::cout << *(int *)&b << std::endl; std::cout << *((int *)&b + 1) << std::endl; std::cout << *((int *)&b + 2) << std::endl; cout<<sizeof(B)<<endl; }
-
- 內存分配有哪幾種形式?分別為何?區別是什么?對編譯速度影響是何(詳解:http://www.cnblogs.com/daocaoren/archive/2011/06/29/2092957.html)
- 棧:存儲局部變量,函數參數。由系統自動創建和釋放的。
- 堆:它是通過new和delete管理的,不用編譯器管理,由程序員管理,如果沒有釋放掉,操作系統會自動的回收,動態的擴展和收縮。
- 棧和堆的區別
- 管理方式:棧由系統控制,堆由程序員管理,所以堆容易產生碎片(忘記釋放空間);
- 大小:棧比較小,堆比較大;
- 生長方式:堆是向上生長的,棧是向下生長的。
- 碎片問題:堆如果頻繁new和delete來申請和釋放內存,會導致內存的不連續,這樣可能會造成大量的內存浪費(產生許多碎片);棧是先進后出的,所以在一塊內存彈出之前,它的上一塊內存早就彈出了,不會出現內存保留問題
- 分配方式:棧可以動態也可以靜態,堆由於是new和delete 操作所以只能是動態的
- 分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是 C/C++ 函數庫提供的,它的機制是很復雜的,例如為了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於內存碎片太多),就有可能調用系統功能去增加程序數據段的內存空間,這樣就有機會分到足夠大小的內存,然后進行返回。顯然,堆的效率比棧要低得多。
- 自由存儲區:malloc和free申請內存你的地方,和堆很相似。
- 全局靜態存儲區:存儲全局變量,靜態變量。
- 常量存儲區:只是存儲常量,一般不允許修改。
- 知道局部變量為什么比全局變量快么?
- 當Cache命中的時候,CPU訪問內存的效率是最高的 由於局部變量是存在棧中的,當一個函數占用的棧空間不是很大的時候,這部分內存很有可能全部命中cache,這時候CPU訪問的效率是很高
- 當Cache命中的時候,CPU訪問內存的效率是最高的 由於局部變量是存在棧中的,當一個函數占用的棧空間不是很大的時候,這部分內存很有可能全部命中cache,這時候CPU訪問的效率是很高
- new,delete和malloc,free的區別
- new是C++的運算符,malloc是C中的庫函數(STL的底層結構的內存管理都是用malloc來進行的(為什么自己查))
- malloc只管分配不進行初始化,產生的都是隨機值;
- new返回的是一個指針對象,malloc返回的是一個強制轉換的指針對象
- int *p=new int pp[100]; int *p=(Int*)malloc(sizeof(int)*128);
- int *p=new int pp[100]; int *p=(Int*)malloc(sizeof(int)*128);
- 有了malloc/free為什么還要new/delete?
- malloc與free是C++/C語言的標准庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。
- 由於C++函數經常要調用C中的庫函數,那么就可能會用到malloc函數,所以要有malloc。
- 對於不是內置的數據類型,malloc和free無法滿足動態對象的需求,對象在創建和銷毀的時候會調用構造函數和析構函數,但是這兩個函數不是運算符,不在編譯器管理范圍之內,所以只能用new和delete
- 內聯函數與宏定義的區別,它們各有什么優點
- 內聯函數在運行時可調試,而宏定義不可以
- 編譯器會對內聯函數的參數類型做安全檢查或自動類型轉換(同普通函數),而宏定義則不會;
- 內聯函數可以訪問類的成員變量,宏定義則不能
- 在類中聲明同時定義的成員函數,自動轉化為內聯函數
- 內聯函數的優點
- 上面都是,也能夠減少內存開銷,提高效率(函數調用引起的)
- 缺點:引入內聯函數過多,導致代碼膨脹。
- 上面都是,也能夠減少內存開銷,提高效率(函數調用引起的)
- 宏定義優點:
- 使程序簡潔易懂,提高程序的運行效率(使用帶參數的宏定義可以完成函數的調用功能,減少了函數調用產生的出棧入棧的開銷,提高了運行的效率)
- 缺點:會產生二義性(括號的使用等),不會檢查參數是否合法,存在安全隱患,還有上面的那些區別。
- 理解什么是重載、覆蓋、隱藏,區別是何?可否舉例?(詳見:http://www.cnblogs.com/qlee/archive/2011/07/04/2097055.html)
- 重載:在一個類中對函數進行重載,函數名稱相同,函數的參數個數,參數可能不同。
- 覆蓋:派生類覆蓋了基類中的同名函數,函數名稱相同,參數相同,基類函數必須是虛函數,
- 隱藏:派生類中的函數屏蔽了與其同名的基類函數,如果函數名稱相同,參數不同(無論是不是虛函數),隱藏;如果函數名稱相同,參數相同,但是不是虛函數,也隱藏
- 多態
- 多態是什么:多態是由虛函數實現的。舉例:動物基類和派生類(叫聲)
- 多態都有哪些(動態和靜態的):http://blog.csdn.net/hackbuteer1/article/details/7475622
- 多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,並生產代碼,是靜態的,就是說地址是早綁定的。(通過函數重載實現的)而如果函數調用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬於晚綁定。(通過虛函數實現的)
- 動態綁定怎么實現:聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。
- 例如:A *a;B b;a=&b;a.god();//這也是相當於動態綁定(有些題目比較難理解)
- 純虛函數和虛函數區別
- 純虛函數:在基類中只能聲明不能定義,在派生類中必須都實現這個純虛函數。帶有純虛函數的稱為抽象類,只能當做基類使用,抽象類不能定義對象
- 虛函數:在基類中聲明定義,但是在派生類中可以不定義。
- 引入純虛函數比較安全,效率也比較高。
- 虛函數原理:主要是通過虛函數指針和一個虛函數表來實現的。舉個例子:在一個類中有一個虛函數,當這個類創建一個實例對象的時候,那么對象的內存中會存在一個虛函數表(保存虛函數的地址),同時創建一個虛函數指針,保存虛函數表的地址,當調用方法的時候,虛函數指針去指向這個虛函數表。對象不同調用的方法也就不想同,這就實現了多態。
- 虛函數表的內存分配?
- 為什么析構函數要定義成虛函數
- 首先要明確:1.每個析構函數(不加 virtual) 只負責清除自己的成員。
2.可能有基類指針,指向的確是派生類成員的情況。(這是很正常的)
那么當析構一個指向派生類成員的基類指針時,程序就不知道怎么辦了。
所以要保證運行適當的析構函數,基類中的析構函數必須為虛析構。
反正你在寫一個類時,將其析構函數寫為虛函數總不會錯,一樣重載的。
- 首先要明確:1.每個析構函數(不加 virtual) 只負責清除自己的成員。
- struct 和class有什么區別?c語言中的struct 和c++中的struct一樣么?有什么區別?
- C++中的struct和class區別
- 默認的訪問權限:struct是C中升級版本,struct的默認是public;class默認是private
- 默認的繼承權限;struct是public,class是private
- class可以有默認的構造和析構函數,而struct沒有。
- 他們都有成員函數,繼承,多態,struct其實也是一個類(但是實際上也還是一個數據結構)。class可以繼承struct,struct可以繼承class
- C中的struct和C++中的struct區別
- C中的struct只是一個自定義的數據類型(結構體),struct是抽象的數據類型,支持一些類的操作和定義
- C中的struct只是一個自定義的數據類型(結構體),struct是抽象的數據類型,支持一些類的操作和定義
- C++中的struct和class區別
- 說說什么是野指針?野指針什么情況下出現? (詳細)
- 野指針:指向一個已經刪除的對象或者未申請訪問受限地址的指針。
- 出現情況:沒有初始化(要么等於NULL,要么指向對的地方),釋放指針沒有置為NULL(釋放只是釋放了指針所指的內存,指針本身並沒有釋放掉)
- 你熟悉預編譯指令么?條件編譯是用來做什么的?你會寫么?
- 預編譯主要是處理#開頭的指令,如#include,宏定義,條件編譯等,進行初期的代碼替換的工作(宏替換等)
- #include 包含源代碼;#ifdef(#if define),#ifndef(ifndef=if not define:如果沒有宏定義),#endif:
- 條件編譯:#if #else #elif #endif 目的是:防止頭文件的重復包含和編譯
-
1 #ifdef 標識符 2 /*程序段 1*/ 3 #else 4 /*程序段 2*/ 5 #endif 6 7 它的作用是當標識符已經由#define定義過了,則編譯程序段1,否則編譯程序段2,也可以使用簡單形式 8 9 #ifdef 標識符 10 /*程序段1*/ 11 #endif
- String
- 知道斷言ASSERT()怎么樣么?一定要常用。它是函數還是宏?為什么不能是函數?
- assert其實是一個宏,我自己沒怎么用過,它的作用是一個判斷計算表達式真假。例如:assert(a!=0)(一般的它只允許調用一個表達式參數,如果多個參數判斷不准確),如果a!=0,就是結果正確,程序繼續運行,如果false那就會先向stderr打印一個錯誤信息,然后再調用abort終止程序。(一般把這個宏放在程序中來進行調試,它主要是用作debug版本中 )
- 它的缺點是:頻繁的調用會影響程序的性能,會增加額外的開銷。
- 鏈表的一些操作和面試常見的題目
- http://blog.csdn.net/luckyxiaoqiang/article/details/7393134
- 題目
- 棧的一些操作和面試題:
- 題目:
- 兩個隊列實現棧
- 兩個棧實現隊列
- 元素出棧、入棧順序的合法性
- 一個數組構造兩個棧(最大限度利用空間,就是通過兩個個指針:前后各一個分別擴充兩個棧的內存)
- 題目:
- 二叉樹,紅黑樹的一些操作和面試題
- 紅黑樹:(詳見:http://www.cnblogs.com/skywang12345/p/3245399.html)
- 紅黑樹的數據結構(枚舉顏色,父,左,右指針,顏色,節點值)
-
1 enum Color 2 { 3 RED = 0, 4 BLACK = 1 5 }; 6 7 struct RBTreeNode 8 { 9 struct RBTreeNode*left, *right, *parent; 10 int key; 11 int data; 12 Color color; 13 };
-
- 紅黑樹的各種操作的時間復雜度是多少?(所有操作都是logn)而二叉樹的都是o(h)
- 紅黑樹的特性(五點)
- 紅黑樹和哈希表怎么選擇?
- 應該從四個方面分析:數據總量,空間消耗,查找效率
- 查找效率:哈希表,常數時間不是吹的,這個和數據總量無關的,紅黑樹是logn級別的。(但是不一定選哪個,如果數量到達了一定的級別,哈希表還是比較好的,但是哈希表還有別的函數的消耗,這個也會影響)
- 內存消耗:對內存消耗十分嚴格,最好是用紅黑樹,哈希表有可能會太大太難管理
- 在實際的系統中,例如,需要使用動態規則的防火牆系統,使用紅黑樹而不是散列表被實踐證明具有更好的伸縮性。Linux內核在管理vm_area_struct時就是采用了紅黑樹來維護內存塊的。
- 紅黑樹相比於BST和AVL樹有什么優點?
- 紅黑樹是犧牲了嚴格的高度平衡的優越條件為代價,它只要求部分地達到平衡要求,降低了對旋轉的要求,從而提高了性能。紅黑樹能夠以O(log n)的時間復雜度進行搜索、插入、刪除操作。此外,由於它的設計,任何不平衡都會在三次旋轉之內解決。
- 相比於BST(log(n+1)/log2),它的時間復雜度是更低的,而且最壞的時間復雜度是oN,這是不如紅黑樹的
- 相比於AVL樹,時間復雜度是相同的,但是紅黑樹的統計效率更高。其實選擇哪個樹主要是看插入的數據,插入的數據分布比較好則適合AVL,插入的數據比較混亂的話就適合紅黑樹了
- 插入和刪除操作(畫圖解釋)
- 左旋(將X變為左子節點),右旋操作(將Y變成右子節點);插入(三種情況)刪除(四種情況)操作。(畫圖解釋原理)
- 紅黑樹的數據結構(枚舉顏色,父,左,右指針,顏色,節點值)
- 二叉樹
- 二叉樹的一些節點的公式()
- 二叉樹第i層上的結點數目最多為 2{i-1} (i≥1)。
- 深度為k的二叉樹至多有2{k}-1個結點(k≥1)。
- 包含n個結點的二叉樹的高度至少為log2 (n+1)。
- 在任意一棵二叉樹中,若終端結點的個數為n0,度為2的結點數為n2,則n0=n2+1。
- 二叉樹的操作的復雜度:o(H)
- 二叉樹的一些面試題目
- 前序,中序,后序遍歷。(遞歸的)
- 怎么判斷二叉樹是不是平衡。(每個部分都是平衡的才算平衡(高度差來判斷)):遞歸法和非遞歸法
- 二叉搜索樹轉化為有序雙鏈表
- 求一個二叉樹的深度(遞歸)
- 二叉樹中兩個節點的最近公共祖先節點(三種情況:無根,有parent;有根,無parent,有左右;有根和節點值)
- 二叉樹中葉子節點的個數(遞歸分別求左子樹中的葉子節點和右子樹的葉子節點))
- 二叉樹的一些節點的公式()
- 紅黑樹:(詳見:http://www.cnblogs.com/skywang12345/p/3245399.html)
- 你知道函數不能返回棧指針么?
-
#include<iostream> #include<string> using namespace std; char * fun() { char s[]="abc";//局部變量存儲在棧中 return s;//返回的是棧指針 } string fun1(){ string s="abc"; return s; } int main(){ char *ptr=fun();//返回的棧指針,但是不管指向地方的內容是什么,一旦發生了函數調用(這里發生了函數調用,函數的入棧 //壓棧操作使得上次棧指針往下壓了(棧是向下生長的),指向的內容也就發生了變化
但是棧指針指向的所以返回的棧指針指向的內容就變了,返回亂碼。 string s=fun1(); cout<<ptr<<endl; cout<<s<<endl; return 0; }
-
- 排序
- 冒泡,交換,快排,堆排序,選擇排序
- http://www.cnblogs.com/Kobe10/p/5573415.html
- 冒泡:最好n平方,最壞n平方,a[j]<a[j+1]
- 交換:最好n平方,最壞n平方,從頭到尾遍歷找到最小的進行交換(固定)(a[j]<a[i])
- 選擇排序:最好n平方,最壞n平方,每次遍歷從頭開始遍歷找到最小的放在數組首部,然后從第二個開始遍歷,依次類推。
- 插入排序:將一個數字插入到已經排序好的有序數組中。最好n平方,最壞n平方。思想:先是找出第一個數,這個數默認是排序的,然后取出第二個數,從后往前比較,小的的交換,依次進行直到頭部,然后多次執行同樣的操作。
- 快排:最好是lgn,最壞是n平方。快排的空間復雜度是lgn,其他都是常數級別。思想:選出第一個數,然后設置兩個指針,指向第二個數和尾部,先是尾部指針移動,再是頭部指針移動,最后將x插入中間,然后遞歸的快排左邊和右邊。
- 希爾排序:最好是lgn,最壞是n的s次方(1<s<2)。思想:設置一個步長,
- 堆:最好是lgn,最壞也是lgn
- 知道什么是struct中的對齊么?
- http://blog.chinaunix.net/uid-14802518-id-2784907.html
- 內存對齊的原則(http://blog.chinaunix.net/uid-26548237-id-3874378.html)
- (1)數據成員的對齊:對於結構的各個成員,第一個成員位於偏移為0的位置,以后每個暑假成員的偏移量必須是min(#pragma pack()指定的數,這個數據成員自身長度)的倍數;
(2)結構體的對齊:在數據成員完成各自對齊之后,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員大小中,比較小的那個進行;(自己舉例)結構體(char,int,short)=12
- (1)數據成員的對齊:對於結構的各個成員,第一個成員位於偏移為0的位置,以后每個暑假成員的偏移量必須是min(#pragma pack()指定的數,這個數據成員自身長度)的倍數;
- extern c‘ 是干什么的?
- extern可以置於變量或者函數前,以標示變量或者函數的定義在別的文件中,
- 此外extern也可用來進行鏈接指定。
- 也就是說extern有兩個作用,第一個,當它與"C"一起連用時,如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個函數名時按着C的規則去翻譯相應的函數名而不是C++的,C++的規則在翻譯這個函數名時會把fun這個名字變得面目 全非,可能是fun@aBc_int_int#%$也可能是別的,這要看編譯器的"脾氣"了(不同的編譯器采用的方法不一樣),為什么這么做呢,因為 C++支持函數的重載啊,在這里不去過多的論述這個問題,如果你有興趣可以去網上搜索,相信你可以得到滿意的解釋!
第二,當extern不與"C"在一起修飾變量或函數時,如在頭文件中: extern int g_Int; 它的作用就是聲明函數或全局變量的作用范圍的關鍵字,其聲明的函數和變量可以在本模塊活其他模塊中使用,記住它是一個聲明不是定義!也就是 說B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數時,它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數或變量, 但它不會報錯,它會在連接時從模塊A生成的目標代碼中找到此函數。 - C++語言在編譯的時候為了解決函數的多態問題,會將函數名和參數聯合起來生成一個中間的函數名稱,而C語言則不會,因此會造成鏈接時找不到對應函數的情況,此時C函數就需要用extern “C”進行鏈接指定,這告訴編譯器,請保持我的名稱,不要給我生成用於鏈接的中間函數名。
- 函數指針和指針函數(const)?
- int *const a;常量指針。a的值不能修改,因為它的地址已經固定了,但是*a是可以修改的。 (地址的內容可以修改)
- const int *a;指針常量,指向常量的指針。*a是不能修改了,指向的是一個常量,但是a是可以修改的,可以給地址重新賦值
- const int const *a;既不可以修改內容,也不可以修改地址。
- 內存管理你懂多少?(包括內存泄漏,野指針知識,非法調用,內存溢出等)
- 內存泄漏:程序中一塊沒有使用的內存,由於用完沒有釋放,導致一直存在程序中,但是又不能分給其他程序使用,造成了內存的浪費,它不會及時的發生錯誤
導致內存泄漏:一般是new或者malloc的時候忘記了free或者delete了
內存泄漏解決方式:VLD - 內存溢出:就是我申請了一塊int a[10]的內存,但是我賦值的時候賦值a[11],這就是內存溢出了
- 因素:存取或者復制內存緩沖區的方式不安全。數組越界。是用非類型安全的語言(C/C++)。編譯器設置的內存緩沖區太靠近關鍵數據結構。
char s[]="1234567890"; char d[]="123"; strcpy(d,s);cout<<d<<endl<<s<<endl;輸出的是1234567890 567890(15位的數,內存溢出了,棧的機制) - 數組下標越界:這個應該是的問題,數組255大小,但是當a[255]就是256個元素,相當於越界了。
死循環:這個就是字符型的變量大小在0-255之間,所以說i永遠不可能大於255的,死循環。
內存泄漏:創建的臨時變量,在棧中,應該會由系統自動釋放,所以應該是不存在內存泄漏的問題。
內存溢出:通俗理解就是內存不夠,通常在運行大型軟件或游戲時,軟件或游戲所需要的內存遠遠超出了你主機內安裝的內存所承受大小,就叫內存溢出。此時軟件或游戲就運行不了,系統會提示內存溢出,有時候會自動關閉軟件,重啟電腦或者軟件后釋放掉一部分內存又可以正常運行該軟件。
棧溢出:屬於緩沖區溢出的一種。棧溢出是由於C語言系列沒有內置檢查機制來確保復制到緩沖區的數據不得大於緩沖區的大小,因此當這個數據足夠大的時候,將會溢出緩沖區的范圍。
- 內存泄漏:程序中一塊沒有使用的內存,由於用完沒有釋放,導致一直存在程序中,但是又不能分給其他程序使用,造成了內存的浪費,它不會及時的發生錯誤
- malloc返回什么?怎么用?
- 字符串的操作和面試題
- 替換空格20%
- 打印字符串的所有排列
- 第一個只出現一次的字符
- 翻轉單詞順序VS左旋轉字符串
- 怎樣將整數轉換成字符串數,並且不用itoa函數?
- 把字符串轉化為數字
- 對稱子字符串的最大長度(第一步先是找字串,找到字串之后從串中間開始往兩邊進行判斷是不是對稱的,這里要分兩種情況,如果字串的長度是奇數的話那么就是以中間一個數為中心;如果字串的長度是偶數的話,那就得以兩個數為中心)
- C++的四種類型轉換機制
- const_cast,static_cast,dynamic_cast (),reinterpret_cast(ri:ɪnˈtɜ:rprɪt)
- http://blog.csdn.net/it_yuan/article/details/22758561
- static_cast:(靜態的類型轉換)主要用在繼承關系的對象類型(還有一般類型)的轉換(不用於指針)。A a;B b; a=static_cast<A>b;(強制轉換)
- dynamic_cast:(動態的類型轉換:在運行的時候才進行)只能用於有繼承關系的對象類型轉換(指針和引用)。A a;B b;B*p=&b;A *pp=dynamic_cast<A*>p;
當然有虛函數的類也行。 - const_cast:用於將指針常量轉換為普通的常量。const int * p="2"; int * pp= const_cast<int *> p;
- reinterpret_cast:將一個類型的指針轉換為另一個類型的指針。double * b=2.0;int *a=reinterpret_cast<double*>b;
- const的作用
- const定義的常量必須賦值初始化,不能另外的初始化。
- const修飾函數的輸入參數:當傳入的參數是用戶自定義的類型,最好是用const引用修飾,可以提高效率。
- const修飾函數的返回值。
- const修飾類的成員函數。
- strcpy函數的編寫?
-
char *strcpy(char *strDest, const char *strSrc); { assert((strDest!=NULL) && (strSrc !=NULL)); // 2分 char *address = strDest; // 2分 while( (*strDest++ = * strSrc++) != ‘\0’ ) ;// 2分return address ; // 2分 }
讀者看了不同分值的strcpy版本,應該也可以寫出一個10分的strlen函數了,完美的版本為:
int strlen( const char *str ) //輸入參數const 以下是引用片段:
{
assert( str != NULL ); //斷言字符串地址非0
int len=0; //注,一定要初始化。
while( (*str++) != '/0' )
{
len++;
}
return len;
}
strcpy返回值的作用。返回的是一個char*,用來支持鏈式表達,舉例如下
- strcpy 函數將strSrc 拷貝至輸出參數strDest 中,同時函數的返回值又是strDest。 這樣做並非多此一舉,可以獲得如下靈活性: char str[20]; int length = strlen( strcpy(str, “Hello World”) )
-
- explicit作用
- 這個參數是用來修飾構造函數的,被修飾的構造函數不能發生隱式的類型轉換。
- 隱式轉換的例子:A a=1;(這個發生了隱式轉換,隱式的調用了)http://www.cnblogs.com/this-543273659/archive/2011/08/02/2124596.html
- 隱式轉換的例子:A a=1;(這個發生了隱式轉換,隱式的調用了)http://www.cnblogs.com/this-543273659/archive/2011/08/02/2124596.html
- 這個參數是用來修飾構造函數的,被修飾的構造函數不能發生隱式的類型轉換。
- 為什么使用static_cast而不是使用C中的轉換函數?:static_cast更安全。
-
- A* p1 = (A*) &b; // 這句是c風格的強制類型轉換,編譯不會報錯,留下了隱患
- A* p2 = static_cast<A*>(&b); // static_cast在編譯時進行了類型檢查,直接報錯
- A* p3 = dynamic_cast<A*>(&b);
-
- C++中的異常處理機制
- 通常,監測異常情況的程序語句包含在try中。如果try塊中發生了異常(也就是錯誤),則用throw處理。異常由catch捕獲,並得到處理。
- 一般一個try不止一個catch(捕獲並處理異常),具體哪個由系統來決定
- 構造函數中,成員變量一定要通過初始化列表來初始化的是
- const修飾的成員變量,引用。(這兩個都是必須在定義的時候初始化),還有需要初始化的數據成員是對象(繼承時調用基類構造函數)
- const修飾的成員變量,引用。(這兩個都是必須在定義的時候初始化),還有需要初始化的數據成員是對象(繼承時調用基類構造函數)
- auto_ptr和shared_ptr
- auto_ptr動態的管理內存資源的智能指針,當對象不存在的時候它會自動的釋放內存,不需要人為的去管理,但是有缺陷,最好是用shared_ptr auto_ptr注意事項:
- auto_ptr不能共享所有權。例如auto_ptr<T> a(new A);auto_ptr<T> b(a);這樣a的指針的所有權就歸b了,a就不能通過指針去調用函數了。
- auto_ptr不能指向數組
- auto_ptr不能作為容器的成員
- 不能通過復制操作來初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OKstd::atuo_ptr<int>p = new int(42);//Error
- 不要把auto_ptr放入容器
- shared_ptr能共享所有權,而且很多好處,一般就用這個。
- static關鍵字的作用
- static的第二個作用是保持變量內容的持久
- static的第三個作用是默認初始化為0(static變量)
- 可以將類中的函數聲明為static,只能夠訪問靜態的數據成員和靜態的成員函數。
- 隱藏
- 什么是拷貝構造函數
- 它是一個單個參數的構造函數,參數類型是const&的對象的類型。它必須有防止自我賦值的操作,最好的拷貝構造函數如下
- 系統會提供默認的拷貝構造函數和賦值運算符,有時候默認的拷貝構造函數不好使,可以自己定義一個拷貝構造函數。
-
賦值運算符
widger& widget::operator=(const widget & rhs){//一份不安全的=版本 if (this==&rhs)return *this;//這個版本還是存在異常的問題,如果new bitmap出現異常,指針會指向一塊已經被delete的部分, delete pb; pb=new bitmap(*rhs.pb); return *this; }
- 深拷貝和淺拷貝
- 淺拷貝:指的是在對象復制時,只是對對象中的數據成員進行簡單的賦值,默認拷貝構造函數執行的也是淺拷貝。
- 深拷貝:當對象復制的時候,對象中存在動態成員,那么就要進行深拷貝,重新動態的分配空間。
Rect(const Rect& r) { width = r.width; height = r.height; p = new int; // 為新對象重新動態分配空間 *p = *(r.p); }
- 防止默認拷貝函數發生的辦法:將默認拷貝構造函數聲明為private。
- 哈希表
- 什么是哈希表?
- 哈希表也稱散列表。它是通過實值和鍵值兩部分組成。通過關鍵的鍵值來訪問數據結構中的實值,來提高查找效率。
- 解決碰撞的方法
- 線性探測,二次線性探測,開鏈法
- 線性探測:比如說插入,當找到的位置不存在那就只能依次向后遍歷進行查找,直到找到一個合適的位置插入,如果還咩有就跳到表頭進行線性探測。
- 二次線性探測:查找的方式和一次相同,但是每次是前進i平方個位置。
- 開鏈法:是由一個vector和多個list組成。思路:vector中存放的是一些指針,這些指針指向list,用插入的元素除以vector的大小,余數對應於指針數,將其插入。
- 哈希沖突如何產生
- 有相同的鍵值但是對應的實值不同插入進來就會產生哈希沖突
- 有相同的鍵值但是對應的實值不同插入進來就會產生哈希沖突
- 什么是哈希表?
- array和list的區別
- array是連續存儲的,list的是線性存儲的
- 當數據量很大的時候:array的查找效率肯定是比list要高的
- 當頻繁的需要插入和刪除的時候:用list
-
1.capacity
指容器在不分配新的存儲空間的前提下它最多可以保存多少元素。
2. size
指當前容器已經保存的元素的數目。
在弄清這兩個概念以后,很容易懂resize和reserve的區別
vector 的reserve增加了vector的capacity,但是它的size沒有改變!而resize改變了vector的capacity同時也增加了它的size!
原因如下:
reserve是容器預留空間,但在空間內不真正創建元素對象,所以在沒有添加新的對象之前,不能引用容器內的元素。加入新的元素時,要調用push_back()/insert()函數。
c.reserve(n)指分配至少能容納n個元素的內存空間。
resize是改變容器的大小,且在創建對象,因此,調用這個函數之后,就可以引用容器內的對象了,因此當加入新的元素時,用operator[]操作符,或者用迭代器來引用元素對象。此時再調用push_back()函數,是加在這個新的空間后面的。
兩 個函數的參數形式也有區別的,reserve函數之后一個參數,即需要預留的容器的空間;resize函數可以有兩個參數,第一個參數是容器新的大小, 第二個參數是要加入容器中的新元素,如果這個參數被省略,那么就調用元素對象的默認構造函數。下面是這兩個函數使用例子:
- 虛函數能聲明為內聯函數嗎?為什么
- 不能。內聯函數是編譯期就已經綁定了的,而虛函數要考慮到多態的性質
- 不能。內聯函數是編譯期就已經綁定了的,而虛函數要考慮到多態的性質
-
拷貝構造函數作用及用途?什么時候需要自定義拷貝構造函數?
- 深拷貝和淺拷貝
- 深拷貝和淺拷貝
-
二維動態數組的申請和刪除
-
size_t row, col; //輸入row和col的數值 int **MathTable = new int*[row]; for (int i = 0; i < row; i++) MathTable[i] = new int[col]; //code for (int i = 0; i < row; i++) delete[] MathTable[i]; delete[]MathTable;
-
- 指針和引用的區別
- 指針是一個變量,指向一個地址,引用時一個原變量的別名
- const指針,沒有const引用
- 指針可以有多級,而指針只能有一級
- 指針可以為空,引用不能為空
- sizeof引用"得到的是所指向的變量(對象)的大小,而"sizeof指針"得到的是指針本身的大小;
- STL中的sort用的什么算法
- 快排。
- 快排。
- #include<>和include""的區別
- include<>先訪問的編譯器的類庫路徑的頭文
- include""引用的是本地的程序目錄的相對路徑的頭文件。(自己編寫的那些頭文件就是)
- 構造函數和析構函數的調用順序
- (有繼承關系)構造函數:
1.基類構造函數。如果有多個基類,則構造函數的調用順序是某類在類派生表中出現的順序,而不是它們在成員初始化表中的順序。
2.成員類對象構造函數。如果有多個成員類對象則構造函數的調用順序是對象在類中被聲明的順序,而不是它們出現在成員初始化表中的順序。
3.派生類構造函數。 - (無繼承關系)析構函數:與構造函數相反
1.對象生命周期結束,被銷毀時(一般類成員的指針變量與引用都i不自動調用析構函數);
2.delete指向對象的指針時,或delete指向對象的基類類型指針,而其基類虛構函數是虛函數時;
3.對象i是對象o的成員,o的析構函數被調用時,對象i的析構函數也被調用。 - 沒有繼承關系的構造函數和析構函數:按照初始化對象的順序進行構造,按照與構造順序相反的順序進行析構。
- (有繼承關系)構造函數:
- C++不能重載的運算符
- c++不能重載的運算符有.(點號),::(域解析符),?:(條件語句運算符),sizeof(求字節運算符),typeid,static_cast,dynamic_cast,interpret_cast(三類類型轉換符)。---出自C++ primer plus
- c++不能重載的運算符有.(點號),::(域解析符),?:(條件語句運算符),sizeof(求字節運算符),typeid,static_cast,dynamic_cast,interpret_cast(三類類型轉換符)。---出自C++ primer plus
- 異常處理中final,finally,finalize
- final為關鍵字; finalize()為方法; finally為為區塊標志,用於try語句中;
- 作用
- final:修飾常量的關鍵字,他修飾的變量存放在常量池中。
- finally:用於標識代碼塊,與try進行配合,如果發生異常,這個必定會執行
- finalize:釋放對象占用的資源,有時候也是類似於析構函數
- final詳解
- final定義基本類型變量時,要求變量初始化必須在聲明時或者構造函數中,不能用於其它地方。該關鍵字定義的常量,除了初始化階段,不能更改常量的值。
- final定義基本類型變量時,要求變量初始化必須在聲明時或者構造函數中,不能用於其它地方。該關鍵字定義的常量,除了初始化階段,不能更改常量的值。
- 使用final關鍵字定義的方法,不能被子類繼承
- 一個任何final類無法被任何人繼承,這也就意味着此類在一個繼承樹中是一個葉子類,並且此類被認為是很完美的,不需要進行任何修改(總之是不推薦使用)
- sizeof和strlen對字符數組的一些常見題目
-
#include <stdio.h> #include<stdlib.h> int main(void) { char str[] = {4, 3, 9, 8, 2, 0, 1, 5}; char str1[]="hello"; char str2[10]={1,2,3,4,0}; char *a=str; //這里很明顯了,strlen是計算長度的,不把\0包括在內,而0數字帶便的就\0,所以第一個數就是5 //sizeof是計算元素的個數的,是計算大小的,他是暴扣\0的,沒滿的時候 printf("%d\n",strlen(str)); printf("%d\n",sizeof(str)); printf("%d\n",strlen(str1)); printf("%d\n",sizeof(str1)); printf("%d\n",strlen(str2)); printf("%d\n",sizeof(str2)); printf("%d\n",sizeof(a)); }
-
-
構造函數與析構函數是否能為虛函數?
大家都知道析構函數可以是虛函數,構造函數不能為虛函數。為什么構造函數不能為虛函數呢?
1。從存儲空間角度
虛函數對應一個vtable,這大家都知道,可是這個vtable其實是存儲在對象的內存空間的。問題出來了,如果構造函數是虛的,就需要通過vtable來調用,可是對象還沒有實例化,也就是內存空間還沒有,怎么找vtable呢?所以構造函數不能是虛函數。
2。從使用角度
虛函數主要用於在信息不全的情況下,能使重載的函數得到對應的調用。構造函數本身就是要初始化實例,那使用虛函數也沒有實際意義呀。所以構造函數沒有必要是虛函數。
3。從作用
虛函數的作用在於通過父類的指針或者引用來調用它的時候能夠變成調用子類的那個成員函數。而構造函數是在創建對象時自動調用的,不可能通過父類的指針或者引用去調用,因此也就規定構造函數不能是虛函數。
4。
vtable在構造函數調用后才建立,因而構造函數不可能成為虛函數。在調用構造函數時還不能確定對象的真實類型(因為子類會調父類的構造函數);而且構造函數的作用是提供初始化,在對象生命期只執行一次,不是對象的動態行為,也沒有必要成為虛函數。
-
vector map list set deque stack幾大容器的使用區別
1。vector (連續的空間存儲,可以使用[]操作符)快速的訪問隨機的元素,快速的在末尾插入元素,但是在序列中間歲間的插入,刪除元素要慢,而且如果一開始分配的空間不夠的話,有一個重新分配更大空間,然后拷貝的性能開銷.
2。deque(小片的連續,小片間用鏈表相連,實際上內部有一個map的指針,因為知道類型,所以還是可以使用[],只是速度沒有vector快)快速的訪問隨機的元素,快速的在開始和末尾插入元素,隨機的插入,刪除元素要慢,空間的重新分配要比vector快,重新分配空間后,原有的元素不需要拷貝。對deque的排序操作,可將deque先復制到vector,排序后在復制回deque。
3。list (每個元素間用鏈表相連)訪問隨機元素不如vector快,隨機的插入元素比vector快,對每個元素分配空間,所以不存在空間不夠,重新分配的情況
4。set 內部元素唯一,用一棵平衡樹結構來存儲,因此遍歷的時候就排序了,查找也比較快的哦。
5。map 一對一的映射的結合,key不能重復。
6。stack 適配器,必須結合其他的容器使用,stl中默認的內部容器是deque。先進后出,只有一個出口,不允許遍歷。
7。queue 是受限制的deque,內部容器一般使用list較簡單。先進先出,不允許遍歷。
1.如果我們需要隨機訪問一個容器則vector要比list好得多 。
2.如果我們已知要存儲元素的個數則vector 又是一個比list好的選擇。
3.如果我們需要的不只是在容器兩端插入和刪除元素則list顯然要比vector好
4.除非我們需要在容器首部插入和刪除元素否則vector要比deque好。
5.如果只在容器的首部和尾部插入數據元素,則選擇deque.
6.如果只需要在讀取輸入時在容器的中間位置插入元素,然后需要隨機訪問元素,則可考慮輸入時將元素讀入到一個List容器,接着對此容器重新排序,使其適合順序訪問,然后將排序后的list容器復制到一個vector容器中。
-
2.簡述sizeof和strlen的區別
最常考察的題目之一。主要區別如下:
1)sizeof是一個操作符,strlen是庫函數。
2)sizeof的參數可以是數據的類型,也可以是變量,而strlen只能以結尾為‘\0‘的字符串作參數。
3)編譯器在編譯時就計算出了sizeof的結果。而strlen 函數必須在運行時才能計算出來。並且sizeof計算的是數據類型占內存的大小,而strlen計算的是字符串實際的長度。
4)數組做sizeof的參數不退化,傳遞給strlen就退化為指針了 - 說說strcpy、sprintf與memcpy三個函數
三個函數的功能分別為:
strcpy:實現字符串變量間的拷貝
sprintf:主要實現其他數據類型格式到字符串的轉化Memcpy:主要是內存塊間的拷貝
它們的區別有:
(1)操作對象不同,strcpy的兩個操作對象均為字符串,sprintf的操作源對象可以是多種數據類型,目的操作對象是字符串,memcpy 的兩個對象就是兩個任意可操作的內存地址,並不限於何種數據類型。
(2)執行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
-
16.寫個小程序確定一個數轉化成二進制后是1的位的個數
很久以前就開始流傳的一道微軟面試題。
- (x)
- countx;
- }
-
19.當一個類C 中沒有任何成員變量與成員函數,這時sizeof(C)的值是多少。如果不是零,請解釋一下編譯器為什么沒有讓它為零。
一個空類對象的大小是1byte。這是被編譯器安插進去的一個字節,這樣就使得這個空類的兩個實例得以在內存中配置獨一無二的地址。
-
20.用變量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-h中的一個或者幾個,答案如下:
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
-
24. 虛擬函數與普通成員函數的區別?內聯函數和構造函數能否為虛擬函數?
區別:虛擬函數有virtual關鍵字,有虛擬指針和虛函數表,虛擬指針就是虛擬函數的接口,而普通成員函數沒有。內聯函數和構造函數不能為虛擬函數。
-
1.面向對象
面向對象的三大特性:封裝、繼承、多態。
類和對象:類由數據成員和成員函數構成,代表抽象派,玩的就是概念,某種意義上來說是一種行為藝術;而對象是具體的,比如說過年回家和老爹下中國象棋,發現棋盤上少了一對‘象’,那是你爸在告訴你該找“對象”了(單身狗表示選擇Go Die)。
封裝:把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的類進行信息隱藏。(C++最大的優點:可以隱藏代碼的實現細節,使得代碼更模塊化)
繼承:可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展,但是基類的構造函數、復制構造函數、析構函數、賦值運算符不能被派生類繼承。(優點是實可以擴展已存在的代碼模塊類)
多態:一個類實例的相同方法在不同情形有不同表現形式。多態實現的兩種方式:將子類對象的指針賦給父類類型的指針或將一個基類的引用指向它的派生類實例。(其中比較重要的是虛函數的使用以及指針或引用)
this指針:一個對象的this指針並不是對象本身的一部分,不會影響sizeof(對象)的結果。this作用域是在類的內部,當在類的非靜態(前面沒加Static)成員函數中訪問類的非靜態成員的時候,編譯器會自動將對象本身的地址作為一個隱含參數傳遞給函數。也就是說,各成員的訪問均通過this指針進行。(靜態成員是沒有this指針的)
-
10.將“引用”作為函數參數有哪些特點?
<1>傳遞引用給函數和傳遞指針的效果是一樣的
<2>使用引用傳遞函數的參數,在內存中並沒有產生實參的副本,它是直接對實參操作(當參數傳遞的數據較大時,用引用比用一般變量傳遞參數的效率和所占空間都好)
<3>與指針作為函數的參數,需要分配存儲單元,且容易產生錯誤且程序的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的地址作為實參,而引用更容易使用、更清晰
- 14.“引用”與指針的區別是什么?
指針通過某個指針變量指向一個對象后,對它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。此外,就是上面提到的對函數傳引用和指針的區別。 - 20.重載(overload)和重寫(overried,有的書也叫做“覆蓋”)的區別?
從定義上來說:
重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
重寫:是指子類重新定義父類虛函數的方法。
從實現原理上來說:
重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然后這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。如,有兩個同名函數:function func(p:integer):integer;和function func(p:string):integer;。那么編譯器做過修飾后的函數名稱可能是這樣的:int_func、str_func。對於這兩個函數的調用,在編譯期間就已經確定了,是靜態的。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態無關!
重寫:和多態真正相關。當子類重新定義了父類的虛函數后,父類指針根據賦給它的不同的子類指針,動態的調用屬於子類的該函數,這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。因此,這樣的函數地址是在運行期綁定的(晚綁定) - 27. main 函數執行以前,還會執行什么代碼?
答案:全局對象的構造函數會在main 函數之前執行。 - 35.簡述數組與指針的區別?
數組要么在靜態存儲區被創建(如全局數組),要么在棧上被創建。指針可以隨時指向任意類型的內存塊。
(1)修改內容上的差別
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 編譯器不能發現該錯誤,運行時錯誤
(2) 用運算符sizeof 可以計算出數組的容量(字節數)。sizeof(p),p 為指針得到的是一個指針變量的字節數,而不是p所指的內存容量。C++/C 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。注意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指針。
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字節
cout<< sizeof(p) << endl; // 4 字節
計算數組和指針的內存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字節而不是100 字節
} - 38.如何打印出當前源文件的文件名以及源文件的當前行號?
答案:
cout << __FILE__ ;
cout<<__LINE__ ;
__FILE__和__LINE__是系統預定義宏,這種宏並不是在某個文件中定義的,而是由編譯器定義的。 -
40.如何判斷一段程序是由C 編譯程序還是由C++編譯程序編譯的?
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
- (3)已知兩個鏈表head1 和head2 各自有序,請把它們合並成一個鏈表依然有序,這次要求用遞歸方法進行。 (Autodesk)
- 合並兩個鏈表,非遞歸實現
-
已知String類定義如下:
class String
{
public:
String(const char *str = NULL); // 通用構造函數
String(const String &another); // 拷貝構造函數
~ String(); // 析構函數
String & operater =(const String &rhs); // 賦值函數
private:
char *m_data; // 用於保存字符串
};
String::~String() { delete []m_data ; } String& String::operator =(const String &rhs) { if ( this == &rhs) return *this ; delete []m_data; //刪除原來的數據,新開一塊內存 m_data = new char[strlen(rhs.m_data) + 1]; strcpy(m_data,rhs.m_data); return *this ; } String::String(const String &another) { m_data = new char[strlen(another.m_data) + 1]; strcpy(m_data,another.m_data); } String::String(const char *str) { if ( str == NULL ) //strlen在參數為NULL時會拋異常才會有這步判斷 { m_data = new char[1] ; m_data[0] = '/0' ; } else { m_data = new char[strlen(str) + 1]; strcpy(m_data,str); } }
- 52.#include<file.h> 與 #include "file.h"的區別?
答:前者是從Standard Library的路徑尋找和引用file.h,而后者是從當前工作路徑搜尋並引用file.h。 -
58.全局變量和局部變量有什么區別?是怎么實現的?操作系統和編譯器是怎么知道的?
全局變量和局部變量的區別主要在於生存周期不同,全局變量在整個程序生成期間可見,局部變量在自己的作用域內可見。全局變量的內存分配是靜態的,位於PE文件在數據區,在main()前由C、C++運行期函數初始化,如果沒有初值,會被初始化為0。局部變量的內存分配是動態的,位於線程堆棧中。如果沒有初始化的,初值視當前內存內的值而定。
操作系統和編譯器從定義變量為變量分配內存時,從變量的定義和存儲區域來分別局部變量和全局變量
-
二、什么函數不能聲明為虛函數:
一個類中將所有的成員函數都盡可能地設置為虛函數總是有益的。
設置虛函數須注意:
1:只有類的成員函數才能說明為虛函數;
2:靜態成員函數不能是虛函數;
3:內聯函數不能為虛函數;
4:構造函數不能是虛函數;
5:析構函數可以是虛函數,而且通常聲明為虛函數。 -
77.不能做switch()的參數類型是?switch的參數不能為實型
- 82.do……while和while……do有什么區別?
答 、前一個循環一遍再判斷,后一個判斷以后再循環 - 89.對於一個頻繁使用的短小函數,在C語言中應用什么實現,在C++中應用什么實現?
答 、c用宏定義,c++用inline -
97.關鍵字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;
} - 102.編寫算法,從10億個浮點數當中,選出其中最大的10000個
用外部排序,在《數據結構》書上有
《計算方法導論》在找到第n大的數的算法上加工 (注意:先將數據進行分割成數據量小的一些文件,如1000000個數據為一個文件,然后將每個文件數據進行排序,用快速排序法排序,然后使用K路合並法將其合並到一個文件下,取出排序好的最大的10000個數據) - 字符串的逆序
-
#include <iostream> #include <string> using namespace std; int main() { string s, r; // 聲明字符串 cin >> s; // 輸入字符串 for (int i = 0; i < s.length (); i++) { r = s [i] + r; } cout << r; // 輸出字符串 return 0; }
-
#include <cstring> using namespace std; int main() { // 輸入字符串。 char s [300]; scanf("%s",s); for (int i = 0; i < strlen(s); i++) { cout << *(s + strlen(s) - i - 1); } cout << endl; return 0; }
- 還可以用棧進行實現
-