C、c++經典面試題

近期有空消化掉

1      編程基礎

1.1   基本概念

1.         的理解:const char*, char const*, char*const的區別問題幾乎是C++面試中每次    都會有的題目。事實上這個概念誰都有隻是三種聲明方式非常相似很容易記混。 Bjarne在他的The C++ Programming Language裏面給出過一個助記的方法:把一個聲明從右向左讀。 const

char  * constcp; ( * 讀成 pointer to )

cp is a const pointerto char

const char * p;

p is a pointer toconst char;

char const * p;

同上因爲C++裏面沒有const*的運算符,所以const只能屬於前面的類型。

2.         指針c

int *p[n];-----指針數組,每個元素均爲指向整型數據的指針。

int (*)p[n];------p爲指向一維數組的指針,這個一維數組有n個整型數據。

int *p();----------函數帶回指針,指針指向返回的值。

int (*)p();------p爲指向函數的指針。

3.         數組越界問題

下面這個程序執行後會有什麼錯誤或者效果:

 #define MAX 255

int main()

{

   unsigned char A[MAX],i;

   for (i=0;i<=MAX;i++)

       A[i]=i;

}

解答:MAX=255,數組A的下標範圍爲:0..MAX-1,這是其一,其二i循環到255,循環內執行: A[255]=255;這句本身沒有問題,但是返回for (i=0;i<=MAX;i++)語句時,由於unsigned char的取值範圍在(0..255),i++以後i又爲0..無限循環下去.

注:char類型爲一個字節,取值範圍是[-128127]unsigned char [0,255]

4.         strcpy 的根本區別?  C++:memset,memcpy

#include"memory.h"

memset用來對一段內存空間全部設置爲某個字符,一般用在對定義的字符串進行初始化爲‘ '‘\0';例:char a[100];memset(a,'\0', sizeof(a));

memcpy用來做內存拷貝,你可以拿它拷貝任何數據類型的對象,可以指定拷貝的數據長度;例:char a[100],b[50];memcpy(b, a, sizeof(b));注意如用sizeof(a),會造成b的內存地址溢出。

strcpy就只能拷貝字符串了,它遇到'\0'就結束拷貝;例:chara[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串長度(第一個‘\0'之前)是否超過50位,如超過,則會造成b的內存地址溢出。

strcpy

原型:extern char *strcpy(char *dest,char *src);

用法:#include

功能:把src所指由NULL結束的字符串複製到dest所指的數組中。

說明:srcdest所指內存區域不可以重疊且dest必須有足夠的空間來容納src的字符串。

返回指向dest的指針。

memcpy

原型:extern void *memcpy(void *dest, void *src,unsigned int count);

用法:#include

功能:由src所指內存區域複製count個字節到dest所指內存區域。

說明:srcdest所指內存區域不能重疊,函數返回指向dest的指針。

Memset

原型:extern void *memset(void *buffer, char c, intcount);

用法:#include

功能:把buffer所指內存區域的前count個字節設置成字符c

說明:返回指向buffer的指針。

5.         是幹什麼用的ASSERT()

ASSERT()是一個調試程序時經常使用的宏,在程序運行時它計算括號內的表達式,如果表達式爲FALSE (0), 程序將報告錯誤,並終止執行。如果表達式不爲0,則繼續執行後面的語句。這個宏通常原來判斷程序中是否出現了明顯非法的數據,如果出現了終止程序以免導致嚴重後果,同時也便於查找錯誤。例如,變量n在程序中不應該爲0,如果爲0可能導致錯誤,你可以這樣寫程序:

......

ASSERT( n != 0);

k = 10/ n;

......

ASSERT只有在Debug版本中才有效,如果編譯爲Release版本則被忽略。

assert()的功能類似,它是ANSI C標準中規定的函數,它與ASSERT的一個重要區別是可以用在Release版本中。

6.         "pause");系統的暫停程序,按任意鍵繼續,屏幕會打印,"按任意鍵繼續。。。。。" 省去了使用getchar();system

7.         請問C++的類和C裏面的struct有什麼區別?

c++中的類具有成員保護功能,並且具有繼承,多態這類oo特點,而c裏的struct沒有

8.         請講一講析構函數和虛函數的用法和作用?

析構函數也是特殊的類成員函數,它沒有返回類型,沒有參數,不能隨意調用,也沒有重載。知識在類對象生命期結束的時候,由系統自動調用釋放在構造函數中分配的資源。這種在運行時,能依據其類型確認調用那個函數的能力稱爲多態性,或稱遲後聯編。另:析構函數一般在對象撤消前做收尾工作,比如回收內存等工作,虛擬函數的功能是使子類可以用同名的函數對父類函數進行重載,並且在調用時自動調用子類重載函數,如果是純虛函數,則純粹是爲了在子類重載時有個統一的命名而已。

9.         全局變量和局部變量有什麼區別?實怎麼實現的?操作系統和編譯器是怎麼知道的?

全局變量的生命週期是整個程序運行的時間,而局部變量的生命週期則是局部函數或過程調用的時間段。其實現是由編譯器在編譯時採用不同內存分配方法。全局變量在main函數調用後,就開始分配,如果是靜態變量則是在main函數前就已經初始化了。而局部變量則是在用戶棧中動態分配的(還是建議看編譯原理中的活動記錄這一塊)

10.     是多少尉的系統?在數據總線上是怎麼實現的?8086

8086系統是16位系統,其數據總線是20位。

1.2   程序設計

1.         編寫用C語言實現的求n階階乘問題的遞歸算法:

long int fact(int n)
{

   int x;

   long int y;

   if(n<0)  

    {

      printf("error!");

    }

   if(n==0)

      return 1;

   x=n-1;

   y=fact(x);

   return (n*y);

}

2.         二分查找算法:

1)        遞歸方法實現:

int BSearch(elemtypea[],elemtype x,int low,int high)

/*在下屆爲low,上界爲high的數組a中折半查找數據元素x*/

{

  int mid;

 if(low>high) return -1;

 mid=(low+high)/2;

  if(x==a[mid])return mid;

 if(x<a[mid]) return(BSearch(a,x,low,mid-1));

  elsereturn(BSearch(a,x,mid+1,high));

}

2)        非遞歸方法實現:

int BSearch(elemtypea[],keytype key,int n)

{

  intlow,high,mid;

 low=0;high=n-1;

 while(low<=high) 

   {

     mid=(low+high)/2;

     if(a[mid].key==key) return mid;

     else if(a[mid].key<key) low=mid+1;

     else high=mid-1;

   }

  return -1;

}

3.         遞歸計算如下遞歸函數的值(斐波拉契):

f(1)=1

f(2)=1

f(n)=f(n-1)+f(n-2)n>2

解:

int f(int n)

{

       int i,s,s1,s2;

       s1=1;/*s1用於保存f(n-1)的值*/

       s2=1;/*s2用於保存f(n-2)的值*/

       s=1;

       for(i=3;i<=n;i++)

{

      s=s1+s2;

      s2=s1;

      s1=s;

    }

return(s);

}

4.         交換兩個數,不用第三塊兒內存:

int a = ……;

int b = ……;

a = a + b;

b = a - b;

a = a - b;

5.         冒泡排序:

voidBubbleSort(elemtype x[],int n)

{

       int i,j;

       elemtype temp;

       for(i=1;i<n;i++) 

       for(j=0;j<n-i;j++)

{

               if(x[j].key>x[j+1].key) 

               {

                   temp=x[j];

                   x[j]=x[j+1];

                   x[j+1]=temp;

               }

}

}

6.         語言文件讀寫c

#include"stdio.h"

main()

{

       FILE *fp;

       char ch,filename[10];

       scanf("%s",filename);

       if((fp=fopen(filename,"w")==NULL)

       {

printf("cann'topen file\n");

exit(0);

  }

  ch=getchar();

  while(ch!='#')

  {

      fputc(ch,fp);

      putchar(ch);

      ch=getchar();

  }

  fclose(fp);

}

7.         編程winsocket

#include<Winsock2.h>

#include<stdio.h>

void main()

{

      WORDwVersionRequested;

      WSADATA wsaData;

      int err;

      wVersionRequested = MAKEWORD(1,1);

      err = WSAStartup(wVersionRequested,&wsaData);

      if( err != 0)

{

          return; 

      }

      if(LOBYTE( wsaData.wVersion ) != 1||

          HIBYTE( wsaData.wVersion) != 1)

{

           WSACleanup();

           return;

       }

      SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);

      SOCKADDR_IN addrSrv;

      addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

      addrSrv.sin_family=AF_INET;

      addrSrv.sin_port=htons(6000);

      bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

      listen(sockSrv,5);

      SOCKADDR_IN addrClient;

      int len=sizeof(SOCKADDR);

      while(1)

      {

           SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);

           char sendBuf[100];

           sprint(sendBuf,"Welcome %s to http://www.sunxin.org",

          inet_ntoa(addrClient.sin_addr));

           send(sockConn,sendBuf,strlen(sendBuf)+1,0);

           char recvBuf[100];

          recv(sockConn,recvBuf);

           printf("%s\n",recvBuf);

           closesocket(sockConn);

           WSACleanup();

      } 

}

注:這是Server端;File->New->Win32Console Application,工程名:TcpSrv;然後,File->New->C++ Source File,文件名:TcpSrv;在該工程的SettingLinkObject/librarymodules項要加入ws2_32.lib

#include<Winsock2.h>

#include<stdio.h>

void main()

{

       WORDwVersionRequested;

       WSADATA wsaData;

       int err;

       wVersionRequested = MAKEWORD(1,1);

       err =WSAStartup(wVersionRequested,&wsaData);

       if( err != 0)

{

           return; 

       }

       if(LOBYTE( wsaData.wVersion ) != 1||

           HIBYTE( wsaData.wVersion) != 1)

{

           WSACleanup();

           return;

       }

       SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);

       SOCKADDR_IN addrSrv;
       addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");

       addrSrv.sin_family=AF_INET;

       addrSrv.sin_porthtons(6000);

      connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

char recvBuf[100];

recv(sockClient,recvBuf,100,0);

printf("%s\n",recvBuf);

send(sockClient,"Thisis zhangsan",strlen("This is zhangsan")+1,0); 

       closesocket(sockClient);

       WSACleanup();

}

注:這是Client端;File->New->Win32 Console Application,工程名:TcpClient;然後,File->New->C++ Source File,文件名:TcpClient;同理,在該工程的SettingLinkObject/librarymodules項要加入ws2_32.lib

8.         類的知識

C++

#include<iostream.h>

class human

{

public:

human(){human_num++;};

static int human_num;

~human()

{

human_num--;

print();

}

void print()

{

cout<<"humannum is: "<<human_num<<endl;

}

protected:

private:

};

int human::human_num= 0;

human f1(human x)

{

x.print();

return x;

}

int main(int argc,char* argv[])

{

human h1;

h1.print();

human h2  =f1(h1);

h2.print();

return 0;

}

輸出:

1

1

0

0

1

2

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

分析:

humanh1;       //調用構造函數,---hum_num = 1;

h1.print();    //輸出:"human is1"

human h2  =f1(h1); //再調用f1(h1)的過程中,由於函數參數是按值傳遞對象,調用默認的複製構造函數,它並沒有對hum_num++,所以hum_num = 1,所以x.print()輸出:"human is1";   在推出f1函數時,要銷燬X,調用析構函數(human_num--),輸出:"human is 0"(,由於該函數返回一個human 對象,所以又調用默認構造函數,創建一個臨時對象(human_num = 0;),把臨時對象賦給h2,又調用默認構造函數(  human_num =0);   h2.print();   //輸出: human is 0;

//在退出main()函數是,先銷燬h2,調用析構函數(human_num--),輸出"human_num is -1" 然後銷燬h1,調用析構函數(--),輸出"human_num is -2"

1.3   Windows的消息機制

1.         Windows的消息機制

      Windows是一個消息(Message)驅動系統。Windows的消息提供了應用程序之間、應用程序與Windows系統之間進行通信的手段。應用程序想要實現的功能由消息來觸發,並且靠對消息的響應和處理來完成。

      Windows系統中有兩種消息隊列:系統消息隊列和應用程序消息隊列。計算機的所有輸入設備由Windows監控。當一個事件發生時,Windows先將輸入的消息放入系統消息隊列中,再將消息拷貝到相應的應用程序消息隊列中。應用程序的消息處理程序將反覆檢測消息隊列,並把檢測到的每個消息發送到相應的窗口函數中。這便是一個事件從發生至到達窗口函數必須經歷的過程。

      必須注意的是,消息並非是搶佔性的,無論事件的緩急,總是按照到達的先後派對,依次處理(一些系統消息除外),這樣可能使一些實時外部事件得不到及時處理。

2.         Windows的消息機制

      Windows中的消息是放在對應的進程的消息隊列裏的。可以通過GetMessage取得,並且對於一般的消息,此函數返回非零值,但是對於WM_QUIT消息,返回零。可以通過這個特徵,結束程序。當取得消息之後,應該先轉換消息,再分發消息。所謂轉換,就是把鍵盤碼的轉換,所謂分發,就是把消息分發給對應的窗口,由對應的窗口處理消息,這樣對應窗體的消息處理函數就會被調用。兩個函數可以實現這兩個功能:TranslateMessageDispatchMessage

      另外,需要注意,當我們點擊窗口的關閉按鈕關閉窗口時,程序並沒有自動退出,而是向程序發送了一個WM_DESTROY消息(其實過程是這樣的,首先向程序發送WM_CLOSE消息,默認的處理程序是調用DestroyWindow銷燬窗體,從而引發WM_DESTROY消息),此時在窗體中我們要響應這個消息,如果需要退出程序,那麼就要向程序發送WM_QUIT消息(通過PostQuitMessage實現)。一個窗體如果想要調用自己的消息處理函數,可以使用SendMessage向自己發消息。

      如上所述,大部分(注意是大部分)的消息是這樣傳遞的:首先放到進程的消息隊列中,之後由GetMessage取出,轉換後,分發給對應的窗口。這種消息成爲存儲式消息。存儲式消息基本上是使用者輸入的結果,以擊鍵(如WM_KEYDOWNWM_KEYUP訊息)、擊鍵產生的字符(WM_CHAR)、鼠標移動(WM_MOUSEMOVE)和鼠標按鈕(WM_LBUTTONDOWN)的形式給出。存儲式消息還包含時鐘消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。但是也有的消息是直接發送給窗口的,它們被稱爲非存儲式消息。例如,當WinMain調用CreateWindow時,Windows將建立窗口並在處理中給窗口消息處理函數發送一個WM_CREATE消息。當WinMain調用ShowWindow時,Windows將給窗口消息處理函數發送WM_SIZEWM_SHOWWINDOW消息。當WinMain調用UpdateWindow時,Windows將給窗口消息處理函數發送WM_PAINT消息。


2      網絡知識

2.1   OSITCP/IP

1.         的七層網絡結構圖(功能及特點)OSI

1)   物理層:爲數據鏈路層提供物理連接,在其上串行傳送比特流,即所傳送數據的單位是比特。此外,該層中還具有確定連接設備的電氣特性和物理特性等功能。

2)   數據鏈路層:負責在網絡節點間的線路上通過檢測、流量控制和重發等手段,無差錯地傳送以幀爲單位的數據。爲做到這一點,在每一幀中必須同時帶有同步、地址、差錯控制及流量控制等控制信息。

3)   網絡層:爲了將數據分組從源(源端系統)送到目的地(目標端系統),網絡層的任務就是選擇合適的路由和交換節點,使源的傳輸層傳下來的分組信息能夠正確無誤地按照地址找到目的地,並交付給相應的傳輸層,即完成網絡的尋址功能。

4)   傳輸層:傳輸層是高低層之間銜接的接口層。數據傳輸的單位是報文,當報文較長時將它分割成若干分組,然後交給網絡層進行傳輸。傳輸層是計算機網絡協議分層中的最關鍵一層,該層以上各層將不再管理信息傳輸問題。

5)   會話層:該層對傳輸的報文提供同步管理服務。在兩個不同系統的互相通信的應用進程之間建立、組織和協調交互。例如,確定是雙工還是半雙工工作。

6)   表示層:該層的主要任務是把所傳送的數據的抽象語法變換爲傳送語法,即把不同計算機內部的不同表示形式轉換成網絡通信中的標準表示形式。此外,對傳送的數據加密(或解密)、正文壓縮(或還原)也是表示層的任務。

7)   應用層:該層直接面向用戶,是OSI中的最高層。它的主要任務是爲用戶提供應用的接口,即提供不同計算機間的文件傳送、訪問與管理,電子郵件的內容處理,不同計算機通過網絡交互訪問的虛擬終端功能等。

2.         (功能及特點)TCP/IP

1)   網絡接口層:這是TCP/IP協議的最低一層,包括有多種邏輯鏈路控制和媒體訪問協議。網絡接口層的功能是接收IP數據報並通過特定的網絡進行傳輸,或從網絡上接收物理幀,抽取出IP數據報並轉交給網際層。

2)   網際網層(IP層):該層包括以下協議:IP(網際協議)、ICMPInternet Control Message Protocol,因特網控制報文協議)、ARPAddress Resolution Protocol,地址解析協議)、RARPReverse Address Resolution Protocol,反向地址解析協議)。該層負責相同或不同網絡中計算機之間的通信,主要處理數據報和路由。在IP層中,ARP協議用於將IP地址轉換成物理地址,RARP協議用於將物理地址轉換成IP地址,ICMP協議用於報告差錯和傳送控制信息。IP協議在TCP/IP協議組中處於核心地位。

3)   傳輸層:該層提供TCP(傳輸控制協議)和UDPUser Datagram Protocol,用戶數據報協議)兩個協議,它們都建立在IP協議的基礎上,其中TCP提供可靠的面向連接服務,UDP提供簡單的無連接服務。傳輸層提供端到端,即應用程序之間的通信,主要功能是數據格式化、數據確認和丟失重傳等。

4)   應用層:TCP/IP協議的應用層相當於OSI模型的會話層、表示層和應用層,它向用戶提供一組常用的應用層協議,其中包括:TelnetSMTPDNS等。此外,在應用層中還包含有用戶應用程序,它們均是建立在TCP/IP協議組之上的專用程序。

3.         參考模型和TCP/IP參考模型的區別:OSI

1)   OSI模型有7層,TCP/IP只有4層;

2)   OSI先於協議出現,因此不會偏向於任何一組特定的協議,通用性更強,但有些功能不知該放哪一層上,因此不得不加入一些子層;TCP/IP後於協議出現,僅是將已有協議的一個描述,因此兩者配合的非常好;但他不適合其他的協議棧,不容易描述其他非TCP/IP的網絡;

3)   OSI中網絡層同時支持無連接和面向連接的通信,但在傳輸層上只支持面向連接的通信;TCP/IP中網絡層只支持無連接通信,傳輸層同時支持兩種通信;

4)   在技術發生變化時,OSI模型比TCP/IP模型中的協議更容易被替換。

4.         請你詳細的解釋一下IP協議的定義,在哪個層上面,主要有什麼作用? TCPUDP呢? 

解:與IP協議配套使用的還有三個協議:

ARP-地址解析協議

RARP-逆地址解析協議

ICMP-因特網控制報文協議ICMP

IP協議-網際協議

IP地址、IP包頭

2.2   交換機和路由器

1.         請問交換機和路由器分別的實現原理是什麼?分別在哪個層次上面實現的?

將網絡互相連接起來要使用一些中間設備(或中間系統),ISO的術語稱之爲中繼(relay)系統。根據中繼系統所在的層次,可以有以下五種中繼系統:

1)       物理層(即常說的第一層、層L1)中繼系統,即轉發器(repeater)。

2)       數據鏈路層(即第二層,層L2),即網橋或橋接器(bridge)。

3)       網絡層(第三層,層L3)中繼系統,即路由器(router)。

4)       網橋和路由器的混合物橋路器(brouter)兼有網橋和路由器的功能。

5)       在網絡層以上的中繼系統,即網關(gateway.

當中繼系統是轉發器時,一般不稱之爲網絡互聯,因爲這僅僅是把一個網絡擴大了,而這仍然是一個網絡。高層網關由於比較複雜,目前使用得較少。因此一般討論網絡互連時都是指用交換機和路由器進行互聯的網絡。本文主要闡述交換機和路由器及其區別。  

2.         第二層交換機和路由器的區別:

傳統交換機從網橋發展而來,屬於OSI第二層即數據鏈路層設備。它根據MAC地址尋址,通過站表選擇路由,站表的建立和維護由交換機自動進行。路由器屬於OSI第三層即網絡層設備,它根據IP地址進行尋址,通過路由表路由協議產生。因特網的路由選擇協議:內部網關協議IGP和外部網關協議EGP

3.         第三層交換機和路由器的區別:

在第三層交換技術出現之前,幾乎沒有必要將路由功能器件和路由器區別開來,他們完全是相同的:提供路由功能正在路由器的工作,然而,現在第三層交換機完全能夠執行傳統路由器的大多數功能。

綜上所述,交換機一般用於LAN-WAN的連接,交換機歸於網橋,是數據鏈路層的設備,有些交換機也可實現第三層的交換。路由器用於WAN-WAN之間的連接,可以解決異性網絡之間轉發分組,作用於網絡層。他們只是從一條線路上接受輸入分組,然後向另一條線路轉發。這兩條線路可能分屬於不同的網絡,並採用不同協議。相比較而言,路由器的功能較交換機要強大,但速度相對也慢,價格昂貴,第三層交換機既有交換機線速轉發報文能力,又有路由器良好的控制功能,因此得以廣播應用。


3      高質量編程C/C++

一、請填寫BOOL , float, 指針變量與零值比較的 if 語句。(10 分)
請寫出 BOOL flag 零值比較的 if 語句。(3 分)
標準答案:
if ( flag )
if ( !flag )
如下寫法均屬不良風格,不得分。
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)


請寫出 float x 零值比較的 if 語句。(4 分)
標準答案示例:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
不可將浮點變量用“==”=”與數字
比較,應該設法轉化成“>=”“<=”
類形式。
如下是錯誤的寫法,不得分。
if (x == 0.0)
if (x != 0.0)


請寫出 char *p 零值比較的 if 語句。(3 分)
標準答案:
if (p == NULL)
if (p != NULL)
如下寫法均屬不良風格,不得分。
if (p == 0)
if (p != 0)
if (p)
if (!)

二、以下爲Windows NT 下的32 C++程序,請計算sizeof 的值(10 分)
void Func ( char str[100])
{
   
請計算
    sizeof( str ) = 4
2 分)
}
char str[] = “Hello” ;
char *p = str ;
int n = 10;
請計算
sizeof (str ) = 6
2 分)
sizeof ( p ) = 4
2 分)
sizeof ( n ) = 4
2 分)
void *p = malloc( 100 );
請計算
sizeof ( p ) = 4
2 分)

三、簡答題(25 分)
1
、頭文件中的 ifndef/define/endif 幹什麼用?(5 分)
答:防止該頭文件被重複引用。


2
#include <filename.h> #include “filename.h” 有什麼區別?(5 分)
答:對於#include<filename.h> ,編譯器從標準庫路徑開始搜索 filename.h
對於#include “filename.h”,編譯器從用戶的工作路徑開始搜索 filename.h


3
const 有什麼用途?(請至少說明兩種)(5 分)
答:(1)可以定義 const 常量,(2const 可以修飾函數的參數、返回值,甚至函數的定義體。被const 修飾的東西都受到強制保護,可以預防意外的變動,能提高程序的健壯性。

4、在C++ 程序中調用被 C 編譯器編譯後的函數,爲什麼要加 extern “C”5 分)
答:C++語言支持函數重載,C 語言不支持函數重載。函數被C++編譯後在庫中的名字
C 語言的不同。假設某個函數的原型爲: void foo(int x, int y);該函數被C 編譯器編譯後在庫中的名字爲_foo C++編譯器則會產生像_foo_int_int 之類的名字。C++提供了C 連接交換指定符號extern“C”來解決名字匹配問題。

5、請簡述以下兩個for 循環的優缺點(5 分)
for (i=0; i<N; i++)
{
    if (condition)
        DoSomething();
    else
        DoOtherthing();
}
if (condition)
{
    for (i=0; i<N; i++)
        DoSomething();
}
else
{
    for (i=0; i<N; i++)
    DoOtherthing();
}
優點:程序簡潔
缺點:多執行了N-1 次邏輯判斷,並且
打斷了循環流水線作業,使得編譯
器不能對循環進行優化處理,降低了效
率。
優點:循環的效率高
缺點:程序不簡潔

 

 

 

四、有關內存的思考題(每小題5 分,共20 分)

void GetMemory(char*p)
{
   p = (char *)malloc(100);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(str);
    strcpy(str, "hello world");
    printf(str);
}
請問運行Test 函數會有什麼樣的結果?
答:程序崩潰。
因爲GetMemory 並不能傳遞動態內存,
Test
函數中的 str 一直都是 NULL
strcpy(str, "hello world");
將使程序崩
潰。

char *GetMemory(void)
{
    char p[] = "hello world";
    return p;
}
void Test(void)
{
    char *str = NULL;
    str = GetMemory();
    printf(str);
}
請問運行Test 函數會有什麼樣的結果?
答:可能是亂碼。
因爲GetMemory 返回的是指向棧內存
的指針,該指針的地址不是 NULL,但其原
現的內容已經被清除,新內容不可知。

void GetMemory2(char**p, int num)
{
    *p = (char *)malloc(num);
}
void Test(void)
{
    char *str = NULL;
    GetMemory(&str, 100);
    strcpy(str, "hello");
    printf(str);
}
請問運行Test 函數會有什麼樣的結果?
答:(1)能夠輸出hello;(2)內存泄漏


void Test(void)
{
    char *str = (char *) malloc(100);
    strcpy(str, “hello”);
    free(str);
    if(str != NULL)
    {
        strcpy(str, “world”);
        printf(str);
    }
}
請問運行Test 函數會有什麼樣的結果?
答:篡改動態內存區的內容,後果難以預
料,非常危險。
因爲free(str);之後,str 成爲野指針,
if(str != NULL)
語句不起作用。

五、編寫strcpy 函數(10 分)
已知strcpy 函數的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
1)不調用C++/C 的字符串庫函數,請編寫函數 strcpy
char *strcpy(char *strDest, const char *strSrc);
{
    assert((strDest!=NULL) && (strSrc !=NULL)); // 2

    char *address = strDest; // 2

    while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2

    NULL ;
    return address ; // 2

}

2strcpy 能把strSrc 的內容複製到strDest,爲什麼還要char * 類型的返回值?
答:爲了實現鏈式表達式。 // 2
例如 int length = strlen(strcpy( strDest, “hello world”) );

六、編寫類String 的構造函數、析構函數和賦值函數(25 分)
已知類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::~String(void) // 3

{
    delete [] m_data;
    //
由於m_data 是內部數據類型,也可以寫成 delete m_data;
}


// String
的普通構造函數
String::String(const char *str) // 6

{
    if(str==NULL)
    {
        m_data = new char[1]; //
若能加 NULL 判斷則更好
        *m_data = ‘\0’;
    }
    else
    {
        int length = strlen(str);
        m_data = new char[length+1]; //
若能加 NULL 判斷則更好
        strcpy(m_data, str);
     }
}


//
拷貝構造函數
String::String(const String &other) // 3

{
    int length = strlen(other.m_data);
    m_data = new char[length+1]; //
若能加 NULL 判斷則更好
    strcpy(m_data, other.m_data);
}


//
賦值函數
String & String::operate =(const String &other) // 13

{
    // (1)
檢查自賦值 // 4
    if(this == &other)
    return *this;
    // (2)
釋放原有的內存資源 // 3
    delete [] m_data;
    //
3)分配新的內存資源,並複製內容 // 3
    int length = strlen(other.m_data);
    m_data = new char[length+1]; //
若能加 NULL 判斷則更好
    strcpy(m_data, other.m_data);
    //
4)返回本對象的引用 // 3
    return *this;
}
 

 

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