隨筆-144  評論-80  文章-1  trackbacks-0
          C/C++ 程序設計員應聘常見面試試題深入剖析
          時間:2006-5-30 17:06:00 ?
          .引言

            本文的寫作目的并不在于提供C/C++程序員求職面試指導,而旨在從技術上分析面試題的內涵。文中的大多數面試題來自各大論壇,部分試題解答也參考了網友的意見。

            許多面試題看似簡單,卻需要深厚的基本功才能給出完美的解答。企業要求面試者寫一個最簡單的strcpy函數都可看出面試者在技術上究竟達到了怎樣的程度,我們能真正寫好一個strcpy函數嗎?我們都覺得自己能,可是我們寫出的strcpy很可能只能拿到10分中的2分。讀者可從本文看到strcpy 函數從2分到10分解答的例子,看看自己屬于什么樣的層次。此外,還有一些面試題考查面試者敏捷的思維能力。

            分析這些面試題,本身包含很強的趣味性;而作為一名研發人員,通過對這些面試題的深入剖析則可進一步增強自身的內功。

            2.找錯題

            試題1:

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

            試題2:

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

            試題3:

          void test3(char* str1)
          {
           char string[10];
           if( strlen( str1 ) <= 10 )
           {
            strcpy( string, str1 );
           }
          }

            解答:

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

            對試題2,如果面試者指出字符數組str1不能在數組內結束可以給3分;如果面試者指出strcpy(string, str1)調用使得從str1內存起復制到string內存起所復制的字節數具有不確定性可以給7分,在此基礎上指出庫函數strcpy工作方式的給10 分;

            對試題3,if(strlen(str1) <= 10)應改為if(strlen(str1) < 10),因為strlen的結果未統計’\0’所占用的1個字節。

            剖析:

            考查對基本功的掌握:

            (1)字符串以’\0’結尾;

            (2)對數組越界把握的敏感度;

            (3)庫函數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;
          }

            從2分到10分的幾個答案我們可以清楚的看到,小小的strcpy竟然暗藏著這么多玄機,真不是蓋的!需要多么扎實的基本功才能寫一個完美的strcpy啊!

            (4)對strlen的掌握,它沒有包括字符串末尾的'\0'。

            讀者看了不同分值的strcpy版本,應該也可以寫出一個10分的strlen函數了,完美的版本為: int strlen( const char *str ) //輸入參數const

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

            試題4:

          void GetMemory( char *p )
          {
           p = (char *) malloc( 100 );
          }

          void Test( void )
          {
           char *str = NULL;
           GetMemory( str );
           strcpy( str, "hello world" );
           printf( str );
          }

            試題5:

          char *GetMemory( void )
          {
           char p[] = "hello world";
           return p;
          }

          void Test( void )
          {
           char *str = NULL;
           str = GetMemory();
           printf( str );
          }

            試題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 );
          }

            試題7:

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

            解答:

            試題4傳入中GetMemory( char *p )函數的形參為字符串指針,在函數內部修改形參并不能真正的改變傳入形參的值,執行完

          char *str = NULL;
          GetMemory( str );

            后的str仍然為NULL;

            試題5中

          char p[] = "hello world";
          return p;

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

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

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

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

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

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

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

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

          str = NULL;

            試題6的Test函數中也未對malloc的內存進行釋放。

            剖析:

            試題4~7考查面試者對內存操作的理解程度,基本功扎實的面試者一般都能正確的回答其中50~60的錯誤。但是要完全解答正確,卻也絕非易事。

            對內存操作的考查主要集中在:

            (1)指針的理解;

            (2)變量的生存期及作用范圍;

            (3)良好的動態內存申請和釋放習慣。

            再看看下面的一段程序有什么錯誤:

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

            在swap函數中,p是一個“野”指針,有可能指向系統區,導致程序運行的崩潰。在VC++中DEBUG運行時提示錯誤“Access Violation”。該程序應該改為:

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

           3.內功題

            試題1:分別給出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)

            剖析:

            考查對0值判斷的“內功”,BOOL型變量的0判斷完全可以寫成if(var==0),而int型變量也可以寫成if(!var),指針變量的判斷也可以寫成if(!var),上述寫法雖然程序都能正確運行,但是未能清晰地表達程序的意思。


            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開始存放)為:

          內存地址存放內容
          0x40000x34
          0x40010x12

            而在Big-endian模式CPU內存中的存放方式則為:

          內存地址存放內容
          0x40000x12
          0x40010x34

            32bit寬的數0x12345678在Little-endian模式CPU內存中的存放方式(假設從地址0x4000開始存放)為:

          內存地址存放內容
          0x40000x78
          0x40010x56
          0x40020x34
          0x40030x12

            而在Big-endian模式CPU內存中的存放方式則為:

          內存地址存放內容
          0x40000x12
          0x40010x34
          0x40020x56
          0x40030x78

            聯合體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;
          }

            所以程序員們需要敏感地將數學等知識用在程序設計中。
          posted on 2006-07-27 14:08 小力力力 閱讀(3551) 評論(2)  編輯  收藏 所屬分類: C/C++

          評論:
          # re: C/C++ 程序設計員應聘常見面試試題深入剖析 2006-10-22 11:32 | wj
          very good
          learn much from you  回復  更多評論
            
          # re: C/C++ 程序設計員應聘常見面試試題深入剖析 2007-06-18 16:11 | 果果
          謝謝  回復  更多評論
            
          主站蜘蛛池模板: 陆良县| 临桂县| 嘉善县| 舟曲县| 临潭县| 宝兴县| 潍坊市| 新源县| 临城县| 乐山市| 玉屏| 扎赉特旗| 沅陵县| 米易县| 东方市| 祥云县| 南部县| 吉水县| 太和县| 灵石县| 澜沧| 扎囊县| 忻城县| 武平县| 乳源| 怀集县| 沛县| 安龙县| 吉安县| 贡嘎县| 栾城县| 深圳市| 育儿| 赤壁市| 高尔夫| 永和县| 海南省| 盐亭县| 通山县| 荆门市| 四会市|