10道C++輸出易錯筆試題收集

面 這些題目都是我以前準備筆試面試過程當中積累的,大部分都是知名公司的筆試題,C++基礎薄弱的很容易栽進去。我從中選了10道簡單的題,C++初學者能夠 進來挑戰下,C++大牛也能夠做爲娛樂玩下(好比下面的第6題)。爲了便於你們思考,將題目與答案分開,不過不管題目自己如何,我以爲後面的解析過程更值得學習,由於涉及不少咱們學習C++過程當中必知必會的小知識點 。ios

 

第一部分:題目

  1. 以下函數,在32 bit系統foo(2^31-3)的值是:()c++

    int foo(int x) 
    { return x&-x; }
     

    A:0 B: 1 C: 2 D: 4
     git

  2. 運算符優先級 面試

    unsigned char i=0x80;
    printf("0x%x\n", ~i>>3+1);


    輸出什麼?編程

  3. 靜態對象是否調用構造函數?數組

    #include <iostream> 
    using namespace std; 
    class A 
    { 
        public: A() { cout << "A's Constructor Called " << endl; } 
    }; 
    class B 
    { 
        static A a; 
        public: B() { cout << "B's Constructor Called " << endl; } 
    }; 
    
    int main() 
    { 
        B b; 
        return 0; 
    }
     
  4. union問題函數

    #include <stdio.h> 
    union 
    { 
        int i; 
        char x[2]; 
    }a; 
    int main() 
    { 
        a.x[0] = 10; 
        a.x[1] = 1; 
        printf("%d",a.i); 
        return 0; 
    }
  5. 下面代碼會報錯嗎?爲何?學習

    class A 
    { 
        public: 
            int m; 
            void print() { cout << "A\n"; } 
    }; 
        A *pa = 0; 
        pa->print();

     

  6. 下面代碼的輸出是什麼?(很是考基礎水平的一道題)this

    char *c[] = {"ENTER","NEW","POINT","FIRST"}; 
    char **cp[] = { c + 3 , c + 2 , c + 1 , c}; 
    char ***cpp = cp; 
    int main(void) 
    { 
        printf("%s",**++cpp); 
    
        printf("%s",*--*++cpp+3); 
    
        printf("%s",*cpp[-2]+3); 
    
        printf("%s\n",cpp[-1][-1]+1); 
    
        return 0; 
    }

     

  7. 結構體spa

    #include <stdio.h> 
    struct data 
    { 
        int a; 
        unsigned short b; 
    }; 
    int main(void) 
    { 
        data mData; 
        mData.b = 0x0102; 
        char *pData = (char *)&mData; 
        printf("%d %d", sizeof(pData), (int)(*(pData + 4))); 
        return 0; 
    }

     

  8. 改變string變量的值?

    #include <iostream> 
    #include <string> 
    using namespace std; 
    void chg_str(string str) 
    { 
        str = "ichgit"; 
    } 
    int main() 
    { 
        string s = "sarrr"; 
        chg_str(s); 
        printf("%s\n", s.c_str()); 
        cout << s << endl; 
        return 0; 
    }

     

  9. 靜態變量的輸出

    #include <stdio.h> 
    int sum(int a) 
    { 
        int c = 0; 
        static int b = 3; // 只執行一次 
        c++; 
        b += 2; 
        return (a + b + c); 
    } 
    int main() 
    { 
        int i; 
        int a = 2; 
        for(i = 0; i < 5; ++i) 
        { 
            printf("%d\n", sum(a)); 
        } 
        return 0; 
    }

     

  10. 返回值加const修飾的必要性
    你以爲下面兩種寫法有區別嗎?

    int GetInt(void) 
    const int GetInt(void)

    若是是下面的呢?其中A 爲用戶自定義的數據類型。

    A GetA(void) 
    const A GetA(void)

     


 

第二部分:答案詳細解析

  1. 以下函數,在32 bit系統foo(2^31-3)的值是:
     

    int foo(int x) { return x&-x; }

    A:0 B: 1 C: 2 D: 4 

    答案:C
    解釋:我只想說注意運算符優先級,注意^是異或而不是冪次方。
     

  2. 運算符優先級

    unsigned char i=0x80;
    printf("0x%x\n", ~i>>3+1);


    輸出什麼?

    輸出:0xfffffff7(提示:+的優先級優於>>)
    若是將unsigned去掉,則輸出0x7。
     

  3. 靜態對象是否調用構造函數?
     

    #include <iostream> 
    using namespace std; 
    class A 
    { 
        public: 
        A() 
        { 
            cout << "A's Constructor Called " << endl; 
        } 
    }; 
    class B 
    { 
        static A a; 
        public: 
        B() 
        { 
            cout << "B's Constructor Called " << endl; 
        } 
    }; 
    int main() 
    { 
        B b; 
        return 0; 
    }

    輸出:

    B's Constructor Called

     

  4. 解釋:上面的程序只是調用了B的構造函數,沒有調用A的構造函數。由於靜態成員變量只是在類中聲明,沒有定義。靜態成員變量必須在類外使用做用域標識符顯式定義。
    若是咱們沒有顯式定義靜態成員變量a,就試圖訪問它,編譯會出錯,好比下面的程序編譯出錯:
     

    #include <iostream> 
    using namespace std; 
    class A 
    { 
        int x; 
        public: 
            A() 
            { 
                cout << "A's constructor called " << endl; 
            } 
    }; 
    class B 
    { 
        static A a; 
        public: B() 
        { 
            cout << "B's constructor called " << endl; 
        } 
        static A getA() { return a; } 
    }; 
    int main() 
    { 
        B b; 
        A a = b.getA(); 
        return 0; 
    }

    輸出:

    Compiler Error: undefined reference to `B::a

    若是咱們加上a的定義,那麼上面的程序能夠正常運行,
    注意:若是A是個空類,沒有數據成員x,則就算B中的a未定義也仍是能運行成功的,便可以訪問A。
     

    #include <iostream> 
    using namespace std; 
    class A 
    { 
        int x; 
        public: 
            A() 
            { 
                cout << "A's constructor called " << endl; 
            } 
    }; 
    class B 
    { 
        static A a; 
        public: 
        B() 
        { 
            cout << "B's constructor called " << endl; 
        } 
        static A getA() { return a; } 
    }; 
    A B::a; // definition of a 
    
    int main() 
    { 
        B b1, b2, b3; 
        A a = b1.getA(); 
        return 0; 
    }

    輸出:

    A's constructor called
    B's constructor called
    B's constructor called
    B's constructor called

    上面的程序調用B的構造函數3次,可是隻調用A的構造函數一次,由於靜態成員變量被全部對象共享,這也是它被稱爲類變量的緣由。同時,靜態成員變量也能夠經過類名直接訪問,好比下面的程序沒有經過任何類對象訪問,只是經過類訪問a。

    int main() 
    { 
        // static member 'a' is accessed without any object of B 
        A a = B::getA(); 
        return 0; 
    }
     

    輸出:

    A's constructor called

     

  5. union問題

    #include <stdio.h> 
    union 
    { 
        int i; 
        char x[2]; 
    }a; 
    
    int main() 
    { 
        a.x[0] = 10; 
        a.x[1] = 1; 
        printf("%d",a.i);     
        return 0; 
    }

    輸出:266,本身畫個內存結構圖就知道了,注意union的存放順序是全部成員都從低地址開始存放。Union的大小爲其內部全部變量的最大值,而且按照類型最大值的整數倍進行內存對齊。

     

  6. 下面代碼會報錯嗎?爲何?

    class A 
    { 
        public: 
            int m; 
            void print() { cout << "A\n"; } 
    }; 
    A *pa = 0; 
    pa->print();

    答案:正常輸出。上面的代碼能夠這樣理解(這很是重要):

    void print(A *this) 
    { 
        cout << "A\n"; 
    } 
    A *pa = 0; 
    print_A();

    也就是:並非類沒有初始化就不能調用類的成員函數,若是成員函數只是簡單的打印個東西,沒有調用類成員啥的就不會報段錯誤。
     

  7. 下面代碼的輸出是什麼?(很是考基礎水平的一道題)

    char *c[] = {"ENTER","NEW","POINT","FIRST"}; 
    char **cp[] = { c + 3 , c + 2 , c + 1 , c}; 
    char ***cpp = cp; 
    int main(void) 
    { 
        printf("%s",**++cpp); 
        printf("%s",*--*++cpp+3); 
        printf("%s",*cpp[-2]+3); 
        printf("%s\n",cpp[-1][-1]+1); 
        return 0; 
    }

    解答:
    c是一個指針數組,每一個數組元素都是char*類型的指針,值分別是那些字符串(的首地址):

    c[0] = "ENTER"
    c[1] = "NEW"
    c[2] = "POINT"
    c[3] = "FIRST"

    而[]和*是本質同樣的運算,即c[i]=*(c+i)

    c和c+i都是char *[]類型,它能夠退化成char **類型,再看cp,它正好是一個char **的數組,來看它的值:

    cp[0] = c + 3
    cp[1] = c + 2
    cp[2] = c + 1
    cp[3] = c

    引用後就有:cp[0][0]=*(c + 3)=c[3]="FIRST",以此類推。

    cp是char **[]類型,它能夠退化成char ***類型,看最後的cpp,它正是char ***類型,它是一個指針變量,和上面兩個不一樣,上面兩個是數組。

    這樣分析事後,下面的解析就一目瞭然了:

    printf("%s",**++cpp);
    ++cpp的值是cp+1,引用一次後是cp[1]再引用是*cp[1]=c[2]="POINT",第一句的輸出
    
    printf("%s",*--*++cpp+3);
    再++cpp的值是cp+2,引用一次是cp[2]=c+1,再對這進行--,減後是c再引用是c[0]="ENTER"再+3,字符串指針指到"ER",輸出是"ER"
    
    printf("%s",*cpp[-2]+3);
    這時cpp的值是cp+2,cpp[-2]=*(cpp-2)=*(cp+2-2)=cp[0]=c+3,再引用是c[3]="FIRST",+3 字符串指針指到"ST",輸出是"ST"
    
    printf("%s\n",cpp[-1][-1]+1);
    cpp仍是cp+2,cpp[-1]=*(cpp-1)=*(cp+2-1)=cp[1]=c+2,再[-1]得*(c+2-1)=c[1]="NEW",+1字符串指針指到"EW",輸出是"EW"。

     

  8. 結構體

    #include <stdio.h> 
    struct data 
    { 
        int a; 
        unsigned short b; 
    }; 
    int main(void) 
    { 
        data mData; 
        mData.b = 0x0102; 
        char *pData = (char *)&mData; 
        printf("%d %d", sizeof(pData), (int)(*(pData + 4))); 
        return 0; 
    }

    輸出:4 2 

    說明:通常變量都是從高到低分配內存地址,但對於結構體來講,結構體的成員在內存中順序存放,所佔內存地址依次增高,第一個成員處於低地址處,最後一個成員處於最高地址處,但結構體成員的內存分配不必定是連續的,編譯器會對其成員變量依據前面介紹的 「對齊」原則進行處理。

    補充知識點:

    除了棧之外,堆、只讀數據區、全局變量地址增加方向都是從低到高的。

 

8.改變string變量的值?
 

#include <iostream> 
#include <string> 
using namespace std; 
void chg_str(string str) 
{ 
    str = "ichgit"; 
} 
int main() 
{ 
    string s = "sarrr"; 
    chg_str(s); 
    printf("%s\n", s.c_str()); 
    cout << s << endl; 
    return 0; 
}

輸出:仍爲「sarrr」。
解釋:string是傳值參數,不能修改其值。要想改變string變量的值,能夠改成傳地址方式:

#include <iostream> 
#include <string> 
using namespace std; 
void chg_str(string *str) 
{ 
    *str = "ichgit"; 
} 
int main() 
{ 
    string s = "sarrr"; 
    chg_str(&s); 
    printf("%s\n", s.c_str()); 
    cout << s << endl; 
    return 0; 
}
  1. 靜態變量的輸出

    #include <stdio.h> 
    int sum(int a) 
    { 
        int c = 0; 
        static int b = 3; // 只執行一次 
        c++; 
        b += 2; 
        return (a + b + c); 
    } 
    int main() 
    { 
        int i; 
        int a = 2; 
        for(i = 0; i < 5; ++i) 
        { 
            printf("%d\n", sum(a)); 
        } 
        return 0; 
    }

    輸出:8 10 12 14 16
    解釋:存儲在靜態數據區的變量會在程序剛開始運行時就完成初始化,也是惟一的一次初始化,此後該初始化再也不執行,至關於一次執行後就做廢,靜態局部變量保存了前次被調用後留下的值。
     

  2. 返回值加const修飾的必要性
    你以爲下面兩種寫法有區別嗎?
     

    int GetInt(void) 
    const int GetInt(void)

    若是是下面的呢?其中A 爲用戶自定義的數據類型。
     

    A GetA(void) 
    const A GetA(void)

    答案:沒有任何區別。
    解釋:若是函數返回值採用「值傳遞方式」,因爲函數會把返回值複製到外部臨時的存儲單元中,加const 修飾沒有任何價值。因此,對於值傳遞來講,加const沒有太多意義。
    因此:

    在編程中要儘量多的使用const(好比函數參數採用const&修飾),這樣能夠得到編譯器的幫助,以便寫出健壯性的代碼

    • 不要把函數int GetInt(void) 寫成const int GetInt(void)。
    • 不要把函數A GetA(void) 寫成const A GetA(void)。
相關文章
相關標籤/搜索