天天看點

c++面試題2

1.    指出以下變量資料存儲位置
 
全局變量int(*g_pFun)(int);g_pFun=myFunction;g_pFun存儲的位置(A ) 為全局的函數指針
 
指向空間的位置( B) 所有函數代碼位于TEXT段
 
函數内部變量 static int nCount;       ( A) 靜态變量總是在DATA段或BSS段中
 
函數内部變量 char p[]=”AAA”;  p 指向空間的位置( C) 局域變量的靜态數組,空間在Stack中
 
函數内部變量 char *p=”AAA”;  p 指向空間的位置( E) ,”AAA”為一字元常量空間,不同編譯器有不同處理方法,大部分儲存在TEXT(代碼段中),也有編譯的rodata段中
 
函數内部變量 char *p=new char; p的位置(C ) 指向空間的位置(D ) 所有malloc空間來自于heap(堆)
 
A.    資料段
 
B.    代碼段
 
C.    堆棧
 
D.    堆
 
E.    不一定, 視情況而定
 
以上知識參見C語言變量的作用域相關課件
 
2.    以下程式的輸出結果為 ( )
 
#include <iostream>
 
main( )
 
{
 
using namespace std;
 
int num[5]={1,2,3,4,5};
 
cout <<*((int *)(&num+1)-1) <<endl;
 
}
 
A. 1        B.2        C. 3        D. 4        E. 5       F. 0        G. 未初始化記憶體,無法确定
 
在C語言中,一維數組名表示數組的首位址,而且是一個指針.如上例num,
 
對&num,表示指針的指針.意味着這裡強制轉換為二維數組指針.
 
這樣 &num+1 等同于 num[5][1],為代碼空間. (&num+1)-1表示 num[4][0].即num[4].是以這裡答案是E.
 
 
 
擴充題目:
 
*((int *)(num+1)-1)   的值是多少?
 
Num是首指針,num+1是第二個元素指針,-1後又變成首指針.是以這裡是答案是num[0]即,A.1
 
3.    以下哪些是程式間可靠的通訊方式( C ),哪些可以用于跨主機通訊( C,D ,F).Windows命名管道跨機器也可跨機器.
 
A. 信号         B. 管道               C. TCP          D. UDP         E. PIPE         F,.序列槽I/O
 
4. class a
 
{
 
public:
 
virtual  void  funa( );
 
virtual  void  funb( );
 
void  fun( );
 
static  void  fund( );
 
static  int  si;
 
private:
 
int  i;
 
char  c;
 
};
 
問: 在32位編譯器預設情況下,sizeof(a)等于( )位元組?
 
A. 28             B. 25      C.24          D. 20           E. 16        F.12             G. 8
 
答案在VC++下是 12. 這裡需要考慮三個問題,一是虛函數表vtable的入口表位址,二是位元組對齊.三 ,靜态成員是所有對象共享,不計入sizeof空間.
 
在大部分C++的實作中,帶有虛函數的類的前4個BYTE是虛函數vtable表的這個類入口位址.是以sizeof必須要加入這個4個byte的長度,除此外,類的sizoef()為所有資料成員總的sizeof之和,這裡是int i,和char c.其中char c被位元組對齊為4.這樣總長度為
 
 
 
Sizeof(a) = sizeof(vtable)+size(int)+sizeof(char + pad) = 12;
 
 
 
5. 32位Windows 系統或Linux系統下
 
struct
 
{
 
char  a;
 
char  b;
 
char  c;
 
}A;
 
struct
 
{
 
short  a;
 
short  b;
 
short  c;
 
}B;
 
struct
 
{
 
short  a;
 
long  b;
 
char  c;
 
}C;
 
printf(“%d,%d,%d\n”,sizeof(A),sizeof(B),sizeof(C)); 的執行結果為: ( )
 
A. 3,6,7         B. 3,6,8         C. 4,8,12              D. 3,6,12      E. 4,6,7         F. 4,8,9
 
 
 
C文法的位元組對齊規則有兩種情況要位元組對齊, 在VC++,gcc測試都是如此
 
1)    對同一個資料類型(short,int,long)發生了跨段分布,(在32CPU裡,即一個資料類型分布在兩個段中)才會發生位元組對齊.
 
2)    資料類型的首部和尾部必須有其一是與4對齊.而且違反上一規則.
 
l  Sizeof(A),sizeof(B)雖然總位元組數不能被4整除.但剛好所有資料平均分布在以4為機關的各個段中.是以無需位元組對齊,是以結果是 3和6
 
l  struct {char a;char b;char c;char d;char e;}F; 的sizoef(F)是等于5.
 
l  用以下執行個體更加清楚
 
struct {
 
char a[20];
 
short b;
 
}A;
 
struct {
 
char a[21];
 
short b;
 
}B;
 
Sizeof(A)=22,sizoef(B)=24.因為前者沒有發生跨段分布.後者,如果不位元組對齊.a[21]占用最後一個段的首位址,b無法作到與首部與尾部與4對齊,隻能在a[21]與b之間加入一個byte,使用b的尾部與4對齊.
 
l  C就是比較好了解.要補多個成12
 
 
 
6.    依據程式,以下選擇中那個是對的? (  )
 
class A
 
{
 
int  m_nA;
 
};
 
class B
 
{
 
int   m_nB;
 
};
 
class C:public A,public B
 
{
 
int  m_nC;
 
};
 
void f (void)
 
{
 
C* pC=new C;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
}
 
A. pC= =pB,(int)pC= =(int)B                                  B. pC= =pB,(int)pC!=(int)pB
 
C. pC!=pB,(int)pC= =(int)pB                                  D. pC!=pB,(int)pC!=(int)pB
 
 
 
這裡主要考多态..将程式變為如下比較易懂
 
 
 
#include <stdio.h>
 
 
 
class A
 
{
 
public:
 
int  m_nA;
 
};
 
class B
 
{
 
public:
 
int   m_nB;
 
};
 
class C:public A,public B
 
{
 
public:
 
int  m_nC;
 
};
 
 
 
void f (void)
 
{
 
C* pC=new C;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
}
 
 
 
void f1 (void)
 
{
 
C* pC=new C;
 
pC->m_nA = 1;
 
pC->m_nB = 2;
 
pC->m_nC = 3;
 
B* pB=dynamic_cast<B*>(pC);
 
A* pA=dynamic_cast<A*>(pC);
 
 
 
printf(“A=%x,B=%x,C=%x,iA=%d,iB=%d,iC=%d\n”,pA,pB,pC,(int)pA,(int)pB,(int)pC);
 
}
 
 
 
 
 
void test1();
 
 
 
int main()
 
{
 
// test1();
 
f1();
 
getchar();
 
return 0;
 
}
 
以上程式輸出:
 
A=4318d0,B=4318d4,C=4318d0,iA=4397264,iB=4397268,iC=4397264
 
即C從,A,B繼承下來,由下圖可以知道 pA=pC.而pB強制轉換後,隻能取到C中B的部分.是以pB在pC向後偏移4個BYTE,(即m_nA)的空間
 
 
 
7,請寫出能比對”[10]:dddddd ”和”[9]:abcdegf ”,不比對”[a]:xfdf ”的正規表達式________,linux下支援正則的指令有:___find,grep_________
 
8.如下程式:
 
int i=1,k=0;
 
long *pl=NULL;
 
char *pc=NULL;
 
if(k++&&i++)
 
k++, pl++, pc++;
 
if(i++||k++)
 
i++, pl++, pc++;
 
printf(“i=%d,k=%d,pl=%ld,pc=%ld\n”,i,k,(long)pl,(long)pc);
 
 
 
列印結果為__i=3,k=1,pl=4,pc=1________
 
主要測試邏輯表達式的短路操作.
 
&&操作中,前一個表達式為0,後一表達式不執行
 
||操作中, 前一個表達式為1,後一表達式不執行
 
 
 
9. 以下程式的輸出為______________
 
#include<iostream>
 
using std::cout;
 
class A
 
{
 
public:
 
void f(void){
 
cout<< ”A::f” <<’ ‘;
 
}
 
virtual void g(void)
 
{
 
cout <<”A::g” << ‘ ‘;
 
}
 
};
 
 
 
class B : public A
 
{
 
public:
 
void f(void)
 
{
 
cout << “B :: f “ << ‘ ‘;
 
}
 
void g(void)
 
{
 
cout << “B:: g “ << ‘ ‘;
 
}
 
 
 
};
 
 
 
int main()
 
{
 
A*  pA =new B;
 
pA->f();
 
pA->g();
 
B* pB = (B*)pA;
 
pB->f();
 
pB->g();
 
}
 
A::f B:: g  B :: f  B:: g  

多态中虛函數調用.
 
f()為非虛函數,這樣強制轉換後,執行本類的同名函數.
 
G()為虛函數,指針總是執行虛函數,這就是多态..
 
10.下列代碼的作用是删除list lTest 中值為6的元素:
 
list<int> :: iterator Index = ITest .begin();
 
for( ;  Index != ITest .end();  ++ Index)
 
{
 
if((*Index) = = 6)
 
{
 
ITest .erase(Index);
 
}
 
}
 
請問有什麼錯誤____ Index = ITest .erase(Index);____________________,
 
STL的遊标處理,erase已經将Index破壞掉,需要用新的Index,否則下一循環的++Index被破壞掉
 
請寫出正确的代碼,或者在原代碼上修正.
 
 
 
11.找錯誤_以下程式:
 
char* ptr = malloc(100);
 
if(!ptr)
 
{
 
…
 
}
 
…
 
//ptr 指向的空間不夠需要重新配置設定
 
ptr = realloc(ptr,200);
 
if(!ptr)
 
{
 
…
 
}
 
…
 
請問有什麼錯誤___if(ptr ==NULL)____________________,請寫出正确的代碼,或者在原代碼上修正.
 
 
 
12.以下為window NT 下32 位C++程式,請填寫如下值
 
class myclass
 
{
 
int a ;
 
int b;
 
};
 
char *p = “hello”;
 
char str[] = “world”;
 
myclass classes[2];
 
void *p2= malloc(100);
 
 
 
sizeof(p)=_4__
 
sizeof(str)=_6_
 
sizeof(classes)=_16__
 
sizeof(p2)=_4___
 
 
 
13.直接在以下程式中的錯誤的行數後的填空欄中打叉
 
程式1:
 
int main(void)
 
{
 
int i=10;_____
 
int *const j=&i;_______
 
(*j)++;____
 
j++;___*_____
 
}
 
程式2:
 
int main(void)
 
{
 
int i=20;_____
 
const int *j=&i;_________
 
*j++;______
 
(*j)++;____*____
 
}
 
 
 
主要考const 出現在*前後不同含意,const 在*後表示指針本身不能改,const 在*前面指針内容不能改,程式1中j不能修改指針,是以j++是錯,程式2,j不能改改内容,是以
 
 
 
14.用C/C++代碼實作以下要求:從1-100中挑選出10個不同的數字,請把可能的所有組合列印出來.
 
 
 
15.有一個非常大的全局數組int a[],長度n超過2的24次方,寫一個針對該數組的查找算法unsigned search(int value)(傳回值下标),插入算法insert(int value,unsigned index).再次注意該數組的長度很長.
 
題目不太清,可能可以把數值本身作下标.并且按順序排序.
 
16.有兩個單向連結清單,表頭pHeader1,pHeader2,請寫一個函數判斷這兩個連結清單是否有交叉.如果有交叉,給出交叉點.程式不能改變連結清單的内容,可以使用額外的空間,時間複雜度盡量小,最好給出兩種解.(雙重循環的解由于時間複雜度高,不算正解).
 
1.移動連結清單指針,如果最終
 
17.編寫程式,将一棵樹從根節點到葉子的所有最長路徑都列印出來.比如一棵樹從跟到最末端的葉子最遠要經
 
過4個節點,那麼就把到所有要經過4個節點才能到達的葉子的搜尋路徑(所有途徑節點)都分别列印出來.
 
 
 
18.請分别對一個連結清單和一個數組進行排序,并指出你排序的算法名稱以及為何選擇該算法
 
數組可用交換法排序
 
 
 
 
 
19.有單向連結清單,其中節點結構為Node{int value;Node *pNext};隻知道指向某個節點的指針pCurrent;并且知道該節點不是尾節點,有什麼辦法把他删除嗎?要求不斷鍊.
 
從連結清單頭開始,找到pCurrent上一個結點pPrev,然後 pPrev->pNext = pCurrent->pNext;
 
 
 
20.問題A:用什麼方法避免c/c++程式設計中的頭檔案重複包含?問題B:假設解決了重複包含問題,但是又需要在兩個不同的頭檔案中引用各申明的類,應該如何處理?具體代碼如下:
 
在頭檔案Man.h中
 
….
 
Class Cman
 
{
 
….
 
CFace m_face;
 
};
 
….
 
在頭檔案Face.h中
 
…
 
Class CFace
 
{
 
…
 
Cman *m_owner;
 
};
 
….
 
這樣類CMan.CFace就互相引用了,該如何處理呢?
 
 
 
1.#ifndef ….
 
#define …..
 
2.類的前向聲明
 
 
 
21.多線程和單線程各自分别在什麼時候效率更高?
 
多線程在并發,并且各線程無需通路共享資料情況詳細最高
 
如果多線程過于頻繁切換,或共享資料很多情況下,使用單線程較好
 
 
 
22.在程式設計中,對公共資源(比如緩沖區等)的操作和通路經常需要使用鎖來進行保護,但在大并發系統中過多的鎖會導緻效率很低,通常有那些方法可以盡量避免或減少鎖的使用?
 
減少鎖的粒度,每次盡可能減少鎖範圍
 
采用隊列處理,這樣無需使用鎖.
 
23.請詳細闡述如何在release版本(windows程式或linux程式都可以)中,查找段錯誤問題.
 
可以用編譯器生成map檔案來定位源碼.通過位址反查源碼
 
 
 
24.假設你編譯連結release版本後得到一個可執行程式(由多個cpp檔案和H檔案編譯),結果可執行程式檔案非常大,你如何找到造成檔案太大的可能原因,可能的原因是什麼?
 
使用一個已經初始化的巨大的全局數組
 
25.在編寫C++指派運算符時有哪些要注意的地方?
 
傳回值,參數最好用引用
 
減少友元函數使用,移植有問題.
 
26.假設你是參與設計嫦娥衛星的嵌入式單闆軟體工程師,其中有一個快速搜尋可能要用到哈希變或者平衡二叉樹,要求不管什麼條件下,單闆必須在指定的短時間内有輸出,你會采取那種算法?為什麼用這種算法,為什麼不用另一種算法?
 
HASH.HASH通路速度較快.
 
27.strcpy()容易引起緩沖區溢出問題,請問有什麼函數可以替代以減少風險,為什麼?
 
strncpy
 
28.請指出spinlock,mutex,semaphore,critical section的作用與差別,都在哪些場合使用.
 
spin_lock Linux 核心自旋鎖. Mutex Windows 互品質, semaphore  POSIX ,critical section Windows
 
29.在哪些方法使阻塞模式的recv函數在沒有收到資料的情況下傳回(不能将socket修改為非阻塞模式)請描述得詳細點.
 
使用select
 
 
 
30.有3個紅色球,2個白色球,1個綠色球.取出兩個不同顔色的球就能變成兩個第三種顔色的球(比如:取出1紅球,1白球,就能變成2個綠球).問,最少幾次變化能将所有球都變成同一顔色,說明步驟和原因?
 
 
 
31.單向連結清單的反轉是一個經常被問到的一個面試題,也是一個非常基礎的問題。比如一個連結清單是這樣的: 1->2->3->4->5 通過反轉後成為5->4->3->2->1。
 
最容易想到的方法周遊一遍連結清單,利用一個輔助指針,存儲周遊過程中目前指針指向的下一個元素,然後将目前節點元素的指針反轉後,利用已經存儲的指針往後面繼續周遊。源代碼如下:
 1.struct linka {
 2.int data;
 3.linka* next;
 4.};
 5.void reverse(linka*& head) {
 6.if(head ==NULL)
 7.                  return;
 8.linka *pre, *cur, *ne;
 9.pre=head;
 10.cur=head->next;
 11.while(cur)
 12.{
 13.   ne = cur->next;
 14.   cur->next = pre;
 15.   pre = cur;
 16.   cur = ne;
 17.}
 18.head->next = NULL;
 19.head = pre;
 20.}
 
還有一種利用遞歸的方法。這種方法的基本思想是在反轉目前節點之前先調用遞歸函數反轉後續節點。源代碼如下。不過這個方法有一個缺點,就是在反轉後的最後一個結點會形成一個環,是以必須将函數的傳回的節點的next域置為NULL。因為要改變head指針,是以我用了引用。算法的源代碼如下:
 1.linka* reverse(linka* p,linka*& head)
 2.{
 3.if(p == NULL || p->next == NULL)
 4.{
 5.   head=p;
 6.   return p;
 7.}
 8.else
 9.{
 10.   linka* tmp = reverse(p->next,head);
 11.   tmp->next = p;
 12.   return p;
 13.}
 14.}
 
32.已知String類定義如下:
 
class String
 {
 public:
 String(const char *str = NULL); // 通用構造函數
 String(const String &another); // 拷貝構造函數
 ~ String(); // 析構函數
 String & operater =(const String &rhs); // 指派函數
 private:
 char *m_data; // 用于儲存字元串
 };
 
嘗試寫出類的成員函數實作。
 
答案:
 
String::String(const char *str)
 {
 if ( str == NULL ) //strlen在參數為NULL時會抛異常才會有這步判斷
 {
 m_data = new char[1] ;
 m_data[0] = ‘\0′ ;
 }
 else
 {
 m_data = new char[strlen(str) + 1];
 strcpy(m_data,str);
 }
 
}
 
String::String(const String &another)
 {
 m_data = new char[strlen(another.m_data) + 1];
 strcpy(m_data,other.m_data);
 }
 
String& String::operator =(const String &rhs)
 {
 if ( this == &rhs)
 return *this ;
 delete []m_data; //删除原來的資料,新開一塊記憶體
 m_data = new char[strlen(rhs.m_data) + 1];
 strcpy(m_data,rhs.m_data);
 return *this ;
 }
 
String::~String()
 {
 delete []m_data ;
 }
 


33.求下面函數的傳回值(微軟)
 
int func(x)
 {
 int countx = 0;
 while(x)
 {
 countx ++;
 x = x&(x-1);
 }
 return countx;
 }
 
假定x = 9999。 答案:8
 
思路:将x轉化為2進制,看含有的1的個數。
 
34. 什麼是“引用”?申明和使用“引用”要注意哪些問題?
 
答:引用就是某個目标變量的“别名”(alias),對應用的操作與對變量直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。引用聲明完畢後,相當于目标變量名有兩個名稱,即該目标原名稱和引用名,不能再把該引用名作為其他變量名的别名。聲明一個引用,不是新定義了一個變量,它隻表示該引用名是目标變量名的一個别名,它本身不是一種資料類型,是以引用本身不占存儲單元,系統也不給引用配置設定存儲單元。不能建立數組的引用。
 
45. 将“引用”作為函數參數有哪些特點?
 
(1)傳遞引用給函數與傳遞指針的效果是一樣的。這時,被調函數的形參就成為原來主調函數中的實參變量或對象的一個别名來使用,是以在被調函數中對形參變量的操作就是對其相應的目标對象(在主調函數中)的操作。
 
(2)使用引用傳遞函數的參數,在記憶體中并沒有産生實參的副本,它是直接對實參操作;而使用一般變量傳遞函數的參數,當發生函數調用時,需要給形參配置設定存儲單元,形參變量是實參變量的副本;如果傳遞的是對象,還将調用拷貝構造函數。是以,當參數傳遞的資料較大時,用引用比用一般變量傳遞參數的效率和所占空間都好。
 
(3)使用指針作為函數的參數雖然也能達到與使用引用的效果,但是,在被調函數中同樣要給形參配置設定存儲單元,且需要重複使用”*指針變量名”的形式進行運算,這很容易産生錯誤且程式的閱讀性較差;另一方面,在主調函數的調用點處,必須用變量的位址作為實參。而引用更容易使用,更清晰。
 
36. 在什麼時候需要使用“常引用”?
 
如果既要利用引用提高程式的效率,又要保護傳遞給函數的資料不在函數中被改變,就應使用常引用。常引用聲明方式:const 類型辨別符 &引用名=目标變量名;
 
例1
 
int a ;
 const int &ra=a;
 ra=1; //錯誤
 a=1; //正确
 
例2
 
string foo( );
 void bar(string & s);
 
那麼下面的表達式将是非法的:
 
bar(foo( ));
 bar(“hello world”);
 
原因在于foo( )和”hello world”串都會産生一個臨時對象,而在C++中,這些臨時對象都是const類型的。是以上面的表達式就是試圖将一個const類型的對象轉換為非const類型,這是非法的。
 
引用型參數應該在能被定義為const的情況下,盡量定義為const 。
 
37. 将“引用”作為函數傳回值類型的格式、好處和需要遵守的規則?
 
格式:類型辨別符 &函數名(形參清單及類型說明){ //函數體 }
 
好處:在記憶體中不産生被傳回值的副本;(注意:正是因為這點原因,是以傳回一個局部變量的引用是不可取的。因為随着該局部變量生存期的結束,相應的引用也會失效,産生runtime error!
 
注意事項:
 
(1)不能傳回局部變量的引用。這條可以參照Effective C++[1]的Item 31。主要原因是局部變量會在函數傳回後被銷毀,是以被傳回的引用就成為了”無所指”的引用,程式會進入未知狀态。
 
(2)不能傳回函數内部new配置設定的記憶體的引用。這條可以參照Effective C++[1]的Item 31。雖然不存在局部變量的被動銷毀問題,可對于這種情況(傳回函數内部new配置設定記憶體的引用),又面臨其它尴尬局面。例如,被函數傳回的引用隻是作為一個臨時變量出現,而沒有被賦予一個實際的變量,那麼這個引用所指向的空間(由new配置設定)就無法釋放,造成memory leak。
 
(3)可以傳回類成員的引用,但最好是const。這條原則可以參照Effective C++[1]的Item 30。主要原因是當對象的屬性是與某種業務規則(business rule)相關聯的時候,其指派常常與某些其它屬性或者對象的狀态有關,是以有必要将指派操作封裝在一個業務規則當中。如果其它對象可以獲得該屬性的非常量引用(或指針),那麼對該屬性的單純指派就會破壞業務規則的完整性。