天天看點

經典基礎C++筆試題(附答案)

一、選擇題

1. 若用數組名作為函數調用時的實參,則實際上傳遞給形參的是( A )

A. 數組首位址

B. 數組的第一個元素值

C. 數組中全部元素的值

D. 數組元素的個數

2. 有關函數重載的正确說法是( B )

A. 函數名不同,但參數的個數和類型相同

B. 函數名相同,但參數的個數不同或參數的類型不同

C. 函數名相同,參數的個數和類型相同

D. 函數名相同,函數的傳回值不同,而與函數的參數和類型無關

3. 已知int a[3][2]={3,2,1}; 則表達式“a[0][0]/a[0][1]/a[0][2]”的值是(B)

A. 0.166667

B. 1

C. 0

D.  錯誤的表達式

4. 要禁止修改指針p本身,又要禁止修改p所指向的資料,這樣的指針應定義為( D )

A. const char *p="ABCD";

B. char const *p="ABCD";

C. char *const p="ABCD";

D. const char * const p="ABCD";

5. 對靜态成員的不正确描述是( C  )

A. 靜态成員不屬于對象,是類的共享成員    

B. 靜态資料成員要在類外定義和初始化

C. 調用靜态成員函數時要通過類或對象激活,是以靜态成員函數擁有this指針

D. 非靜态成員函數也可以操作靜态資料成員

6. 下面函數原型聲明中,( B  )聲明了fun為純虛函數

A. void fun()=0;        

B. virtual void fun()=0;        

C. virtual void fun();      

D. virtual void fun(){};

7. 在排序方法中,關鍵碼比較次數與記錄地初始排列無關的是( D )

A. Shell排序    

B. 歸并排序      

C. 直接插入排序        

D. 選擇排序

8. 一個棧的入棧序列是A,B,C,D,E,則棧的不可能的輸出序列是( C )

A. EDCBA;  

B. DECBA;    

C. DCEAB;  

D. ABCDE

二、填空題

1.求下面函數的傳回值 _____

int func(x)
{
int countx = 0;
while(x)
{
countx ++;
x = x&(x-1);
}
return countx;
}      

假定x = 9999。 答案:8 

思路:将x轉化為2進制,看含有的1的個數。

2. 以下三條輸出語句分别輸出什麼 _____

char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc"; 
const char str4[] = "abc"; 
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 輸出什麼?
cout << boolalpha << ( str3==str4 ) << endl; // 輸出什麼?
cout << boolalpha << ( str5==str6 ) << endl; // 輸出什麼?      

答:分别輸出false,false,true。str1和str2都是字元數組,每個都有其自己的存儲區,它們的值則是各存儲區首位址,不等;str3和str4同上,隻是按const語義,它們所指向的資料區不能修改。str5和str6并非數組而是字元指針,并不配置設定存儲區,其後的“abc”以常量形式存于靜态資料區,而它們自己僅是指向該區首位址的指針,相等。

3.請給出如下程式的結果

int a = 3;
int b = a << 3;      

3*2^3 = 24

a = __3__ , b = __24__ 。

4.請給出如下程式的結果

#define MAX_NUM 100+200
int nTemp = MAX_NUM*10;      

則 Temp = __2100__ 。

5. 程式的運作結果為___

void Func(char str[100])
{
  printf("%d\n", sizeof(str));
}      

數組作為函數的形參退化為指針

答:4

分析: 指針長度

6. 程式的運作結果為____

int sum(int a)
{
auto int c=0;
static int b=3;
c+=1;
b+=2;
return(a+b+c);
}
void main()
{
    int I;
    int a=2;
    for(I=0;I<5;I++)
    {
        printf("%d,", sum(a));
    }
}      

// static會儲存上次結果,記住這一點,剩下的自己寫

輸出:8,10,12,14,16,

7. 程式的運作結果為____

unsigned short array[]={1,2,3,4,5,6,7}; 
int i = 3; 
*(array + i) = ?      

答: 4

8.寫出以下程式的執行結果:

static char *s[]= {"black","white","yellow","violet"};
char **ptr[]={s+3,s+2,s+1,s},***p=ptr;
    
**++p;
    
printf("%s\n",*++*++p+3);      

思路:*++(*++p); // s+1 -> s+2 即 "white" -> "yellow"

答:low

分析:注意指針++的差別,以及*p+1與*(p+1)的差別

三、問答題

1、頭檔案中的 ifndef/define/endif 幹什麼用?

答:防止該頭檔案被重複引用。

2. C++中為什麼用模闆類。

答:(1)可用來建立動态增長和減小的資料結構

  (2)它是類型無關的,是以具有很高的可複用性。

  (3)它在編譯時而不是運作時檢查資料類型,保證了類型安全

  (4)它是平台無關的,可移植性

  (5)可用于基本資料類型

3. 說一說C與C++的記憶體配置設定方式?

(1)從靜态存儲區域配置設定。記憶體在程式編譯的時候就已經配置設定好,這塊記憶體在程式的整個運作期間都存在,如全局變量,static變量。

(2)在棧上建立。在執行函數時,函數内局部變量的存儲單元都可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧記憶體配置設定運算内置于處理器的指令集中,效率很高,但是配置設定的記憶體容量有限。

(3)從堆上配置設定(動态記憶體配置設定)程式在運作的時候用malloc或new申請任意多少的記憶體,程式員負責在何時用free或delete釋放記憶體。動态記憶體的生存期自己決定,使用非常靈活。

4. 關鍵字static的作用是什麼?

這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:

(1)在函數體,一個被聲明為靜态的變量在這一函數被調用過程中維持其值不變。

(2)在子產品内(但在函數體外),一個被聲明為靜态的變量可以被子產品内所用函數通路,但不能被子產品外其它函數通路。它是一個本地的全局變量。

(3)在子產品内,一個被聲明為靜态的函數隻可被這一子產品内的其它函數調用。那就是,這個函數被限制在聲明它的子產品的本地範圍内使用。

大多數應試者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化資料和代碼範圍的好處和重要性。

5. 面向對象的三個基本特征,并簡單叙述之?

(1)封裝:将客觀事物抽象成類,每個類對自身的資料和方法實行protection(private, protected,public)

(2) 繼承:廣義的繼承有三種實作形式:實作繼承(指使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實作代碼)、接口繼承 (僅使用屬性和方法,實作滞後到子類實作)。前兩種(類繼承)和後一種(對象組合=>接口繼承以及純虛函數)構成了功能複用的兩種方式。

(3)多态:是将父對象設定成為和一個或更多的他的子對象相等的技術,指派之後,父對象就可以根據目前指派給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許将子類類型的指針指派給父類類型的指針。

6. 局部變量能否和全局變量重名?  

 答:能,局部會屏蔽全局。要用全局變量,需要使用"::" ;局部變量可以與全局變量同名,在函數内引用這個變量時,會用到同名的局部變量,而不會用到全局變量。對于有些編譯器而言,在同一個函數内可以定義多個同名的局部變量,比如在兩個循環體内都定義一個同名的局部變量,而那個局部變量的作用域就在那個循環體内。

7. 重載(overload)和重寫(overried,有的書也叫做“覆寫”)的差別?

答:從定義上來說:

重載:是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。

重寫:是指子類重新定義複類虛函數的方法。

從實作原理上來說:

重載:編譯器根據函數不同的參數表,對同名函數的名稱做修飾,然後這些同名函數就成了不同的函數。

重寫:當子類重新定義了父類的虛函數後,父類指針根據賦給它的不同的子類指針,動态的調用屬于子類的該函數,這樣的函數調用在編譯期間是無法确定的(調用的子類的虛函數的位址無法給出)。

8. new delete 與malloc free 的聯系與差別?

答案:都是在堆(heap)上進行動态的記憶體操作。用malloc函數需要指定記憶體配置設定的位元組數并且不能初始化對象,new 會自動調用對象的構造函數。delete 會調用對象的destructor,而free 不會調用對象的destructor.

9. 什麼是死鎖?其條件是什麼?怎樣避免死鎖?

答案:死鎖的概念:在兩個或多個并發程序中,如果每個程序持有某種資源而又都等待别的程序釋放它或它們現在保持着的資源,在未改變這種狀态之前都不能向前推進,稱這一組程序産生了死鎖。通俗地講,就是兩個或多個程序被無限期地阻塞、互相等待的一種狀态。

死鎖産生的原因主要是:? 系統資源不足;? 程序推進順序非法。

産生死鎖的必要條件:

(1)互斥(mutualexclusion),一個資源每次隻能被一個程序使用;

(2)不可搶占(nopreemption),程序已獲得的資源,在未使用完之前,不能強行剝奪;

(3)占有并等待(hold andwait),一個程序因請求資源而阻塞時,對已獲得的資源保持不放;

(4)環形等待(circularwait),若幹程序之間形成一種首尾相接的循環等待資源關系。

這四個條件是死鎖的必要條件,隻要系統發生死鎖,這些條件必然成立,而隻要上述條件之一不滿足,就不會發生死鎖。

死鎖的解除與預防:了解了死鎖的原因,尤其是産生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。是以,在系統設計、程序排程等方面注意如何不讓這四個必要條件成立,如何确定資源的合理配置設定算法,避免程序永久占據系統資源。此外,也要防止程序在處于等待狀态的情況下占用資源。是以,對資源的配置設定要給予合理的規劃。

死鎖的處理政策:鴕鳥政策、預防政策、避免政策、檢測與恢複政策。

10. ARP 是位址解析協定,簡單語言解釋一下工作原理。

答:

( 1 )首先,每個主機都會在自己的 ARP 緩沖區中建立一個 ARP 清單,以表示 IP 位址和 MAC 位址之間的對應關系。

( 2 )當源主機要發送資料時,首先檢查 ARP 清單中是否有對應 IP 位址的目的主機的 MAC 位址,如果有,則直接發送資料,如果沒有,就向本網段的所有主機發送 ARP 資料包,該資料包包括的内容有:源主機 IP 位址,源主機 MAC 位址,目的主機的 IP 位址。

( 3 )當本網絡的所有主機收到該 ARP 資料包時,首先檢查資料包中的 IP 位址是否是自己的 IP 位址,如果不是,則忽略該資料包,如果是,則首先從資料包中取出源主機的 IP 和 MAC 位址寫入到 ARP 清單中,如果已經存在,則覆寫,然後将自己的 MAC 位址寫入 ARP 響應包中,告訴源主機自己是它想要找的 MAC 位址。

( 4 )源主機收到 ARP 響應包後。将目的主機的 IP 和 MAC 位址寫入 ARP 清單,并利用此資訊發送資料。如果源主機一直沒有收到 ARP 響應資料包,表示 ARP 查詢失敗。

廣播發送 ARP 請求,單點傳播發送 ARP 響應。

11、請定義一個宏,比較兩個數a、b的大小,不能使用大于、小于、if語句

#define Max(a,b)  ( a/b)?a:b

12. 分别寫出BOOL,int,float,指針類型的變量a 與“零”的比較語句。

答案:

BOOL :    if ( !a ) or if(a)
int :     if ( a == 0)
float :   const EXPRESSION EXP = 0.000001
          if ( a < EXP && a >-EXP)
pointer : if ( a != NULL) or if(a == NULL)      

13. 已知strcpy的函數原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字元串,strSrc 是源字元串。不調用C++/C 的字元串庫函數,請編寫函數 strcpy。

/*
編寫strcpy函數(10分)
已知strcpy函數的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字元串,strSrc是源字元串。
(1)不調用C++/C的字元串庫函數,請編寫函數 strcpy
(2)strcpy能把strSrc的内容複制到strDest,為什麼還要char * 類型的傳回值?
答:為了 實作鍊式表達式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );
*/
#include <assert.h>
#include <stdio.h>
char*strcpy(char*strDest, constchar*strSrc)
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char* address = strDest; // 2分
while( (*strDest++=*strSrc++) !='\0' )// 2分
NULL; 
return address ; // 2分
}      

14. 已知連結清單的頭結點head(有内容),寫一個函數把這個連結清單逆序

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 ;// 頭結點的next指向NULL
while ( p3 != NULL )
{
p2->next = p1 ;// 逆序
p1 = p2 ;// 三個指針分别後移一位
p2 = p3 ;
p3 = p3->next ;
}
p2->next = p1 ;
head = p2 ;// 作為新的頭結點
return head ;
}      

15. 寫個函數交換兩個指針。

答案 : 

#include 
using namespace std;
void ex(char **a,char **b)
{
char *c;
c=*a;
*a=*b;
*b=c;
}
int main()
{
char *pt=”pt\n”;
char *pt_another=”pt_another\n”;
cout<<pt<<pt_another;
ex(&pt,&pt_another);
cout<<pt<<pt_another;
system(“pause”);
}