Posted on 2008-04-23 10:39
silentneil 閱讀(976)
評論(1) 編輯 收藏 引用
1 編程基礎
1.1 基本概念
1. 的理解:const char*, char const*, char*const的區別問題幾乎是C++面試中每次 都會有的題目。 事實上這個概念誰都有只是三種聲明方式非常相似很容易記混。 Bjarne在他的The C++ Programming Language里面給出過一個助記的方法: 把一個聲明從右向左讀。 const
char * const cp; ( * 讀成 pointer to )
cp is a const pointer to char
const char * p;
p is a pointer to const 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類型為一個字節,取值范圍是[-128,127],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'就結束拷貝;例:char a[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所指的數組中。
說明:src和dest所指內存區域不可以重疊且dest必須有足夠的空間來容納src的字符串。
返回指向dest的指針。
memcpy
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#include
功能:由src所指內存區域復制count個字節到dest所指內存區域。
說明:src和dest所指內存區域不能重疊,函數返回指向dest的指針。
Memset
原型:extern void *memset(void *buffer, char c, int count);
用法:#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(elemtype a[],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));
else return(BSearch(a,x,mid+1,high));
}
2) 非遞歸方法實現:
int BSearch(elemtype a[],keytype key,int n)
{
int low,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. 冒泡排序:
void BubbleSort(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't open 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)
{
SOCKET sockConn=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->Win32 Console Application,工程名:TcpSrv;然后,File->New->C++ Source File,文件名:TcpSrv;在該工程的Setting的Link的Object/library modules項要加入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,"This is zhangsan",strlen("This is zhangsan")+1,0);
closesocket(sockClient);
WSACleanup();
}
注:這是Client端;File->New->Win32 Console Application,工程名:TcpClient;然后,File->New->C++ Source File,文件名:TcpClient;同理,在該工程的Setting的Link的Object/library modules項要加入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<<"human num 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
----------------------------
分析:
human h1; //調用構造函數,---hum_num = 1;
h1.print(); //輸出:"human is 1"
human h2 = f1(h1); //再調用f1(h1)的過程中,由于函數參數是按值傳遞對象,調用默認的復制構造函數,它并沒有對hum_num++,所以hum_num 仍= 1,所以x.print()輸出:"human is 1"; 在推出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. 的消息機制1Windows
Windows是一個消息(Message)驅動系統。Windows的消息提供了應用程序之間、應用程序與Windows系統之間進行通信的手段。應用程序想要實現的功能由消息來觸發,并且靠對消息的響應和處理來完成。
Windows系統中有兩種消息隊列:系統消息隊列和應用程序消息隊列。計算機的所有輸入設備由Windows監控。當一個事件發生時,Windows先將輸入的消息放入系統消息隊列中,再將消息拷貝到相應的應用程序消息隊列中。應用程序的消息處理程序將反復檢測消息隊列,并把檢測到的每個消息發送到相應的窗口函數中。這便是一個事件從發生至到達窗口函數必須經歷的過程。
必須注意的是,消息并非是搶占性的,無論事件的緩急,總是按照到達的先后派對,依次處理(一些系統消息除外),這樣可能使一些實時外部事件得不到及時處理。
2. 的消息機制2Windows
Windows中的消息是放在對應的進程的消息隊列里的。可以通過GetMessage取得,并且對于一般的消息,此函數返回非零值,但是對于WM_QUIT消息,返回零。可以通過這個特征,結束程序。當取得消息之后,應該先轉換消息,再分發消息。所謂轉換,就是把鍵盤碼的轉換,所謂分發,就是把消息分發給對應的窗口,由對應的窗口處理消息,這樣對應窗體的消息處理函數就會被調用。兩個函數可以實現這兩個功能:TranslateMessage和 DispatchMessage。
另外,需要注意,當我們點擊窗口的關閉按鈕關閉窗口時,程序并沒有自動退出,而是向程序發送了一個WM_DESTROY消息(其實過程是這樣的,首先向程序發送WM_CLOSE消息,默認的處理程序是調用DestroyWindow銷毀窗體,從而引發WM_DESTROY消息),此時在窗體中我們要響應這個消息,如果需要退出程序,那么就要向程序發送WM_QUIT消息(通過PostQuitMessage實現)。一個窗體如果想要調用自己的消息處理函數,可以使用SendMessage向自己發消息。
如上所述,大部分(注意是大部分)的消息是這樣傳遞的:首先放到進程的消息隊列中,之后由GetMessage取出,轉換后,分發給對應的窗口。這種消息成為存儲式消息。存儲式消息基本上是使用者輸入的結果,以擊鍵(如WM_KEYDOWN和WM_KEYUP訊息)、擊鍵產生的字符(WM_CHAR)、鼠標移動(WM_MOUSEMOVE)和鼠標按鈕(WM_LBUTTONDOWN)的形式給出。存儲式消息還包含時鐘消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。但是也有的消息是直接發送給窗口的,它們被稱為非存儲式消息。例如,當WinMain調用 CreateWindow時,Windows將建立窗口并在處理中給窗口消息處理函數發送一個WM_CREATE消息。當WinMain調用 ShowWindow時,Windows將給窗口消息處理函數發送WM_SIZE和WM_SHOWWINDOW消息。當WinMain調用 UpdateWindow時,Windows將給窗口消息處理函數發送WM_PAINT消息。
2 網絡知識
2.1 OSI和TCP/IP
1. 的七層網絡結構圖(功能及特點)OSI
1) 物理層:為數據鏈路層提供物理連接,在其上串行傳送比特流,即所傳送數據的單位是比特。此外,該層中還具有確定連接設備的電氣特性和物理特性等功能。
2) 數據鏈路層:負責在網絡節點間的線路上通過檢測、流量控制和重發等手段,無差錯地傳送以幀為單位的數據。為做到這一點,在每一幀中必須同時帶有同步、地址、差錯控制及流量控制等控制信息。
3) 網絡層:為了將數據分組從源(源端系統)送到目的地(目標端系統),網絡層的任務就是選擇合適的路由和交換節點,使源的傳輸層傳下來的分組信息能夠正確無誤地按照地址找到目的地,并交付給相應的傳輸層,即完成網絡的尋址功能。
4) 傳輸層:傳輸層是高低層之間銜接的接口層。數據傳輸的單位是報文,當報文較長時將它分割成若干分組,然后交給網絡層進行傳輸。傳輸層是計算機網絡協議分層中的最關鍵一層,該層以上各層將不再管理信息傳輸問題。
5) 會話層:該層對傳輸的報文提供同步管理服務。在兩個不同系統的互相通信的應用進程之間建立、組織和協調交互。例如,確定是雙工還是半雙工工作。
6) 表示層:該層的主要任務是把所傳送的數據的抽象語法變換為傳送語法,即把不同計算機內部的不同表示形式轉換成網絡通信中的標準表示形式。此外,對傳送的數據加密(或解密)、正文壓縮(或還原)也是表示層的任務。
7) 應用層:該層直接面向用戶,是OSI中的最高層。它的主要任務是為用戶提供應用的接口,即提供不同計算機間的文件傳送、訪問與管理,電子郵件的內容處理,不同計算機通過網絡交互訪問的虛擬終端功能等。
2. (功能及特點)TCP/IP
1) 網絡接口層:這是TCP/IP協議的最低一層,包括有多種邏輯鏈路控制和媒體訪問協議。網絡接口層的功能是接收IP數據報并通過特定的網絡進行傳輸,或從網絡上接收物理幀,抽取出IP數據報并轉交給網際層。
2) 網際網層(IP層):該層包括以下協議:IP(網際協議)、ICMP(Internet Control Message Protocol,因特網控制報文協議)、ARP(Address Resolution Protocol,地址解析協議)、RARP(Reverse Address Resolution Protocol,反向地址解析協議)。該層負責相同或不同網絡中計算機之間的通信,主要處理數據報和路由。在IP層中,ARP協議用于將IP地址轉換成物理地址,RARP協議用于將物理地址轉換成IP地址,ICMP協議用于報告差錯和傳送控制信息。IP協議在TCP/IP協議組中處于核心地位。
3) 傳輸層:該層提供TCP(傳輸控制協議)和UDP(User Datagram Protocol,用戶數據報協議)兩個協議,它們都建立在IP協議的基礎上,其中TCP提供可靠的面向連接服務,UDP提供簡單的無連接服務。傳輸層提供端到端,即應用程序之間的通信,主要功能是數據格式化、數據確認和丟失重傳等。
4) 應用層:TCP/IP協議的應用層相當于OSI模型的會話層、表示層和應用層,它向用戶提供一組常用的應用層協議,其中包括:Telnet、SMTP、DNS等。此外,在應用層中還包含有用戶應用程序,它們均是建立在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協議的定義,在哪個層上面,主要有什么作用? TCP與UDP呢?
解:與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 常量,(2)const 可以修飾函數的參數、返回值,甚至函數的定義體。被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分
}
(2)strcpy 能把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;
}