C/C++ 筆試、面試題目大匯總(一)

來源:互聯網
上載者:User
一位同學整理的常見筆試/面試題目,答案僅供參考,不代表本人觀點。
這個東西有些參考價值,和同學討論一下發現還是有些錯誤,

1.已知strcpy函數的原型是:
        char * strcpy(char * strDest,const char * strSrc);
    1.不調用庫函數,實現strcpy函數。
    2.解釋為什麼要返回char *。
解說:
    1.strcpy的實現代碼
        char * strcpy(char * strDest,const char * strSrc)
        {
                if ((strDest==NULL)||(strSrc==NULL))    //[1]
                        throw "Invalid argument(s)";             //[2]
                char * strDestCopy=strDest;                    //[3]
                while ((*strDest++=*strSrc++)!='/0');       //[4]
                return strDestCopy;
        }
    錯誤的做法:
    [1]
    (A)不檢查指標的有效性,說明答題者不注重代碼的健壯性。
    (B)檢查指標的有效性時使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),說明答題者對C語言中類型的隱式轉換沒有深刻認識。在本例中char *轉換為bool即是類型隱式轉換,這種功能雖然靈活,但更多的是導致出錯機率增大和維護成本升高。所以C++專門增加了bool、true、false三個關鍵字以提供更安全的條件運算式。
    (C)檢查指標的有效性時使用((strDest==0)||(strSrc==0)),說明答題者不知道使用常量的好處。直接使用字面常量(如本例中的0)會減少程式的可維護性。0雖然簡單,但程式中可能出現很多處對指標的檢查,萬一出現筆誤,編譯器不能發現,產生的程式內含邏輯錯誤,很難排除。而使用NULL代替0,如果出現拼字錯誤,編譯器就會檢查出來。
    [2]
    (A)return new string("Invalid argument(s)");,說明答題者根本不知道傳回值的用途,並且他對記憶體流失也沒有警惕心。從函數中返回函數體內分配的記憶體是十分危險的做法,他把釋放記憶體的義務拋給不知情的調用者,絕大多數情況下,調用者不會釋放記憶體,這導致記憶體流失。
    (B)return 0;,說明答題者沒有掌握異常機制。調用者有可能忘記檢查傳回值,調用者還可能無法檢查傳回值(見後面的鏈式運算式)。妄想讓傳回值肩負返回正確值和異常值的雙重功能,其結果往往是兩種功能都失效。應該以拋出異常來代替傳回值,這樣可以減輕調用者的負擔、使錯誤不會被忽略、增強程式的可維護性。
    [3]
    (A)忘記儲存原始的strDest值,說明答題者邏輯思維不嚴密。
    [4]
    (A)迴圈寫成while (*strDest++=*strSrc++);,同[1](B)。
    (B)迴圈寫成while (*strSrc!='/0') *strDest++=*strSrc++;,說明答題者對邊界條件的檢查不力。迴圈體結束後,strDest字串的末尾沒有正確地加上'/0'。

    2.返回strDest的原始值使函數能夠支援鏈式運算式,增加了函數的“附加值”。同樣功能的函數,如果能合理地提高的可用性,自然就更加理想。
    鏈式運算式的形式如:
        int iLength=strlen(strcpy(strA,strB));
    又如:
        char * strA=strcpy(new char[10],strB);
    返回strSrc的原始值是錯誤的。其一,源字串肯定是已知的,返回它沒有意義。其二,不能支援形如第二例的運算式。其三,為了保護源字串,形參用const限定strSrc所指的內容,把const char *作為char *返回,類型不符,編譯報錯。

    參考文獻:林銳,《高品質程式設計指南--C/C++語言》,北京,電子工業出版社,2002.6。

 

 

2.已知類String 的原型為:
class String
{
public:
String(const char *str = NULL); // 普通建構函式
String(const String &other); // 拷貝建構函式
~ String(void); // 解構函式
String & operate =(const String &other); // 賦值函數
private:
char *m_data; // 用於儲存字串
};
請編寫String 的上述4 個函數。
答案:
String::String(const char *str)
{
if ( str == NULL ) //strlen在參數為NULL時會拋異常才會有這步判斷
{
m_data = new char[1] ;
m_data[0] = '' ;
}
else
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str);
}
}
String::String(const String &other)
{
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
}
String & String::operator =(const String &other)
{
if ( this == &other)
return *this ;
delete []m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data,other.m_data);
return *this ;
}
String::~ String(void)
{
delete []m_data ;
}

3.簡答
3.1 標頭檔中的ifndef/define/endif 幹什麼用?
答:防止該標頭檔被重複引用。
3.2#i nclude <filename.h> 和#i nclude “filename.h” 有什麼區別?
答:對於#i nclude <filename.h> ,編譯器從標準庫路徑開始搜尋filename.h
對於#i nclude “filename.h”,編譯器從使用者的工作路徑開始搜尋filename.h
3.3 在C++ 程式中調用被C 編譯器編譯後的函數,為什麼要加extern “C”?
答:C++語言支援函數重載,C 語言不支援函數重載。函數被C++編譯後在庫中的名字與C 語言的不同。假設某個函數的原型為: void foo(int x, int y);
該函數被C 編譯器編譯後在庫中的名字為_foo , 而C++ 編譯器則會產生像_foo_int_int 之類的名字。
C++提供了C 串連交換指定符號extern“C”來解決名字匹配問題。
3.4 一個類有基類、內部有一個其他類的成員對象,建構函式的執行順序是怎樣的。(Autodesk)
答:先執行基類的(如果基類當中有虛基類,要先執行虛基類的,其他基類則按照聲明衍生類別時的順序依次執行),再執行成員對象的,最後執行自己的。
3.5 請描述一個你熟悉的設計模式(Autodesk)
3.6 在UML 中,彙總(aggregation)和組合(composition)有什麼區別 Autodesk)
答案:彙總關係更強,類似於pages 和book 的關係;組合關係要弱,類似於books和bookshelf 的關係。
3.7C#和C++除了文法上的差別以外,有什麼不同的地方?(Autodesk,Microsoft)
答案:(C#我只是瞭解,不是很精通)
(1) c#有垃圾自動回收機制,程式員不用擔心對象的回收。(2)c#嚴禁使用指標,只能處理對象。如果希望使用指標,則僅可在unsafe 程式塊中能使用指標。(3)c#只能單繼承。(4)必須通過類名訪問靜態成員。不能像C++中那樣,通過對象訪問靜態成員。(5)在子類中覆蓋父類的虛函數時必須用關鍵字override,覆蓋父類的方法要用關鍵字new
3.8ADO.net 和ADO 的區別?
答案:實際上除了“能夠讓應用程式處理儲存於DBMS 中的資料“這一基本相似點外,兩者沒有太多共同之處。但是ADO 使用OLE DB 介面並基於微軟的COM 技術,而ADO.NET 擁有自己的ADO.NET 介面並且基於微軟的.NET 體系架構。眾所周知.NET 體系不同於COM 體系,ADO.NET 介面也就完全不同於ADO和OLE DB 介面,這也就是說ADO.NET 和ADO是兩種資料訪問方式。ADO.net 提供對XML 的支援。
3.9 New delete 與malloc free 的區別 ( Autodesk)
答案:用malloc 函數不能初始化對象,new 會調用對象的建構函式。Delete 會調用對象的destructor,而free 不會調用對象的destructor.
3.10 #define DOUBLE(x) x+x (Autodesk)
i = 5*DOUBLE(10); i 是多少?正確的聲明是什嗎?
答案:i 為60。正確的聲明是#define DOUBLE(x) (x+x)
3.11 有哪幾種情況只能用intialization list 而不能用assignment? (Autodesk)
答案:當類中含有const、reference 成員變數;基類的建構函式都需要參數;類中含有其他類的成員對象,而該類的建構函式都需要參數。
3.11 C++是不是型別安全的? (Autodesk)
答案:不是。兩個不同類型的指標之間可以強制轉換。C#是型別安全的。
3.12 main 函數執行以前,還會執行什麼代碼? (Autodesk)
答案:全域對象的建構函式會在main 函數之前執行。
3.13 描述記憶體配置方式以及它們的區別。 (Autodesk , Microsoft)
答案:1) 從靜態儲存地區分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個運行期間都存在。例如全域變數,static 變數。
(2) 在棧上建立。在執行函數時,函數內局部變數的儲存單元都可以在棧上建立,函數執行結束時這些儲存單元自動被釋放。棧記憶體配置運算內建於處理器的指令集。
(3) 從堆上分配,亦稱動態記憶體分配。程式在啟動並執行時候用malloc 或new 申請任意多少的記憶體,程式員自己負責在何時用free 或delete 釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。
3.14 什麼是虛擬儲存空間?virtual memory 怎樣映射到physical memory?頁面替換演算法有哪些? (Microsoft)
見作業系統 p238 頁。掌握的頁面替換演算法NRU,FIFO,第二次機會頁面替換演算法,LRU
3.15 有四個同樣的容器,裡面裝滿了粒數相同的藥丸,正常藥丸的品質為m,變質藥丸的品質為m+1,現在已知這四個容器中,有一個裝的全是變質藥丸,用電子秤只稱一次,找出哪個容器裝的是變質藥丸 (Microsoft)
答案:把四個容器依次編號為1、2、3、4,然後從中分別取出1、2、3、4 粒藥丸,稱這10 粒藥丸的品質,如果品質為10m+1,則說明第一個容器裝的是變質藥丸,如果為10m+2 則說明第二個裝的變質藥丸,依次類推。
3.16 比較一下C++中static_cast 和 dynamic_cast 的區別。 (Autodesk)
dynamic_casts在協助你瀏覽繼承層次上是有限制的。它不能被用於缺乏虛函數的類型上,它被用於安全地沿著類的繼承關係向下進行類型轉換。如你想在沒有繼承關係的類型中進行轉換,你可能想到static_cast
3.17 Struct 和class 的區別 (Autodesk)
答案:struct 中成員變數和成員函數預設存取權限是public,class 是private
3.18 當一個類A 中沒有生命任何成員變數與成員函數,這時sizeof(A)的值是多少,如果不是零,請解釋一下編譯器為什麼沒有讓它為零。(Autodesk)
答案:肯定不是零。我舉個反例,如果是零的話,聲明一個class A[10]對象數組,而每一個對象佔用的空間是零,這時就沒辦法區分A[0],A[1]…了
3.19 在8086 彙編下,邏輯地址和物理地址是怎樣轉換的?(Intel)
答案:通用寄存器給出的地址,是段內位移地址,相應段寄存器地址*10H+通用寄存器內地址,就得到了真正要訪問的地址。
3.20 描述一下C++的多態 (microsoft)
答案:C++的多態表現在兩個部分,一個是靜態連編下的函數重載,運算子多載;動態連編下的虛函數、純虛函數(抽象類別)

4.寫出BOOL,int,float,指標類型的變數a 與零的比較語句。
答案:
BOOL : if ( !a )
int : if ( a == 0)
float : const EXPRESSION EXP = 0.000001
if ( a < EXP && a >-EXP)
pointer : if ( a != NULL)

5.請說出const 與#define 相比優點
答案:
(1) const 常量有資料類型,而宏常量沒有資料類型。編譯器可以對前者進行型別安全檢查。而對後者只進行字元替換,沒有型別安全檢查,並且在字元替換可能會產生意料不到的錯誤。
(2) 有些整合化的調試工具可以對const 常量進行調試,但是不能對宏常量進行調試。

6.簡述數組與指標的區別
數組要麼在靜態儲存區被建立(如全域數組),要麼在棧上被建立。指標可以隨時指向任意類型的記憶體塊。
(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 位元組
}

7.類成員函數的重載、覆蓋和隱藏區別
答案:
成員函數被重載的特徵:
(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual 關鍵字可有可無。
覆蓋是指衍生類別函數覆蓋基類函數,特徵是:
(1)不同的範圍(分別位於衍生類別與基類);
(2)函數名字相同;
(3)參數相同;
(4)基類函數必須有virtual 關鍵字。
“隱藏”是指衍生類別的函數屏蔽了與其同名的基類函數,規則如下:
(1)如果衍生類別的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
(2)如果衍生類別的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual 關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)

8.There are two int variables: a and b, don’t use “if”, “? :”, “switch”
or other judgement statements, find out the biggest one of the two
numbers.
答案:( ( a + b ) + abs( a – b ) ) / 2

9.如何列印出當前源檔案的檔案名稱以及源檔案的當前行號?
答案:
cout << __FILE__ ;
cout<<__LINE__ ;
__FILE__和__LINE__是系統預定義宏,這種宏並不是在某個檔案中定義的,而是由編譯器定義的。

10.main 主函數執行完畢後,是否可能會再執行一段代碼,給出說明?
答案:可以,可以用_onexit 註冊一個函數,它會在main 之後執行int fn1(void), fn2(void), fn3(void), fn4 (void);
void main( void )
{
String str("zhanglin");
_onexit( fn1 );
_onexit( fn2 );
_onexit( fn3 );
_onexit( fn4 );
printf( "This is executed first./n" );
}
int fn1()
{
printf( "next./n" );
return 0;
}
int fn2()
{
printf( "executed " );
return 0;
}
int fn3()
{
printf( "is " );
return 0;
}
int fn4()
{
printf( "This " );
return 0;
}
The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.

11.如何判斷一段程式是由C 編譯器還是由C++編譯器編譯的?
答案:
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif

12.檔案中有一組整數,要求排序後輸出到另一個檔案中
答案:
void Order(vector<int> &data) //起泡排序
{
int count = data.size() ;
int tag = false ;
for ( int i = 0 ; i < count ; i++)
{
for ( int j = 0 ; j < count - i - 1 ; j++)
{
if ( data[j] > data[j+1])
{
tag = true ;
int temp = data[j] ;
data[j] = data[j+1] ;
data[j+1] = temp ;
}
}
if ( !tag )
break ;
}
}
void main( void )
{
vector<int>data;
ifstream in("c://data.txt");
if ( !in)
{
cout<<"file error!";
exit(1);
}
int temp;
while (!in.eof())
{
in>>temp;
data.push_back(temp);
}
in.close();
Order(data);
ofstream out("c://result.txt");
if ( !out)
{
cout<<"file error!";
exit(1);
}
for ( i = 0 ; i < data.size() ; i++)
out<<data[i]<<" ";
out.close();
}

13.排序方法比較 (intel)
排序方法 平均時間 最壞時間 輔助儲存
直接插入排序
起泡排序
快速排序
簡單選擇排序
堆排序
歸併排序
基數排序

14.一個鏈表的結點結構
struct Node
{
int data ;
Node *next ;
};
typedef struct Node Node ;
(1)已知鏈表的頭結點head,寫一個函數把這個鏈表逆序 ( Intel)
Node * ReverseList(Node *head) //鏈表逆序
{
if ( head == NULL || head->next == NULL )
return head;
Node *p1 = head ;
Node *p2 = p1->next ;
Node *p3 = p2->next ;
p1->next = NULL ;
while ( p3 != NULL )
{
p2->next = p1 ;
p1 = p2 ;
p2 = p3 ;
p3 = p3->next ;
}
p2->next = p1 ;
head = p2 ;
return head ;
}
(2)已知兩個鏈表head1 和head2 各自有序,請把它們合并成一個鏈表依然有序。
Node * Merge(Node *head1 , Node *head2)
{
if ( head1 == NULL)
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
Node *p1 = NULL;
Node *p2 = NULL;
if ( head1->data < head2->data )
{
head = head1 ;
p1 = head1->next;
p2 = head2 ;
}
else
{
head = head2 ;
p2 = head2->next ;
p1 = head1 ;
}
Node *pcurrent = head ;
while ( p1 != NULL && p2 != NULL)
{
if ( p1->data <= p2->data )
{
pcurrent->next = p1 ;
pcurrent = p1 ;
p1 = p1->next ;
}
else
{
pcurrent->next = p2 ;
pcurrent = p2 ;
p2 = p2->next ;
}
}
if ( p1 != NULL )
pcurrent->next = p1 ;
if ( p2 != NULL )
pcurrent->next = p2 ;
return head ;
}
(2)已知兩個鏈表head1 和head2 各自有序,請把它們合并成一個鏈表依然有序,這次要求用遞迴方法進行。 ( Autodesk)
答案:
Node * MergeRecursive(Node *head1 , Node *head2)
{
if ( head1 == NULL )
return head2 ;
if ( head2 == NULL)
return head1 ;
Node *head = NULL ;
if ( head1->data < head2->data )
{
head = head1 ;
head->next = MergeRecursive(head1->next,head2);
}
else
{
head = head2 ;
head->next = MergeRecursive(head1,head2->next);
}
return head ;
}

15.分析一下這段程式的輸出 (Autodesk)
class B
{
public:
B()
{
cout<<"default constructor"<<endl;
}
~B()
{
cout<<"destructed"<<endl;
}
B(int i):data(i)
{
cout<<"constructed by parameter" << data <<endl;
}
private:
int data;
};
B Play( B b)
{
return b ;
}
int main(int argc, char* argv[])
{
B temp = Play(5);
return 0;
}
請自己執行一下看看。

16.寫一個函數找出一個整數數組中,第二大的數 (microsoft)
答案:
const int MINNUMBER = -32767 ;
int find_sec_max( int data[] , int count) //類似於1 4 4 4這樣的序列將認為1是第二大數
{
int maxnumber = data[0] ;
int sec_max = MINNUMBER ;
for ( int i = 1 ; i < count ; i++)
{
if ( data[i] > maxnumber )
{
sec_max = maxnumber ;
maxnumber = data[i] ;
}
else
{
if ( data[i] > sec_max )
sec_max = data[i] ;
}
}
return sec_max ;
}

17 寫一個在一個字串中尋找一個子串第一個位置的函數
這個題目的一般演算法比較簡單我就不給出了,如果要求高效率的話請參見資料結構中的KMP 演算法,不過在筆試時間有限情況下,寫出那個演算法還是挺難的。

英文題目
1. Introduce yourself in English
2. What is your great advantage you think of yourself?
3. What is your drawback you think of yourself?
Maybe I will feel very tense if I make a speech in front of a lot of people.
4. How do you feel shanghai?

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.