10道C++輸出易錯(cuò)筆試題收集(敢進(jìn)來(lái)挑戰(zhàn)嗎?)

下面這些題目都是我之前準(zhǔn)備筆試面試過(guò)程中積累的,大部分都是知名公司的筆試題,C++基礎(chǔ)薄弱的很容易栽進(jìn)去。我從中選了10道簡(jiǎn)單的題,C++初學(xué)者可以進(jìn)來(lái)挑戰(zhàn)下,C++大牛也可以作為娛樂(lè)玩下(比如下面的第6題)。為了便于大家思考,將題目與答案分開(kāi),不過(guò)無(wú)論題目本身如何,我覺(jué)得后面的解析過(guò)程更值得學(xué)習(xí),因?yàn)樯婕昂芏辔覀儗W(xué)習(xí)C++過(guò)程中必知必會(huì)的小知識(shí)點(diǎn) 。

第一部分:題目

  1. 如下函數(shù),在32 bit系統(tǒng)foo(2^31-3)的值是:()
    int foo(int x)
    {
        return x&-x;
    }
    

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

  1. 運(yùn)算符優(yōu)先級(jí)
    unsigned char i=0x80;
    printf("0x%x\n", ~i>>3+1);
    輸出什么?

  2. 靜態(tài)對(duì)象是否調(diào)用構(gòu)造函數(shù)?

    #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;
    }
    
  3. union問(wèn)題

    #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;
    }
    
  4. 下面代碼會(huì)報(bào)錯(cuò)嗎?為什么?

class A {
public:
     int m;
     void print() {  cout << "A\n";  } 
};
 A *pa = 0;
 pa->print();
  1. 下面代碼的輸出是什么?(非常考基礎(chǔ)水平的一道題)
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;  
}  
  1. 結(jié)構(gòu)體
#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;
}
  1. 改變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. 靜態(tài)變量的輸出
#include <stdio.h>
int sum(int a) {
    int c = 0;
    static int b = 3; // 只執(zhí)行一次
    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;
}
  1. 返回值加const修飾的必要性
    你覺(jué)得下面兩種寫法有區(qū)別嗎?
int GetInt(void) 
const int GetInt(void)

如果是下面的呢?其中A 為用戶自定義的數(shù)據(jù)類型。

A GetA(void)
const A GetA(void)

<p>
</p>


第二部分:答案詳細(xì)解析

  1. 如下函數(shù),在32 bit系統(tǒng)foo(2^31-3)的值是:
    int foo(int x)
    {
        return x&-x;
    }
    

A:0 B: 1 C: 2 D: 4
答案:C
解釋:我只想說(shuō)注意運(yùn)算符優(yōu)先級(jí),注意^是異或而不是冪次方。

  1. 運(yùn)算符優(yōu)先級(jí)
    unsigned char i=0x80;
    printf("0x%x\n", ~i>>3+1);輸出什么?
    輸出:0xfffffff7(提示:+的優(yōu)先級(jí)優(yōu)于>>)
    如果將unsigned去掉,則輸出0x7。

  2. 靜態(tài)對(duì)象是否調(diào)用構(gòu)造函數(shù)?

    #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

解釋:上面的程序只是調(diào)用了B的構(gòu)造函數(shù),沒(méi)有調(diào)用A的構(gòu)造函數(shù)。因?yàn)殪o態(tài)成員變量只是在類中聲明,沒(méi)有定義。靜態(tài)成員變量必須在類外使用作用域標(biāo)識(shí)符顯式定義。
如果我們沒(méi)有顯式定義靜態(tài)成員變量a,就試圖訪問(wèn)它,編譯會(huì)出錯(cuò),比如下面的程序編譯出錯(cuò):
```cpp
#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的定義,那么上面的程序可以正常運(yùn)行,
注意:如果A是個(gè)空類,沒(méi)有數(shù)據(jù)成員x,則就算B中的a未定義也還是能運(yùn)行成功的,即可以訪問(wèn)A。
```cpp
#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

上面的程序調(diào)用B的構(gòu)造函數(shù)3次,但是只調(diào)用A的構(gòu)造函數(shù)一次,因?yàn)殪o態(tài)成員變量被所有對(duì)象共享,這也是它被稱為類變量的原因。同時(shí),靜態(tài)成員變量也可以通過(guò)類名直接訪問(wèn),比如下面的程序沒(méi)有通過(guò)任何類對(duì)象訪問(wèn),只是通過(guò)類訪問(wèn)a。
```cpp
int main()
{
// static member 'a' is accessed without any object of B
A a = B::getA();

    return 0;
}
```

輸出:

A's constructor called

  1. union問(wèn)題
    #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,自己畫個(gè)內(nèi)存結(jié)構(gòu)圖就知道了,注意union的存放順序是所有成員都從低地址開(kāi)始存放。Union的大小為其內(nèi)部所有變量的最大值,并且按照類型最大值的整數(shù)倍進(jìn)行內(nèi)存對(duì)齊。

  1. 下面代碼會(huì)報(bào)錯(cuò)嗎?為什么?
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(); 

也就是:并不是類沒(méi)有初始化就不能調(diào)用類的成員函數(shù),如果成員函數(shù)只是簡(jiǎn)單的打印個(gè)東西,沒(méi)有調(diào)用類成員啥的就不會(huì)報(bào)段錯(cuò)誤。

  1. 下面代碼的輸出是什么?(非??蓟A(chǔ)水平的一道題)
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是一個(gè)指針數(shù)組,每個(gè)數(shù)組元素都是char類型的指針*,值分別是那些字符串(的首地址):

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

而[]和*是本質(zhì)一樣的運(yùn)算,即c[i]=*(c+i)

c和c+i都是char *[]類型,它可以退化成char **類型,再看cp,它正好是一個(gè)char **的數(shù)組,來(lái)看它的值:

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 ***類型,它是一個(gè)指針變量,和上面兩個(gè)不同,上面兩個(gè)是數(shù)組。

這樣分析過(guò)后,下面的解析就一目了然了:

  • printf("%s",**++cpp);
    ++cpp的值是cp+1,引用一次后是cp[1]再引用是*cp[1]=c[2]="POINT",第一句的輸出
  • printf("%s",*--*++cpp+3);
    再++cpp的值是cp+2,引用一次是cp[2]=c+1,再對(duì)這進(jìn)行--,減后是c再引用是c[0]="ENTER"再+3,字符串指針指到"ER",輸出是"ER"
  • printf("%s",*cpp[-2]+3);
    這時(shí)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"。
  1. 結(jié)構(gòu)體
#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
說(shuō)明:一般變量都是從高到低分配內(nèi)存地址,但對(duì)于結(jié)構(gòu)體來(lái)說(shuō),結(jié)構(gòu)體的成員在內(nèi)存中順序存放,所占內(nèi)存地址依次增高,第一個(gè)成員處于低地址處,最后一個(gè)成員處于最高地址處,但結(jié)構(gòu)體成員的內(nèi)存分配不一定是連續(xù)的,編譯器會(huì)對(duì)其成員變量依據(jù)前面介紹的 “對(duì)齊”原則進(jìn)行處理。

結(jié)構(gòu)體內(nèi)存分布圖

補(bǔ)充知識(shí)點(diǎn):

除了棧以外,堆、只讀數(shù)據(jù)區(qū)、全局變量地址增長(zhǎng)方向都是從低到高的。

  1. 改變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是傳值參數(shù),不能修改其值。要想改變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. 靜態(tài)變量的輸出
#include <stdio.h>
int sum(int a) {
    int c = 0;
    static int b = 3; // 只執(zhí)行一次
    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
解釋:存儲(chǔ)在靜態(tài)數(shù)據(jù)區(qū)的變量會(huì)在程序剛開(kāi)始運(yùn)行時(shí)就完成初始化,也是唯一的一次初始化,此后該初始化不再執(zhí)行,相當(dāng)于一次執(zhí)行后就作廢,靜態(tài)局部變量保存了前次被調(diào)用后留下的值。

  1. 返回值加const修飾的必要性
    你覺(jué)得下面兩種寫法有區(qū)別嗎?
int GetInt(void) 
const int GetInt(void)

如果是下面的呢?其中A 為用戶自定義的數(shù)據(jù)類型。

A GetA(void)
const A GetA(void)

答案:沒(méi)有任何區(qū)別。
解釋:如果函數(shù)返回值采用“值傳遞方式”,由于函數(shù)會(huì)把返回值復(fù)制到外部臨時(shí)的存儲(chǔ)單元中,加const 修飾沒(méi)有任何價(jià)值。所以,對(duì)于值傳遞來(lái)說(shuō),加const沒(méi)有太多意義。
所以:

  • 不要把函數(shù)int GetInt(void) 寫成const int GetInt(void)。
  • 不要把函數(shù)A GetA(void) 寫成const A GetA(void)。

在編程中要盡可能多的使用const(比如函數(shù)參數(shù)采用const&修飾),這樣可以獲得編譯器的幫助,以便寫出健壯性的代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容