來自:http://hi.baidu.com/litomboy/blog/item/08dade95a32c3148d0135e38.html
其他http://www.zeliu.com/digg_view.asp?id=112
題1:編寫一個函數,作用是把一個char組成的字串迴圈右移n個。比如原來是“abcdefghi”如果n=2,移位後應該是“hiabcdefgh”
函數頭是這樣的:
//pStr是指向以''結尾的字串的指標 //steps是要求移動的n void LoopMove ( char * pStr, int steps ) { //請填充... } |
解答:
正確解答1:
void LoopMove ( char *pStr, int steps ) { int n = strlen( pStr ) - steps; char tmp[MAX_LEN]; strcpy ( tmp, pStr + n ); strcpy ( tmp + steps, pStr); *( tmp + strlen ( pStr ) ) = ''; strcpy( pStr, tmp ); } |
正確解答2:
void LoopMove ( char *pStr, int steps ) { int n = strlen( pStr ) - steps; char tmp[MAX_LEN]; memcpy( tmp, pStr + n, steps ); memcpy(pStr + steps, pStr, n ); memcpy(pStr, tmp, steps ); } |
剖析:
這個試題主要考查面試者對標準庫函數的熟練程度,在需要的時候引用庫函數可以很大程度上簡化程式編寫的工作量。
最頻繁被使用的庫函數包括:
(1) strcpy
(2) memcpy
(3) memset
試題2:已知WAV檔案格式如下表,開啟一個WAV檔案,以適當的資料結構組織WAV檔案頭並解析WAV格式的各項資訊。
WAVE檔案格式說明表
|
位移地址 |
位元組數 |
資料類型 |
內 容 |
檔案頭 |
00H |
4 |
Char |
"RIFF"標誌 |
04H |
4 |
int32 |
檔案長度 |
08H |
4 |
Char |
"WAVE"標誌 |
0CH |
4 |
Char |
"fmt"標誌 |
10H |
4 |
|
過渡位元組(不定) |
14H |
2 |
int16 |
格式類別 |
16H |
2 |
int16 |
通道數 |
18H |
2 |
int16 |
採樣率(每秒樣本數),表示每個通道的播放速度 |
1CH |
4 |
int32 |
波形音頻資料傳送速率 |
20H |
2 |
int16 |
資料區塊的調整數(按位元組算的) |
22H |
2 |
|
每樣本的資料位元數 |
24H |
4 |
Char |
資料標記符"data" |
28H |
4 |
int32 |
語音資料的長度 |
解答:
將WAV檔案格式定義為結構體WAVEFORMAT:
typedef struct tagWaveFormat { char cRiffFlag[4]; UIN32 nFileLen; char cWaveFlag[4]; char cFmtFlag[4]; char cTransition[4]; UIN16 nFormatTag ; UIN16 nChannels; UIN16 nSamplesPerSec; UIN32 nAvgBytesperSec; UIN16 nBlockAlign; UIN16 nBitNumPerSample; char cDataFlag[4]; UIN16 nAudioLength; } WAVEFORMAT; |
假設WAV檔案內容讀出後存放在指標buffer開始的記憶體單元內,則分析檔案格式的代碼很簡單,為:
WAVEFORMAT waveFormat; memcpy( &waveFormat, buffer,sizeof( WAVEFORMAT ) ); |
直接通過訪問waveFormat的成員,就可以獲得特定WAV檔案的各項格式資訊。
剖析:
試題6考查面試者組織資料結構的能力,有經驗的程式設計者將屬於一個整體的資料成員組織為一個結構體,利用指標類型轉換,可以將memcpy、memset等函數直接用於結構體地址,進行結構體的整體操作。透過這個題可以看出面試者的程式設計經驗是否豐富。
試題3:編寫類String的建構函式、解構函式和賦值函數,已知類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::String(const char *str) { if(str==NULL) { m_data = new char[1]; // 得分點:對Null 字元串自動申請存放結束標誌''的空 //加分點:對m_data加NULL 判斷 *m_data = ''; } else { int length = strlen(str); m_data = new char[length+1]; // 若能加 NULL 判斷則更好 strcpy(m_data, str); } } // String的解構函式 String::~String(void) { delete [] m_data; // 或delete m_data; } //拷貝建構函式 String::String(const String &other) // 得分點:輸入參數為const型 { int length = strlen(other.m_data); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷 strcpy(m_data, other.m_data); } //賦值函數 String & String::operate =(const String &other) // 得分點:輸入參數為const型 { if(this == &other) //得分點:檢查自賦值 return *this; delete [] m_data; //得分點:釋放原有的記憶體資源 int length = strlen( other.m_data ); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷 strcpy( m_data, other.m_data ); return *this; //得分點:返回本對象的引用 } |
剖析:
能夠準確無誤地編寫出String類的建構函式、拷貝建構函式、賦值函數和解構函式的面試者至少已經具備了C++基本功的60%以上!
在這個類中包括了指標類成員變數m_data,當類中包括指標類成員變數時,一定要重載其拷貝建構函式、賦值函數和解構函式,這既是對C++程式員的基本要求,也是《Effective C++》中特彆強調的條款。
仔細學習這個類,特別注意加註釋的得分點和加分點的意義,這樣就具備了60%以上的C++基本功!
試題4:請說出static和const關鍵字儘可能多的作用
解答:
static關鍵字至少有下列n個作用:
(1)函數體內static變數的作用範圍為該函數體,不同於auto變數,該變數的記憶體只被分配一次,因此其值在下次調用時仍維持上次的值;
(2)在模組內的static全域變數可以被模組內所用函數訪問,但不能被模組外其它函數訪問;
(3)在模組內的static函數只可被這一模組內的其它函數調用,這個函數的使用範圍被限制在聲明它的模組內;
(4)在類中的static成員變數屬於整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的static成員函數屬於整個類所擁有,這個函數不接收this指標,因而只能訪問類的static成員變數。
const關鍵字至少有下列n個作用:
(1)欲阻止一個變數被改變,可以使用const關鍵字。在定義該const變數時,通常需要對它進行初始化,因為以後就沒有機會再去改變它了;
(2)對指標來說,可以指定指標本身為const,也可以指定指標所指的資料為const,或二者同時指定為const;
(3)在一個函式宣告中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
(4)對於類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的成員變數;
(5)對於類的成員函數,有時候必須指定其傳回值為const類型,以使得其傳回值不為“左值”。例如:
const classA operator*(const classA& a1,const classA& a2); |
operator*的返回結果必須是一個const對象。如果不是,這樣的變態代碼也不會編譯出錯:
classA a, b, c; (a * b) = c; // 對a*b的結果賦值 |
操作(a * b) = c顯然不符合編程者的初衷,也沒有任何意義。
剖析:
驚訝嗎?小小的static和const居然有這麼多功能,我們能回答幾個?如果只能回答1~2個,那還真得閉關再好好修鍊修鍊。
這個題可以考查面試者對程式設計知識的掌握程度是初級、中級還是比較深入,沒有一定的知識廣度和深度,不可能對這個問題給出全面的解答。大多數人只能回答出static和const關鍵字的部分功能。
4.技巧題
試題1:請寫一個C函數,若處理器是Big_endian的,則返回0;若是Little_endian的,則返回1
解答:
int checkCPU() { { union w { int a; char b; } c; c.a = 1; return (c.b == 1); } } |
剖析:
嵌入式系統開發人員應該對Little-endian和Big-endian模式非常瞭解。採用Little-endian模式的CPU對運算元的存放方式是從低位元組到高位元組,而Big-endian模式對運算元的存放方式是從高位元組到低位元組。例如,16bit寬的數0x1234在Little-endian模式CPU記憶體中的存放方式(假設從地址0x4000開始存放)為:
記憶體位址 |
存放內容 |
0x4000 |
0x34 |
0x4001 |
0x12 |
而在Big-endian模式CPU記憶體中的存放方式則為:
記憶體位址 |
存放內容 |
0x4000 |
0x12 |
0x4001 |
0x34 |
32bit寬的數0x12345678在Little-endian模式CPU記憶體中的存放方式(假設從地址0x4000開始存放)為:
記憶體位址 |
存放內容 |
0x4000 |
0x78 |
0x4001 |
0x56 |
0x4002 |
0x34 |
0x4003 |
0x12 |
而在Big-endian模式CPU記憶體中的存放方式則為:
記憶體位址 |
存放內容 |
0x4000 |
0x12 |
0x4001 |
0x34 |
0x4002 |
0x56 |
0x4003 |
0x78 |
聯合體union的存放順序是所有成員都從低地址開始存放,面試者的解答利用該特性,輕鬆地獲得了CPU對記憶體採用Little-endian還是Big-endian模式讀寫。如果誰能當場給出這個解答,那簡直就是一個天才的程式員。
試題2:寫一個函數返回1+2+3+…+n的值(假定結果不會超過長整型變數的範圍)
解答:
int Sum( int n ) { return ( (long)1 + n) * n / 2; //或return (1l + n) * n / 2; } |
剖析:
對於這個題,只能說,也許最簡單的答案就是最好的答案。下面的解答,或者基於下面的解答思路去最佳化,不管怎麼“折騰”,其效率也不可能與直接return ( 1 l + n ) * n / 2相比!
int Sum( int n ) { long sum = 0; for( int i=1; i<=n; i++ ) { sum += i; } return sum; } |