C/C++求職寶典重點筆記

這是我以前準備找工做時看《C/C++求職寶典》一書作的筆記,都是一些筆試面試中常考的重點難點問題,但比較基礎,適合初學者看。html

 

1. char c = '\72'; 中的\72表明一個字符,72是八進制數,表明ASCII碼字符「:」。面試

2. 10*a++ 中a先進行乘法運算再自增(筆試中常常喜歡出這類運算符優先級容易混淆的輸出問題)。算法

3. const和static的做用
太常見的問題了,下面給出一個較詳細的參考答案:
static關鍵字:
1)函數體內static變量的做用範圍爲函數體。不一樣於auto變量。該變量的內存只被分配一次。所以其值在下次調用時仍維持上次的值。
2)在模塊內的static全局變量能夠被模塊內的全部函數訪問。但不能被模塊外的其餘函數訪問。
3)在模塊內的static函數只可被這一模塊內的其它函數調用。這個函數的使用範圍被限制在聲明它的模塊內。
4)在類中的static成員變量屬於整個類全部,對類的全部對象只有一份複製。
5)在類中的static成員函數屬於整個類全部,這個函數不接受this指針,於是只能訪問類的static成員變量。
const關鍵字:
1)欲阻止一個變量被改變,可使用const關鍵字。在定義該const變量時,一般須要對它進行初始化。由於之後就沒有機會再改變它了。
2)對指針來講,能夠指定指針的自己爲const,也能夠指定指針所指向的數爲const。或兩者同時爲const。
3)在一個函數的聲明中,const能夠修飾形參,代表它是一個輸入參數。在函數內不能改變其值。
4)對於類的成員函數,若指定其爲const類型。則代表其是一個常量函數。不能修改類的成員變量。
5)對於類的成員函數,有時候必須指定其返回值爲const類型。以使得其返回值不爲「左值」。
 
4. 注意sizeof不是函數而是運算符,因此在計算變量所佔用空間大小時,括號是能夠省略的,但在計算類型大小時括號則不能省略,好比int i = 0; 則sizeof int是錯誤的。
 
5. 有1,2,…,n的無序數組,求排序算法,而且要求時間複雜度爲O(n),空間複雜度O(1),使用交換,並且一次只能交換兩個數。
複製代碼
#include <stdio.h>
int main() {
    int a[] = {10, 6, 9, 5, 2, 8, 4, 7, 1, 3};
    int i, tmp;
    int len = sizeof(a) / sizeof(a[0]);
    for(i = 0; i < len;) {
        tmp = a[a[i] - 1];
        a[a[i] - 1] = a[i];
        a[i] = tmp;
        if(a[i] == i + 1) i++;
    }
    for(i = 0; i < len; ++i)
        printf("%d ", a[i]);
    printf("\n");
    return 0;
}
複製代碼

 

6. 易誤解:若是int a[5], 那麼a與&a是等價的,由於二者地址相同。
解答:必定要注意a與&a是不同的,雖然二者地址相同,但意義不同,&a是整個數組對象的首地址,而a是數組首地址,也就是a[0]的地址,a的類型是int[5],a[0]的類型是int,所以&a+1至關於a的地址值加上sizeof(int) * 5,也就是a[5],下一個對象的地址,已經越界了,而a+1至關於a的地址加上sizeof(int),即a[1]的地址。
 
7. 如何將一個小數分解成整數部分和小數部分?
要記得利用頭文件中的庫函數modf,下面是函數原型(記住一些實用的庫函數,避免本身重寫):
double modf(double num, double *i); // 將num分解爲整數部分*i和小數部分(返回值決定)

 

8. 可做爲函數重載判斷依據的有:參數個數、參數類型、const修飾符;
   不能夠做爲重載判斷依據的有:返回類型。
 
9. 程序輸出題:
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = &(a + 1)[3];
printf("%d\n", *p);

輸出:5編程

說明:由於a+1指向a的第二個元素,[3]表示再向後移動3個元素。
 
10. 程序輸出題:
複製代碼
 char str1[] = "abc";
 char str2[] = "abc";
 const char str3[] = "abc";
 const char str4[] = "abc";
 const char *str5 = "abc";
 const char *str6 = "abc";
 char *str7 = "abc";
 char *str8 = "abc";
 cout << (str1 == str2) << endl;
 cout << (str3 == str4) << endl;
 cout << (str5 == str6) << endl;
 cout << (str7 == str8) << endl;
複製代碼

輸出:0 0 1 1數組

說明:輸出str1~str8的地址爲:
0x23aa80
0x23aa70
0x23aa60
0x23aa50
0x23aa48
0x23aa40
0x23aa38
0x23aa30
輸出str1~str8內容「abc」的存儲地址爲:
0x23aa80
0x23aa70
0x23aa60
0x23aa50
0x100403030
0x100403030
0x100403030
0x100403030
能夠發現str1~str4中的內容是存在棧上,地址各不相同,而str5~str8的內容都是存儲在常量區,因此地址都相同。
 
注意:
char *str = "abc";
printf("%p\n", str1);
cout << &str1 << endl;

上面打印的是字符串 「abc」的地址,下面打印的是 str1 變量的地址。函數

 
11. C的結構體和C++結構體的區別
(1)C的結構體內不容許有函數存在,C++容許有內部成員函數,且容許該函數是虛函數。因此C的結構體是沒有構造函數、析構函數、和this指針的。
(2)C的結構體對內部成員變量的訪問權限只能是public,而C++容許public,protected,private三種。
(3)C語言的結構體是不能夠繼承的,C++的結構體是能夠從其餘的結構體或者類繼承過來的。
 
以上都是表面的區別,實際區別就是面向過程和麪向對象編程思路的區別:
C的結構體只是把數據變量給包裹起來了,並不涉及算法。
而C++是把數據變量及對這些數據變量的相關算法給封裝起來,而且給對這些數據和類不一樣的訪問權限。
C語言中是沒有類的概念的,可是C語言能夠經過結構體內建立函數指針實現面向對象思想。
 
12. 如何在類中定義常量成員併爲其初始化?
解答:只能在初始化列表裏對const成員初始化,像下面這樣:
class CBook {
public:
    const double m_price;
    CBook() :m_price(8.8) { }
};

下面的作法是錯誤的:this

複製代碼
class CBook {
public:
    const double m_price;
    CBook() {
        m_price = 8.8;
    }
};
複製代碼

而下面的作法雖未報錯,但有個warning,也不推薦:spa

class CBook {
public:
    const double m_price = 8.8; // 注意這裏若沒有const則編譯出錯
    CBook() { }
};

 

13. 在定義類的成員函數時使用mutable關鍵字的做用是什麼?
解答:當須要在const方法中修改對象的數據成員時,能夠在數據成員前使用mutable關鍵字,防止出現編譯出錯。例子以下:
複製代碼
class CBook {
public:
    mutable double m_price; // 若是不加就會出錯
    CBook(double price) :m_price(price) { }
    double getPrice() const; // 定義const方法
};
double CBook::getPrice() const {
    m_price = 9.8;
    return m_price;
}
複製代碼

 

14. 構造函數、拷貝構造函數、析構函數的調用點和順序問題,以下面這個例子輸出是什麼?
複製代碼
class CBook {
public:
    CBook() {
        cout << "constructor is called.\n";
    }
    ~CBook() {
        cout << "destructor is called.\n";
    }
};
 
void invoke(CBook book) { // 對象做爲函數參數,若是這裏加了個&就不是了,由於加了&後是引用方式傳遞,形參和實參指向同一塊地
                          // 址,就不須要建立臨時對象,也就不須要調用拷貝構造函數了
    cout << "invoke is called.\n";
}
 
int main() {
    CBook c;
    invoke(c);
}
複製代碼

解答:注意拷貝構造函數在對象做爲函數參數傳遞時被調用,注意是對象實例而不是對象引用。所以該題輸出以下:指針

constructor is called.
invoke is called.
destructor is called. // 在invoke函數調用結束時還要釋放拷貝構造函數建立的臨時對象,所以這裏還調用了個析構函數
destructor is called.

 

引伸:拷貝構造函數在哪些狀況下被調用?
(1)函數的參數爲類對象且參數採用值傳遞方式;
(2)將類對象作爲函數的返回值。
 
15. C++中的explicit關鍵字有何做用?
解答:禁止將構造函數做爲轉換函數,即禁止構造函數自動進行隱式類型轉換。
例如CBook中只有一個參數m_price,在構建對象時可使用CBook c = 9.8這樣的隱式轉換,使用explicit防止這種轉換髮生。
 
16. 在C++中,若是肯定了某一個構造函數的建立過程,在該構造函數中若是調用了其它重載的構造函數,它將不會執行其它構造函數的初始化列表部分代碼,而是執行函數體代碼,此時已經退化成普通函數了。例子說明以下:
複製代碼
class CBook {
public:
    double m_price;
    CBook() {
        CBook(8.8);
    }
    CBook(double price) : m_price(price) { }
};
int main() {
    CBook c;
    cout << c.m_price << endl; // 此時並不會輸出理想中的8.8
}
複製代碼

 

17. 靜態數據成員只能在全局區域進行初始化,而不能在類體中進行(構造函數中初始化也不行),且靜態數據成員不涉及對象,所以不受類訪問限定符的限制。
例子說明以下:
class CBook {
public:
    static double m_price;
};
double CBook::m_price = 8.8; // 只能在這初始化,不能在CBook的構造函數或直接初始化

 

18. C++中能夠重載的運算符:new/delete、new[]/delete[]、++等。
    不能夠重載的運算符:、.、::、?:、sizeof、typeid、.、**、不能改變運算符的優先級。
 
引伸:重載++和–時是怎麼區分前綴++和後綴++的?
例如當編譯器看到++a(先自增)時,它就調用operator++(a);
但當編譯器看到a++時,它就調用operator++(a, int)。即編譯器經過調用不一樣的函數區別這兩種形式。
 
19. C++的多態性分爲靜態多態和動態多態。
靜態多態性:編譯期間肯定具體執行哪一項操做,主要是經過函數重載和運算符重載來實現的;
動態多態性:運行時肯定具體執行哪一項操做,主要是經過虛函數來實現的。
 
20. 虛函數原理考點,例以下面程序的輸出是什麼?
複製代碼
class A {
public:
    virtual void funa();
    virtual void funb();
    void func();
    static void fund();
    static int si;
private:
    int i;
    char c;
};
複製代碼

問:sizeof(A) = ?code

解答:
關於類佔用的內存空間,有如下幾點須要注意:
(1)若是類中含有虛函數,則編譯器須要爲類構建虛函數表,類中須要存儲一個指針指向這個虛函數表的首地址,注意無論有幾個虛函數,都只創建一張表,全部的虛函數地址都存在這張表裏,類中只須要一個指針指向虛函數表首地址便可。
(2)類中的靜態成員是被類全部實例所共享的,它不計入sizeof計算的空間
(3)類中的普通函數或靜態普通函數都存儲在棧中,不計入sizeof計算的空間
(4)類成員採用字節對齊的方式分配空間
答案:12(32位系統)或16(64位系統)
 
21. 虛繼承的做用是什麼?
在多繼承中,子類可能同時擁有多個父類,若是這些父類還有相同的父類(祖先類),那麼在子類中就會有多份祖先類。例如,類B和類C都繼承與類A,若是類D派生於B和C,那麼類D中就會有兩份A。爲了防止在多繼承中子類存在重複的父類狀況,能夠在父類繼承時使用虛函數,即在類B和類C繼承類A時使用virtual關鍵字,例如:
class B : virtual public A
class C : virtual public A
注:由於多繼承會帶來不少複雜問題,所以要慎用。
 
 
參考連接:
相關文章
相關標籤/搜索