日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C++ 笔记(33)— C/C++ 程序员常见面试试题深入剖析

發布時間:2023/11/28 生活经验 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ 笔记(33)— C/C++ 程序员常见面试试题深入剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 找錯題

試題1:

void test1()
{char string[10];char* str1 = "0123456789";strcpy(string, str1);
} 

錯誤原因:字符串 str1 需要 11 個字節才能存放下(包括末尾的 \0),而 string只有 10 個字節的空間,strcpy會導致數組越界;
?

試題2:

void test2()
{char string[10], str1[10];int i;for(i=0; i<10; i++){str1[i] = 'a';}strcpy(string, str1);
} 

如果面試者指出字符數組 str1不能在數組內結束可以給3分;
如果面試者指出 strcpy(string, str1)調用使得從 str1內存起復制到 string內存起所復制的字節數具有不確定性可以給7分,在此基礎上指出庫函數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的結果未統計 \0所占用的 1 個字節。
?

試題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 )函數的形參為字符串指針,在函數內部修改形參并不能真正的改變傳入形參的值,執行完

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[]數組為函數內的局部自動變量,在函數返回后,內存已經被釋放。這是許多程序員常犯的錯誤,其根源在于不理解變量的生存期。
?

試題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函數中也未對 malloc的內存進行釋放。
?

GetMemory避免了試題4的問題,傳入 GetMemory的參數為字符串指針的指針,但是在GetMemory中執行申請內存及賦值語句

*p = (char *) malloc( num ); 

后未判斷內存是否申請成功,應加上:

if (*p == NULL)
{...//進行申請內存失敗處理
}

?

試題7:

void Test( void )
{char *str = (char *) malloc( 100 );strcpy( str, "hello" );free( str ); ... //省略的其它語句
} 

存在與試題6同樣的問題,在執行

char *str = (char *) malloc(100); 

?

后未進行內存是否申請成功的判斷;另外,在 free(str)后未置 str為空,導致可能變成一個“野”指針,應加上:

str = NULL; 

試題8:

swap( int* p1,int* p2 )
{int *p;*p = *p1;*p1 = *p2;*p2 = *p;
} 

swap函數中,p是一個“野”指針,有可能指向系統區,導致程序運行的崩潰。運行報錯:

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函數

庫函數 strcpy的工作方式,如果編寫一個標準 strcpy函數的總分值為 10,下面給出幾個不同得分的答案:
?

2 分

void strcpy( char *strDest, char *strSrc )
{while( (*strDest++ = *strSrc++) != '\0' );
}

4 分

void strcpy( char *strDest, const char *strSrc ) 
//將源字符串加const,表明其為輸入參數,加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 分

//為了實現鏈式操作,將目的地址返回,加3分!char * strcpy( char *strDest, const char *strSrc ) 
{assert( (strDest != NULL) && (strSrc != NULL) );char *address = strDest; while( (*strDest++ = * strSrc++) != '\0' ); return address;
} 

3. 編寫一個標準strlen函數

切記 strlen它沒有包括字符串末尾的 \0

int strlen(const char *str) 
{assert(strt != NULL); //斷言字符串地址非0int len;while( (*str++) != '\0' ) { len++; } return len;
}

4. 分別給出 BOOL,int,float,指針變量 與“零值”比較的 if 語句

假設變量名為 varBOOL型變量

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判斷一個數值型變量( shortintlong等),應該用 if(var==0),表明是與 0進行“數值”上的比較;而判斷指針則適宜用 if(var==NULL),這是一種很好的編程習慣。
?

浮點型變量并不精確,所以不可將 float變量用 ==!=與數字比較,應該設法轉化成 >=<=形式。如果寫成 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] )函數中數組名作為函數形參時,在函數體內,數組名失去了本身的內涵,僅僅只是一個指針;在失去其內涵的同時,它還失去了其常量特性,可以作自增、自減等操作,可以被修改。
?

數組名的本質如下:

  1. 數組名指代一種數據結構,這種數據結構就是數組;
char str[10];
cout << sizeof(str) << endl; 

輸出結果為 10,str指代數據結構 char[10]

  1. 數組名可以轉換為指向其指代實體的指針,而且是一個指針常量,不能作自增、自減等操作,不能被修改;
char str[10]; 
str++; //編譯出錯,提示str不是左值
  1. 數組名作為函數形參時,淪為普通指針;

在一般的平臺上指針的長度(占用內存的大小)為 4 字節,故 sizeof(str)sizeof(p) 都為 4。
?

6. 寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個

#define MIN(A,B) ((A) <= (B) ? (A) : (B)) 

對宏定義的使用要非常小心,特別要注意兩個問題:

  1. 謹慎地將宏定義中的“參數”和整個宏用用括弧括起來。所以,嚴格地講,下述解答都應判0分;
#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B ) 
  1. 防止宏的副作用;

宏定義

#define MIN(A,B) ((A) <= (B) ? (A) : (B))

MIN(*p++, b)的作用結果是:

((*p++) <= (b) ? (*p++) : (*p++))

這個表達式會產生副作用,指針 p會作三次 ++自增操作。
?

除此之外,另一個應該判 0分的解答是:

#define MIN(A,B) ((A) <= (B) ? (A) : (B));  

這個解答在宏定義的后面加 ;,顯示編寫者對宏的概念模糊不清,只能被無情地判 0 分并被面試官淘汰。
?

7. 編寫一個函數,作用是把一個char組成的字符串循環右移n個。比如原來是“abcdefghi”如果n=2,移位后應該是“hiabcdefgh”

//pStr是指向以'\0'結尾的字符串的指針
//steps是要求移動的nvoid 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)) = '\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)函數體內 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); 

9. 編寫類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; // 用于保存字符串 
};
  1. 普通構造函數
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); }
}
  1. 析構函數
String::~String(void) 
{delete []m_data; // 或delete m_data;
}
  1. 拷貝構造函數
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); 
}
  1. 賦值函數
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%以上!

的面試者至少已經具備了C++基本功的60%以上!

?

在這個類中包括了指針類成員變量m_data,當類中包括指針類成員變量時,一定要重載其拷貝構造函數、賦值函數和析構函數,這既是對C++程序員的基本要求,也是《Effective C++》中特別強調的條款。

總結

以上是生活随笔為你收集整理的C++ 笔记(33)— C/C++ 程序员常见面试试题深入剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。