C++ 笔记(33)— C/C++ 程序员常见面试试题深入剖析
1. 找錯題
試題1:
void test1()
{char string[10];char* str1 = "0123456789";strcpy(string, str1);
}
錯誤原因:字符串 str1 需要 11 個字節(jié)才能存放下(包括末尾的 \0),而 string只有 10 個字節(jié)的空間,strcpy會導致數(shù)組越界;
?
試題2:
void test2()
{char string[10], str1[10];int i;for(i=0; i<10; i++){str1[i] = 'a';}strcpy(string, str1);
}
如果面試者指出字符數(shù)組 str1不能在數(shù)組內(nèi)結束可以給3分;
如果面試者指出 strcpy(string, str1)調(diào)用使得從 str1內(nèi)存起復制到 string內(nèi)存起所復制的字節(jié)數(shù)具有不確定性可以給7分,在此基礎上指出庫函數(shù)strcpy工作方式的給10分;
?
試題3:
void test3(char* str1)
{char string[10];if(strlen(str1) <= 10 ){strcpy(string, str1);}
}
if(strlen(str1) <= 10)應改為 if(strlen(str1) < 10),因為 strlen的結果未統(tǒng)計 \0所占用的 1 個字節(jié)。
?
試題4:
void GetMemory( char *p )
{p = (char *) malloc( 100 );
}void Test( void )
{char *str = NULL;GetMemory( str ); strcpy( str, "hello world" );printf( str );
}
GetMemory( char *p )函數(shù)的形參為字符串指針,在函數(shù)內(nèi)部修改形參并不能真正的改變傳入形參的值,執(zhí)行完
char *str = NULL;
GetMemory( str );
后的 str仍然為 NULL;
?
試題5:
char *GetMemory( void )
{ char p[] = "hello world"; return p;
}void Test( void )
{ char *str = NULL; str = GetMemory(); printf( str );
}
p[]數(shù)組為函數(shù)內(nèi)的局部自動變量,在函數(shù)返回后,內(nèi)存已經(jīng)被釋放。這是許多程序員常犯的錯誤,其根源在于不理解變量的生存期。
?
試題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 );
}
Test函數(shù)中也未對 malloc的內(nèi)存進行釋放。
?
GetMemory避免了試題4的問題,傳入 GetMemory的參數(shù)為字符串指針的指針,但是在GetMemory中執(zhí)行申請內(nèi)存及賦值語句
*p = (char *) malloc( num );
后未判斷內(nèi)存是否申請成功,應加上:
if (*p == NULL)
{...//進行申請內(nèi)存失敗處理
}
?
試題7:
void Test( void )
{char *str = (char *) malloc( 100 );strcpy( str, "hello" );free( str ); ... //省略的其它語句
}
存在與試題6同樣的問題,在執(zhí)行
char *str = (char *) malloc(100);
?
后未進行內(nèi)存是否申請成功的判斷;另外,在 free(str)后未置 str為空,導致可能變成一個“野”指針,應加上:
str = NULL;
試題8:
swap( int* p1,int* p2 )
{int *p;*p = *p1;*p1 = *p2;*p2 = *p;
}
在 swap函數(shù)中,p是一個“野”指針,有可能指向系統(tǒng)區(qū),導致程序運行的崩潰。運行報錯:
main.cpp:7:13: warning: ‘p’ is used uninitialized in this function [-Wuninitialized]*p = *p1;^
Segmentation fault (core dumped)
該程序應該改為:
swap( int* p1,int* p2 )
{int p;p = *p1;*p1 = *p2;*p2 = p;
}
2. 編寫一個標準strcpy函數(shù)
庫函數(shù) strcpy的工作方式,如果編寫一個標準 strcpy函數(shù)的總分值為 10,下面給出幾個不同得分的答案:
?
2 分
void strcpy( char *strDest, char *strSrc )
{while( (*strDest++ = *strSrc++) != '\0' );
}
4 分
void strcpy( char *strDest, const char *strSrc )
//將源字符串加const,表明其為輸入?yún)?shù),加2分
{while( (*strDest++ = * strSrc++) != '\0' );
}
7 分
void strcpy(char *strDest, const char *strSrc)
{//對源地址和目的地址加非0斷言,加3分assert( (strDest != NULL) && (strSrc != NULL) );while( (*strDest++ = * strSrc++) != '\0' );
}
10 分
//為了實現(xiàn)鏈式操作,將目的地址返回,加3分!char * strcpy( char *strDest, const char *strSrc )
{assert( (strDest != NULL) && (strSrc != NULL) );char *address = strDest; while( (*strDest++ = * strSrc++) != '\0' ); return address;
}
3. 編寫一個標準strlen函數(shù)
切記 strlen它沒有包括字符串末尾的 \0。
int strlen(const char *str)
{assert(strt != NULL); //斷言字符串地址非0int len;while( (*str++) != '\0' ) { len++; } return len;
}
4. 分別給出 BOOL,int,float,指針變量 與“零值”比較的 if 語句
假設變量名為 var,BOOL型變量
if(!var)
int型變量
if(var==0)
float型變量
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指針變量
if(var==NULL)
bool型變量的0判斷完全可以寫成if(var==0);int型變量也可以寫成if(!var);- 指針變量的判斷也可以寫成
if(!var);
上述寫法雖然程序都能正確運行,但是未能清晰地表達程序的意思。
?
一般的,如果想讓 if判斷一個布爾變量的“真”、“假”,應直接使用 if(var)、if(!var),表明其為“邏輯”判斷;如果用 if判斷一個數(shù)值型變量( short、int、long等),應該用 if(var==0),表明是與 0進行“數(shù)值”上的比較;而判斷指針則適宜用 if(var==NULL),這是一種很好的編程習慣。
?
浮點型變量并不精確,所以不可將 float變量用 == 或 !=與數(shù)字比較,應該設法轉化成 >=或 <=形式。如果寫成 if (x == 0.0),則判為錯,得0分。
5. 計算sizeof的值
請計算 sizeof的值
void Func(char str[100])
{sizeof(str) = ?
}void *p = malloc(100);
sizeof(p) = ?
答案
sizeof(str) = 4
sizeof(p) = 4
Func( char str[100] )函數(shù)中數(shù)組名作為函數(shù)形參時,在函數(shù)體內(nèi),數(shù)組名失去了本身的內(nèi)涵,僅僅只是一個指針;在失去其內(nèi)涵的同時,它還失去了其常量特性,可以作自增、自減等操作,可以被修改。
?
數(shù)組名的本質(zhì)如下:
- 數(shù)組名指代一種數(shù)據(jù)結構,這種數(shù)據(jù)結構就是數(shù)組;
char str[10];
cout << sizeof(str) << endl;
輸出結果為 10,str指代數(shù)據(jù)結構 char[10]。
- 數(shù)組名可以轉換為指向其指代實體的指針,而且是一個指針常量,不能作自增、自減等操作,不能被修改;
char str[10];
str++; //編譯出錯,提示str不是左值
- 數(shù)組名作為函數(shù)形參時,淪為普通指針;
在一般的平臺上指針的長度(占用內(nèi)存的大小)為 4 字節(jié),故 sizeof(str) 、sizeof(p) 都為 4。
?
6. 寫一個“標準”宏MIN,這個宏輸入兩個參數(shù)并返回較小的一個
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
對宏定義的使用要非常小心,特別要注意兩個問題:
- 謹慎地將宏定義中的“參數(shù)”和整個宏用用括弧括起來。所以,嚴格地講,下述解答都應判0分;
#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )
- 防止宏的副作用;
宏定義
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
對 MIN(*p++, b)的作用結果是:
((*p++) <= (b) ? (*p++) : (*p++))
這個表達式會產(chǎn)生副作用,指針 p會作三次 ++自增操作。
?
除此之外,另一個應該判 0分的解答是:
#define MIN(A,B) ((A) <= (B) ? (A) : (B));
這個解答在宏定義的后面加 ;,顯示編寫者對宏的概念模糊不清,只能被無情地判 0 分并被面試官淘汰。
?
7. 編寫一個函數(shù),作用是把一個char組成的字符串循環(huán)右移n個。比如原來是“abcdefghi”如果n=2,移位后應該是“hiabcdefgh”
//pStr是指向以'\0'結尾的字符串的指針
//steps是要求移動的nvoid LoopMove(char *pStr, int steps)
{//請?zhí)畛?..
}
正確解答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)) = '\0';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);
}
8. 請說出static和const關鍵字盡可能多的作用
static關鍵字至少有下列n個作用:
(1)函數(shù)體內(nèi) static變量的作用范圍為該函數(shù)體,不同于 auto變量,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時仍維持上次的值;
(2)在模塊內(nèi)的 static全局變量可以被模塊內(nèi)所用函數(shù)訪問,但不能被模塊外其它函數(shù)訪問;
(3)在模塊內(nèi)的 static函數(shù)只可被這一模塊內(nèi)的其它函數(shù)調(diào)用,這個函數(shù)的使用范圍被限制在聲明它的模塊內(nèi);
(4)在類中的 static成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的 static成員函數(shù)屬于整個類所擁有,這個函數(shù)不接收 this指針,因而只能訪問類的 static成員變量。
?
?
const關鍵字至少有下列n個作用:
(1)欲阻止一個變量被改變,可以使用 const關鍵字。在定義該 const變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本身為 const,也可以指定指針所指的數(shù)據(jù)為 const,或二者同時指定為 const;
(3)在一個函數(shù)聲明中,const可以修飾形參,表明它是一個輸入?yún)?shù),在函數(shù)內(nèi)部不能改變其值;
(4)對于類的成員函數(shù),若指定其為 const類型,則表明其是一個常函數(shù),不能修改類的成員變量;
(5)對于類的成員函數(shù),有時候必須指定其返回值為 const類型,以使得其返回值不為“左值”。例如:
const classA operator*(const classA& a1,const classA& a2);
9. 編寫類String的構造函數(shù)、析構函數(shù)和賦值函數(shù)
已知類 String的原型為:
class String
{ public: String(const char *str = NULL); // 普通構造函數(shù) String(const String &other); // 拷貝構造函數(shù) ~String(void); // 析構函數(shù) String & operate =(const String &other); // 賦值函數(shù) private: char *m_data; // 用于保存字符串
};
- 普通構造函數(shù)
String::String(const char *str)
{if(str==NULL) {m_data = new char[1]; // 得分點:對空字符串自動申請存放結束標志'\0'的空//加分點:對m_data加NULL 判斷*m_data = '\0'; } else{int length = strlen(str); m_data = new char[length+1]; // 若能加 NULL 判斷則更好 strcpy(m_data, str); }
}
- 析構函數(shù)
String::~String(void)
{delete []m_data; // 或delete m_data;
}
- 拷貝構造函數(shù)
String::String(const String &other) // 得分點:輸入?yún)?shù)為const型
{ int length = strlen(other.m_data); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷strcpy(m_data, other.m_data);
}
- 賦值函數(shù)
String & String::operate =(const String &other) // 得分點:輸入?yún)?shù)為const型
{ if(this == &other) //得分點:檢查自賦值return *this; delete [] m_data; //得分點:釋放原有的內(nèi)存資源int length = strlen( other.m_data ); m_data = new char[length+1]; //加分點:對m_data加NULL 判斷strcpy( m_data, other.m_data ); return *this; //得分點:返回本對象的引用
}
能夠準確無誤地編寫出String類的構造函數(shù)、拷貝構造函數(shù)、賦值函數(shù)和析構函數(shù)的面試者至少已經(jīng)具備了C++基本功的60%以上!
的面試者至少已經(jīng)具備了C++基本功的60%以上!
?
在這個類中包括了指針類成員變量m_data,當類中包括指針類成員變量時,一定要重載其拷貝構造函數(shù)、賦值函數(shù)和析構函數(shù),這既是對C++程序員的基本要求,也是《Effective C++》中特別強調(diào)的條款。
總結
以上是生活随笔為你收集整理的C++ 笔记(33)— C/C++ 程序员常见面试试题深入剖析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国塑料管的制造行业
- 下一篇: 2022-2028年中国塑料板的制造行业