c++面試

1.new、delete、malloc、free關係
    delete會調用對象的析構函數,和new對應free只會釋放內存,new調用構造函數。malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。它們都可用於申請動態內存和釋放內存。對於非內部數據類型的對象而言,光用maloc/free無法滿足動態對象的要求。對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數。

 

2.與 delete []區別
  delete只會調用一次析構函數,而delete[]會調用每一個成員的析構函數。

 

3.有哪些性質(面向對象特點)
 封裝,繼承和多態。

 

4.子類析構時要調用父類的析構函數嗎?
   析構函數調用的次序是先派生類的析構後基類的析構,也就是說在基類的的析構調用的時候,派生類的信息已經全部銷燬了。定義一個對象時先調用基類的構造函數、然後調用派生類的構造函數;析構的時候恰好相反:先調用派生類的析構函數、然後調用基類的析構函數

 

5. C++中的class和struct的區別

從語法上,在C++中(只討論C++中)。class和struct做類型定義時只有兩點區別:
(一)默認繼承權限。如果不明確指定,來自class的繼承按照private繼承處理,來自struct的繼承按照public繼承處理;
(二)成員的默認訪問權限。class的成員默認是private權限,struct默認是public權限。
除了這兩點,class和struct基本就是一個東西。語法上沒有任何其它區別。

 

6.求下面函數的返回值(微軟)
int func(x)
   {
       int countx = 0;
      while(x)
      {
          countx ++;
          x = x&(x-1);
       }
      return countx;
  } 

假定x = 9999。 答案:8

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

 

7:什麼是“引用”?申明和使用“引用”要注意哪些問題?

  引用就是某個目標變量的“別名”(alias),對應用的操作與對變量直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標變量名的一個別名,它本身不是一種數據類型,因此引用本身不佔存儲單元,系統也不給引用分配存儲單元。

 

8:“引用”與多態的關係?

  引用是除指針外另一個可以產生多態效果的手段。這意味着,一個基類的引用可以指向它的派生類實例


 

a)

#include <stdio.h>
union
{
int i;
char x[2];
}a;


void main()
{
a.x[0] = 10; //2進制就是0000 1010(0AH)
a.x[1] = 1 ; //2進制就是 0000 0001(01H)
printf("%d",a.i); //結果就是 0000 0001 0000 1010=266
}
答案:266 (低位低地址,高位高地址,內存佔用情況是Ox010A)

b)

main()
     {
          union

{                   /*定義一個聯合*/
               int i;
               struct

       {             /*在聯合中定義一個結構*/
                    char first;
                    char second;
               }half;
          }number;


          number.i=0x4241;         /*聯合成員賦值*/
          printf("%c%cn", number.half.first, mumber.half.second);
          number.half.first='a';   /*聯合中結構成員賦值*/
          number.half.second='b';
          printf("%xn", number.i);
          getch();
     }
答案: AB   (0x41對應'A',是低位;Ox42對應'B',是高位)

       6261 (number.i和number.half共用一塊地址空間)

 

9:多態的作用?
主要是兩個:

1. 隱藏實現細節,使得代碼能夠模塊化;擴展代碼模塊,實現代碼重用;

2. 接口重用:爲了類在繼承和派生的時候,保證使用家族中任一類的實例的某一屬性時的正確調用。

12. 描述內存分配方式以及它們的區別?

1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。
2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集。
3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程序員自己負責在何時用free 或delete 釋放內存。動態內存的生存期由程序員決定,使用非常靈活,但問題也最多。


BOOL :    if ( !a ) or if(a)
int :     if ( a == 0)
float :             if ( a < 0.00001&& a >-0.00001)
pointer : if ( a != NULL) or if(a == NULL)

 

9:請說出const與#define 相比,有何優點?
答案:

const作用:定義常量、修飾函數參數、修飾函數返回值三個作用。被const修飾的東西都受到強制保護,可以預防意外的變動,能提高程序的健壯性。

   1) const 常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查。而對後者只進行字符替換,沒有類型安全檢查,並且在字符替換可能會產生意料不到的錯誤。
   2) 有些集成化的調試工具可以對const 常量進行調試,但是不能對宏常量進行調試。

 

15:簡述數組與指針的區別?

   數組要麼在靜態存儲區被創建(如全局數組),要麼在棧上被創建。指針可以隨時指向任意類型的內存塊。


 

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

 

17.求出兩個數中的較大值
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


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

 

20:堆棧溢出一般是由什麼原因導致的?
答 、1.沒有回收垃圾資源

      2.層次太深的遞歸調用

 

21:什麼函數不能聲明爲虛函數?
答 、constructor

 

22.用兩個棧實現一個隊列的功能?要求給出算法和思路!
答 、設2個棧爲A,B, 一開始均爲空.

    入隊:

     將新元素push入棧A;

   出隊:

    (1)判斷棧B是否爲空;

    (2)如果不爲空,則將棧A中所有元素依次pop出並push到棧B;

    (3)將棧B的棧頂元素pop出;

 

23.寫一個“標準”宏MIN,這個宏輸入兩個參數並返回較小的一個。
 

#define MIN(A,B) ((A) <= (B)? (A) : (B))

 

24.交換兩個數的宏定義
 

交換兩個參數值的宏定義爲:. #define SWAP(a,b) (a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);

 

25.用變量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

 


 26:關鍵字static的作用是什麼?

 

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

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

2). 在模塊內(但在函數體外),一個被聲明爲靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。

3). 在模塊內,一個被聲明爲靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用。

 

27:.下面的代碼輸出是什麼,爲什麼?
 

void foo(void)

{

unsigned int a = 6;

int b = -20;

(a+b > 6) puts("> 6") : puts("<= 6");

這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換爲無符號類型。因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大於6

 

28:.C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什麼?

int a = 5, b = 7, c;

c = a+++b;

因此, 這段代碼持行後a = 6, b = 7, c = 12。

 


29:.用遞歸算法判斷數組a[N]是否爲一個遞增數組。

遞歸的方法,記錄當前最大的,並且判斷當前的是否比這個還大,大則繼續,否則返回false結束:

bool fun( int a[], int n )

{

if( n= =1 )

return true;

if( n= =2 )

return a[n-1] >= a[n-2];

return fun( a,n-1) && ( a[n-1] >= a[n-2] );

}


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


       char a[] = "hello world";
       char *p = a;
       cout<< sizeof(a) << endl; // 12 字節,結束符號'/0'也要計算在裏面
       cout<< sizeof(p) << endl; // 4 字節
    計算數組和指針的內存容量

    注意當數組作爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。
      void Func(char a[100])
      {
           cout<< sizeof(a) << endl; // 4 字節而不是100 字節
      } 

 


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

 

20:引用”與指針的區別是什麼?
   指針通過某個指針變量指向一個對象後,對它所指向的變量間接操作。程序中使用指針,程序的可讀性差;而引用本身就是目標變量的別名,對引用的操作就是對目標變量的操作。

 

21.結構與聯合有和區別?

      (1). 結構和聯合都是由多個不同的數據類型成員組成, 但在任何同一時刻, 聯合中只存放了一個被選中的成員(所有成員共用一塊地址空間), 而結構的所有成員都存在(不同成員的存放地址不同)。
   (2). 對於聯合的不同成員賦值, 將會對其它成員重寫, 原來成員的值就不存在了, 而對於結構的不同成員賦值是互不影響的。

 

22.     析構函數和虛函數的用法和作用

析構函數是在對象生存期結束時自動調用的函數,用來釋放在構造函數分配的內存。

虛函數是指被關鍵字virtual說明的函數,作用是使用C++語言的多態特性

 

23:  Windows程序的入口是哪裏?寫出Windows消息機制的流程

Windows程序的入口是WinMain()函數。

              

Windows應用程序消息處理機制:

A. 操作系統接收應用程序的窗口消息,將消息投遞到該應用程序的消息隊列中

B. 應用程序在消息循環中調用GetMessage函數從消息隊列中取出一條一條的消息,取出消息後,應用程序可以對消息進行一些預處理。

C. 應用程序調用DispatchMessage,將消息回傳給操作系統。

D. 系統利用WNDCLASS結構體的lpfnWndProc成員保存的窗口過程函數的指針調用窗口過程,對消息進行處理。

 

宏與內聯函數的區別

    內聯函數和宏都是在程序出現的地方展開,內聯函數不是通過函數調用實現的,是在調用該函數的程序處將它展開(在編譯期間完成的);宏同樣是;  

   不同的是:內聯函數可以在編譯期間完成諸如類型檢測,語句是否正確等編譯功能;宏就不具有這樣的功能,而且宏展開的時間和內聯函數也是不同的(在運行期間展開)

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

一.如何判斷一個單鏈表是有環的?(注意不能用標誌位,最多隻能用兩個額外指針)
struct node { char val; node* next;}
         bool check(const node* head) {} //return false : 無環;true: 有環一種O(n)的辦法就是(搞兩個指針,一個每次遞增一步,一個每次遞增兩步,如果有環的話兩者必然重合,反之亦然):
bool check(const node* head)
{
    if(head==NULL)  return false;
    node *low=head, *fast=head->next;
    while(fast!=NULL && fast->next!=NULL)
    {
        low=low->next;
        fast=fast->next->next;
        if(low==fast) return true;
    }
    return false;
}


二.指針找錯題
分析這些面試題,本身包含很強的趣味性;而作爲一名研發人員,通過對這些面試題的深入剖析則可進一步增強自身的內功。
  2.找錯題

試題1:
以下是引用片段:
void test1()  //數組越界
  {
  char string[10];
  char* str1 = "0123456789";
  strcpy( string, str1 );
  }
  試題2: 
以下是引用片段:
 void test2()
  {
  char string[10], str1[10];
  int i;
  for(i=0; i<10; i++)
  {
  str1= 'a';
  }
  strcpy( string, str1 );
  }
  試題3:  
以下是引用片段:
void test3(char* str1)
  {
  char string[10];
  if( strlen( str1 ) <= 10 )
  {
  strcpy( string, str1 );
  }
  }
  解答:
  試題1字符串str1需要11個字節才能存放下(包括末尾的’/0’),而string只有10個字節的空間,strcpy會導致數組越界;對試題2,如果面試者指出字符數組str1不能在數組內結束可以給3分;如果面試者指出strcpy(string,str1)調用使得從 str1內存起復制到string內存起所複製的字節數具有不確定性可以給7分,在此基礎上指出庫函數strcpy工作方式的給10分;
對試題3,if(strlen(str1) <= 10)應改爲if(strlen(str1) <10),因爲strlen的結果未統計’/0’所佔用的1個字節。剖析:考查對基本功的掌握
  (1)字符串以’/0’結尾;
  (2)對數組越界把握的敏感度;
  (3)庫函數strcpy的工作方式,


試題4: void GetMemory( char *p )
  {
  p = (char *) malloc( 100 );
  }
  void Test( void )
  {
  char *str = NULL;
  GetMemory( str );
  strcpy( str, "hello world" );
  printf( str );
   }
  試題5:   char *GetMemory( void )
  {
  char p[] = "hello world";
  return p;
  }
  void Test( void )
  {
  char *str = NULL;
  str = GetMemory();
  printf( str );
  }
  試題6:void GetMemory( char **p, int num )
  {
  *p = (char *) malloc( num );
  }
  void Test( void )
  {
  char *str = NULL;
  GetMemory( &str, 100 );
  strcpy( str, "hello" );
  printf( str );
  }
  試題7:以下是引用片段:
 void Test( void )
  {
  char *str = (char *) malloc( 100 );
  strcpy( str, "hello" );
  free( str );
  ... //省略的其它語句
  }
  解答:試題4傳入中GetMemory( char *p )函數的形參爲字符串指針,在函數內部修改形參並不能真正的改變傳入形參的值,執行完
  char *str = NULL;
  GetMemory( str );
  後的str仍然爲NULL;試題5中
  char p[] = "hello world";
  return p;
  的p[]數組爲函數內的局部自動變量,在函數返回後,內存已經被釋放。這是許多程序員常犯的錯誤,其根源在於不理解變量的生存期。
  試題6的GetMemory避免了試題4的問題,傳入GetMemory的參數爲字符串指針的指針,但是在GetMemory中執行申請內存及賦值語句 tiffanybracelets
  *p = (char *) malloc( num );
  後未判斷內存是否申請成功,應加上:
  if ( *p == NULL )
  {
  ...//進行申請內存失敗處理

  }
  試題7存在與試題6同樣的問題,在執行
  char *str = (char *) malloc(100);
  後未進行內存是否申請成功的判斷;另外,在free(str)後未置str爲空,導致可能變成一個“野”指針,應加上:
  str = NULL;
  試題6的Test函數中也未對malloc的內存進行釋放。
  剖析:
  試題4~7考查面試者對內存操作的理解程度,基本功紮實的面試者一般都能正確的回答其中50~60的錯誤。但是要完全解答正確,卻也絕非易事。

  對內存操作的考查主要集中在:
  (1)指針的理解;
  (2)變量的生存期及作用範圍;
  (3)良好的動態內存申請和釋放習慣。
  再看看下面的一段程序有什麼錯誤:  
以下是引用片段:
swap( int* p1,int* p2 )
  {
  int *p;
  *p = *p1;
  *p1 = *p2;
  *p2 = *p;
  }
  在swap函數中,p是一個“野”指針,有可能指向系統區,導致程序運行的崩潰。在VC++中DEBUG運行時提示錯誤“Access Violation”。該程序應該改爲
以下是引用片段:
swap( int* p1,int* p2 )
  {
  int p;
  p = *p1;
  *p1 = *p2;
  *p2 = p;
  }

 101.用遞歸算法判斷數組a[N]是否爲一個遞增數組。

遞歸的方法,記錄當前最大的,並且判斷當前的是否比這個還大,大則繼續,否則返回false結束:

bool fun( int a[], int n )

{

if( n= =1 )

return true;

if( n= =2 )

return a[n-1] >= a[n-2];

return fun( a,n-1) && ( a[n-1] >= a[n-2] );

}

 

如何定義和實現一個類的成員函數爲回調函數?

typedef   void   (*FunPtr)(void);  
  class   A  
  {  
     public   :  
     static   void     callBackFun(void   )  
    {  
      cout<<"callBackFun   "<<endl;  
    }  
  };

void   Funtype(   FunPtr   p)  
  {  
      p();  
  } 


void     main(void)  
  {  
      Funtype(A::callBackFun);  
  }

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章