題解5道c++面試題第一期(含解題思路、答案解析和實現代碼)

本篇文章送上5道c/c++面試題目,並附上答案、解題思路以及擴展知識。ios

1. 求下面函數的返回值

#include <stdio.h>

int func(int x)
{
    int iCnt = 0;
    while(x)
    {
        iCnt++;
        x = x&(x-1);
    }
    return iCnt;
}

int main()
{
    printf("cnt = %d\n", func(9999));
    return 0;
}

這題問的是函數的返回值,而經過代碼咱們能看到返回值的多少取決於x何時變爲0,而x的值又取決於x&(x-1)這個表達式,在c++中有一個規則,凡是看到&或者|這樣的符號,那就把它左右兩邊的值轉換爲二進制去計算,假設x是7,轉換爲二進制是00000111,x-1那就是00000110,那x&(x-1)就變成00000110了,再減一個1,變成00000101,那x&(x-1)就是00000100,因此實際上這個表達式每執行一次,二進制就少一個1,這樣的話,這篇題目就轉換成了,輸入的數字轉換爲二進制有多少個1,那麼返回值就是多少。c++

9999轉換爲二進制是10011100001111,因此本道題目答案:cnt = 8面試

2. 下面的代碼輸出是什麼?

給一段代碼,以下:函數

#include <stdio.h>

void testputs()
{
    unsigned int a = 6;//無符號整型
    int b = (-20);//有符號整型
    (a+b) > 6 ? puts(">6"):puts("<6");
}

int main()
{
    testputs();
    return 0;
}

初一看,6+(-20)應該是-14,那就應該輸出<6,可是這麼簡單的話,就不會有這麼一道題了,咱們編譯後實際上輸出了>6的結果,這是爲何呢,由於在c語言中,無符號和有符號進行運算或者比較的時候,都會直接把有符號的轉換爲無符號,而後再進行運算或者比較。spa

如今讓咱們增長一行代碼,看看輸出結果,以下:code

#include <stdio.h>

void testputs()
{
    unsigned int a = 6;
    int b = (-20);
    (a+b) > 6 ? puts(">6"):puts("<6");
    printf("%u\n", b);//%u輸出無符號整型
}

int main()
{
    testputs();
    return 0;
}

編譯後輸出以下結果:對象

>6
4294967276

也就是說-20轉換爲無符號整型之後變成了4294967276,這個數字是怎麼來的呢,首先這裏涉及到int和unsigned int的取值範圍,以下:進程

  • int類型取值範圍:-2^31~2^31-1;
  • unsigned int類型取值範圍:0~2^32-1;

那有符號轉換爲無符號是什麼樣的一個規則呢,有符號的0轉換爲無符號也是0,而後有符號的-1轉換爲無符號其實就是unsigned int的最大值2^32-1,也就是4294967295,那-20的話,再減19那就是4294967276,這樣就獲得了咱們先前輸出的結果。內存

固然上面這是字面上的轉換規則,還有一種辦法,咱們能夠根據內存的存儲二進制去進行計算,本質上這個轉換隻是轉換了類型,但並不會去動內存中存儲的內容,那負數是怎麼存儲的呢,分三步:字符串

  • 首先求出相應正數的二進制;
  • 而後按位取反;
  • 加1;

那麼20的二進制是00000000000000000000000000010100,而後按位取反11111111111111111111111111101011,加1之後變成11111111111111111111111111101100,轉換爲無符號就是:4294967276。

3. 下面代碼一共產生多少個進程?

看下面這段代碼:

#include <unistd.h>
#include <sys/types.h>

int main()
{
    fork();
    fork()&&fork()||fork();
    fork();
    //while(1);
    return 0;
}

這題的關鍵有兩點:

  • 第一個是要清楚fork函數的做用,fork函數是克隆出一個子進程,而且父進程返回子進程的進程ID,而子進程則返回0,而且在沒有判斷fork返回值的時候,父子進程共享全部的代碼;
  • 第二是要知道符號&&||的用法,對於&&,若是它左邊的表達式值爲真,則執行右邊的表達式,不然再也不執行後面的表達式,而對於||,若是它左邊的表達式爲真,則右邊的表達式再也不執行,不然繼續執行右邊的表達式。

下面咱們用一張圖來描述一下進程產生的過程:

下面咱們用文字對圖進行解說,以下:

  • 1號進程是main函數產生的;
  • 調用第一個fork函數之後,產生了2號進程;
  • 此時已經存在1號進程和2號進程,而後他們都調用第二個fork函數,那就產生了3號進程和4號進程,此時對於fork函數返回值,1號進程返回了3號進程的id,2號進程返回了4號進程的id,而3號進程和4號進程都返回0;
  • 根據上面說的,對於&&,只有左邊值不爲0,纔會繼續調用,因此只有1號進程和2號進程調用了第三個fork進程,分別產生了5號進程和6號進程,此時對於fork函數返回值,1號進程返回5號進程id,2號進程返回6號進程id,5號進程和6號進程都返回0;
  • 接下來是符號||,它左邊爲假,纔會執行右邊的表達式,而||的左邊是fork()&&fork(),因此只要第二個fork函數和第三個fork函數的調用有任意一個返回值爲0,它都要執行第四個fork函數,而根據上面的第二點和第三點,三、四、五、6這四個進程都要執行第四個fork函數,繼而產生了七、八、九、10這四個進程;
  • 最後的第五個fork函數調用沒有條件,全部現有的10個進程都要調用一次fork函數,最後就變成了20個進程。

因此答案是:20,咱們能夠把代碼裏面的while循環註釋放開,而後查看進程數量,就是20個進程。

4. 下面的代碼輸出什麼?

代碼以下:

#include <stdio.h>

int main()
{
    char *szName = "shengzhenjiayou";
    printf("%s %5.3s %3.5s %3.4s\n", szName, szName, szName, "aa");
    return 0;
}

先看輸出結果:shengzhenjiayou she sheng aa

這就很疑惑了,不少時候咱們只有在輸出浮點數的時候格式裏面纔會帶小數點,這裏輸出字符串帶小數點是什麼意思呢?

其實這裏%5.3s這樣的格式,小數點前面的表示至少要輸出的總寬度(其實就是對齊寬度),小數點後面的表示從左邊開始字符串輸出的最大寬度,因此%5.3s輸出了' she'這樣的數據,它總共輸出5列,但只取字符串前面3列,不足的部分補空格,之因此空格在左邊,那是由於默認是右對齊的,那若是是%-5.3s這樣的,就會變成左對齊。

得出結論以下:對於%5.3s這樣的格式而言,小數點前面的表示最少要輸出這個寬度,小數點後面的表示只能從字符串中截取這個寬度的數據,不夠也不會進行補充。

5. 一個空類有多大?

首先看一下下面的代碼:

#include <iostream>

class A
{
};

int main()
{
    printf("sizeof(A)=%d\n", sizeof(A));
    return 0;
}

輸出結果以下:sizeof(A)=1

這題通常不瞭解的人就會很疑惑,咱們通常計算一個類佔用多大空間,其實就是計算它的成員變量所佔用的空間,而類A沒有任何成員變量,那爲何長度會爲1呢。

這是由於c++標準規定,類實例化對象佔用內存的大小不能爲0,爲何這麼規定呢。

咱們來看,不管是標準c++類型仍是咱們自定義的類型(這裏剔除包含純虛函數的類),它都是能夠實例化產生一個變量的,而變量都是要存儲在內存中的,若是變量沒有大小,是沒有存儲的,也沒有辦法得到一個地址,那若是類型A實例化了不少對象,沒有地址的話,咱們就沒有辦法區分各個對象了,因此編譯器纔會給空類一個字節的空間,這樣咱們每個對象都會擁有一個獨一無二的地址。

這裏延伸一下,空類大小是1,那空結構體呢,基於以上一樣的緣由,空結構體實際上也是1。

本篇是c/c++題解第一期,後續會不按期發佈更多的題解,若是文章對你有用,麻煩分享和再看哦!

相關文章
相關標籤/搜索