360面試準備

360面試準備

  1. C++新特性

1)lambda表達式
for_each (array, array + SIZE,
[] (int a){ cout << a << ” “; });
lambda 表達式可以方便地構造匿名函數,如果你的代碼裏面存在大量的小函數,而這些函數一般只被調用一次,那麼不妨將他們重構成 lambda 表達式
作用:使得代碼更加簡潔

2)自動類型推導和 decltype
auto x=0, 0是int類型,故x也是int類型
auto ci = vi.begin();

decltype用於從對象或表達式中俘獲類型,如:
const vector vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;

3)統一的初始化語法
class C
{
int a;
int b;
public:
C(int i, int j);
};
C c {0,0}; //C++11 only. 相當於 C c(0,0);
int* a = new int[3] { 1, 2, 0 }; /C++11 only
class X {
int a[4];
public:
X() : a{1,2,3,4} {} //C++11, 初始化數組成員
};

4)還有一大好事就是對於容器來說,終於可以擺脫 push_back() 調用了,C++11中可以直觀地初始化容器了:
// C++11 container initializer
vector vs={ “first”, “second”, “third”};
map singers =
{ {“Lady Gaga”, “+1 (212) 555-7890”},
{“Beyonce Knowles”, “+1 (212) 555-0987”}};

5)C++11 標準的兩個新特性:defaulted 和 deleted 函數。
對於 defaulted 函數,編譯器會爲其自動生成默認的函數定義體,從而獲得更高的代碼執行效率,
也可免除程序員手動定義該函數的工作量。對於 deleted 函數, 編譯器會對其禁用,
從而避免某些非法的函數調用或者類型轉換,從而提高代碼的安全性。

struct A
{
A()=default; //C++11
virtual ~A()=default; //C++11
};

struct NoCopy
{
NoCopy & operator =( const NoCopy & ) = delete;
NoCopy ( const NoCopy & ) = delete;
};
NoCopy a;
NoCopy b(a); //編譯錯誤,拷貝構造函數是 deleted 函數

6)nullptr類型
nullptr 是一個新的 C++ 關鍵字,它是空指針常量,它是用來替代高風險的 NULL 宏和 0 字面量的。nullptr 是強類型的

void f(int); //#1
void f(char *);//#2
//C++03
f(0); //調用的是哪個 f?
//C++11
f(nullptr) //毫無疑問,調用的是 #2

所有跟指針有關的地方都可以用 nullptr,包括函數指針和成員指針

const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
cout<

   例如:

                    std::unique_ptr<int> p1(new int(5));
                    std::unique_ptr<int> p2 = p1; // 編譯會出錯
                    std::unique_ptr<int> p3 = std::move(p1); // 轉移所有權, 現在那塊內存歸p3所有, p1成爲無效的指針.

C++11或boost的shared_ptr,基於引用計數的智能指針。可隨意賦值,直到內存的引用計數爲0的時候這個內存會被釋放。

C++11或boost的weak_ptr,弱引用。 引用計數有一個問題就是互相引用形成環,這樣兩個指針指向的內存都無法釋放。
需要手動打破循環引用或使用weak_ptr。顧名思義,weak_ptr是一個弱引用,只引用,不計數。
如果一塊內存被shared_ptr和weak_ptr同時引用,當所有shared_ptr析構了之後,不管還有沒有weak_ptr引用該內存,
內存也會被釋放。所以weak_ptr不保證它指向的內存一定是有效的,在使用之前需要檢查weak_ptr是否爲空指針。

3),智能指針的簡單實現。

template <class T> 
class myAutoPtr
{
private:
    T *myPtr;

public:
    explicit myAutoPtr( T *p = 0 )
    {
        myPtr = p;
    }

    myAutoPtr &operator=( myAutoPtr &a )
    {
        if( this == &a )
        {
            return *this;
        }

        delete myPtr;
        myPtr = a.relase();
        return *this;
    }

    ~myAutoPtr()
    {
        delete myPtr;
    }

    T& operator*()
    {
        return *myPtr;
    }

    T* operator->()
    {
        return myPtr;
    }

    T *get()
    {
        return myPtr;
    }

    T *relase()
    {
        T *tmp = myPtr;
        myPtr = NULL;
        return tmp;
    }

    void reset( T *p )
    {
        if( p != myPtr )
        {
            delete myPtr;
            myPtr = p;
        }

        return;
    }


};

5.非遞歸求樹的高度,鏈表逆序

typedef struct Node
{
    int data;
    struct Node *left;
    struct Node *right;

    struct Node()
    {
        data = 0;
        left = right = NULL;
    }

}Node, *pNode;

int treeDepth( pNode root )
{
    int depth = 0;

    if( NULL == root )
    {
        return depth;
    }

    queue<pNode> qu;
    qu.push(root);

    while( !qu.empty() )
    {
        int curSize = qu.size();
        depth++;

        for( int i = 0; i < curSize; i++ )
        {
            pNode cur = qu.front();
            qu.pop();

            if( cur->left != NULL )
            {
                qu.push( cur->left );
            }

            if( cur->right != NULL )
            {
                qu.push( cur->right );
            }

        }
    }

    return depth;
}


// 逆序一個鏈表
Node *reverseLink( Node *head )
{
    if( NULL == head )
    {
        return head;
    }

    Node *cur = NULL;

    while( head != NULL )
    {
        Node *pNext = head->next;
        head->next = cur;
        cur = head;
        head = pNext;
    }

    return cur;
}

  1. 紅黑樹相關
    1)紅黑樹的數據結構定義
enum Color
{
    RED = 0,
    BLACK = 1
};

struct redBlackNode
{
    int key;
    int data;
    struct redBlackNode *left;
    struct redBlackNode *right;
    Color color;
};

2)紅黑樹性質
節點顏色爲紅色或黑色,根節點爲黑色,葉子節點(Nil,指樹尾端NIL指針或NULL結點)爲黑色,紅節點的子節點爲黑色,對於任一節點,其到葉子葉子節點的任一路徑含有的黑節點數相同;

3)時間複雜度:能保證在最壞情況下,時間複雜度爲O(lgn)

4)紅黑樹相比於BST和AVL樹有什麼優點?

紅黑樹是犧牲了嚴格的高度平衡的優越條件爲代價,它只要求部分地達到平衡要求,降低了對旋轉的要求,
從而提高了性能。紅黑樹能夠以O(log2 n)的時間複雜度進行搜索、插入、刪除操作。此外,由於它的設計
,任何不平衡都會在三次旋轉之內解決。當然,還有一些更好的,但實現起來更復雜的數據結構能夠做到
一步旋轉之內達到平衡,但紅黑樹能夠給我們一個比較“便宜”的解決方案。

相比於BST,因爲紅黑樹可以能確保樹的最長路徑不大於兩倍的最短路徑的長度,所以可以看出它的查找效果
是有最低保證的。在最壞的情況下也可以保證O(logN)的,這是要好於二叉查找樹的。因爲二叉查找樹最壞情
況可以讓查找達到O(N)。

紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高,所以在插入和刪除中所做的後期維護操作肯定
會比紅黑樹要耗時好多,但是他們的查找效率都是O(logN),所以紅黑樹應用還是高於AVL樹的. 實際上插入 AVL
樹和紅黑樹的速度取決於你所插入的數據.如果你的數據分佈較好,則比較宜於採用 AVL樹(例如隨機產生系列數),
但是如果你想處理比較雜亂的情況,則紅黑樹是比較快的

5)紅黑樹相對於哈希表,在選擇使用的時候有什麼依據?
權衡三個因素: 查找速度, 數據量, 內存使用,可擴展性。
重於查找速度,內存消耗小:用hash
可擴展性:用紅黑樹
如何數據基本是靜態的,用紅黑樹,如果數據完全是靜態的,做一個哈希表,性能可能會更好一些。

但若你對內存使用特別嚴格, 希望程序儘可能少消耗內存,那麼一定要小心,hash可能會讓你陷入尷尬,
特別是當你的hash對象特別多時,你就更無法控制了而且 hash的構造速度較慢。

6).如何擴展紅黑樹來獲得比某個結點小的元素有多少個?
每個節點添加一個size域,表示以該節點x爲根的子樹的節點數大小

1)找到樹中第i小的節點

findIthNode( x, i)
{
  r = size[ left[x] ] + 1;

  if( i == r )
    return x;
  else if( i < r )
        return findIthNode( left[x], i )
  else  return findIthNode( right[x], i );

}思路:size[left[x]]表示在對x爲根的子樹進行中序遍歷時排在x之前的個數,遞歸調用的深度不會超過O(lgn);

2).確定某個結點之前有多少個結點,也就是我們要解決的問題;

OS-Rank( T, x )
{
   r = x.left.size + 1;
   y = x;

   while( y != T.root )
   {
    if( y == y.p.right )
        r = r + y.p.left.size + 1;

        y = y.p;
   }

   return r;
}

思路:x的秩可以視爲在對樹的中序遍歷種,排在x之前的結點個數加上一。最壞情況下,OS-RANK運行時間與樹高成正比,所以爲O (lgn).

7.擴展數據結構有什麼步驟?
1).選擇基礎數據結構;

2).確定要在基礎數據結構種添加哪些信息;

3).驗證可用基礎數據結構上的基本修改操作來維護這些新添加的信息;

4).設計新的操作。

8.爲什麼一般hashtable的桶數會取一個素數

設有一個哈希函數
H( c ) = c % N;
當N取一個合數時,最簡單的例子是取2^n,比如說取2^3=8,這時候
H( 11100(二進制) ) = H( 28 ) = 4
H( 10100(二進制) ) = H( 20 )= 4

這時候c的二進制第4位(從右向左數)就”失效”了,也就是說,無論第c的4位取什麼值,都會導致H( c )的值一樣.這時候c的第四位就根本不參與H( c )的運算,這樣H( c )就無法完整地反映c的特性,增大了導致衝突的機率.

取其他合數時,都會不同程度的導致c的某些位”失效”,從而在一些常見應用中導致衝突.
但是取質數,基本可以保證c的每一位都參與H( c )的運算,從而在常見應用中減小衝突機率..

8.線程池
一個線程池包括以下四個基本組成部分:
1、線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷燬線程池,添加新任務;
2、工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;
3、任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行,它主要規定了任務的入口,
任務執行完後的收尾工作,任務的執行狀態等;
4、任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。

  1. hash處理衝突的方法
    1)再散列法,基本思想是:當關鍵字的哈希地址出現衝突時,以p=H(key)爲基礎,產生另外一個哈希地址p1,一直找到不衝突的哈希
    地址pi爲止,如:線性探測法,二次探測法

2)再哈希法,同時構造多個不同的哈希函數,第一個哈希函數出現衝突,用第二個函數函數;

3)鏈地址法

4)建立公共溢出區,將哈希表分爲基本表和溢出表,凡是和基本表發生衝突的元素,一律填入溢出表;

  1. 內存泄露的情況?如何檢測內存泄露?
    內存泄漏的原因:分配了未釋放;程序代碼有問題,如:
    Temp1 = new BYTE[100];
    Temp2 = new BYTE[100];
    Temp2 = Temp1;
    某些API函數操作不正確

如何檢測:
1)包括手動檢測和靜態工具分析,代碼靜態掃描和分析的工具比較多,比如 splint, PC-LINT, BEAM 等
2)動態運行檢測,實時檢測工具主要有 valgrind, Rational purify 等

  1. Nginx是一個輕量級,高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP服務器,
    因它的穩定性、豐富的功能集、示例配置文件和低系統資源的消耗而聞名。

特點:熱部署(在不停止服務的情況下可以升級ngnix的可執行文件,修改配置文件),可以高併發連接,佔用內存少,
處理響應請求很快,具有很高的可靠性。

  1. DOS的攻擊原理,以及與DDOS的區別
    DOS攻擊原理:
    1)發生大量SYN標誌的TCP請求,導致服務器端的緩衝區隊列滿了,導致無法服務其他正常請求;
    2)利用IP欺騙,迫使服務器將正常合法用戶的連接復位,影響合法用戶的連接。
    這種攻擊利用RST位來實現。假設現在有一個合法用戶(1.1.1.1)已經同服務器建立了正常的連接,
    攻擊者構造攻擊的TCP數據,僞裝自己的IP爲1.1.1.1,並向服務器發送一個帶有RST位的TCP數據段。
    服務器接收到這樣的數據後,認爲從1.1.1.1發送的連接有錯誤,就會清空緩衝區中建立好的連接。
    這時,如果合法用戶1.1.1.1再發送合法數據,服務器就已經沒有這樣的連接了,該用戶就必須從新開始建立連接。
    3)帶寬DOS攻擊, 消耗服務器的帶寬;
    4)塞滿服務器的硬盤,如果web服務器和郵件服務器在同一臺機器上,我就發送大量垃圾郵件,消耗其硬盤空間;

分佈式拒絕服務(DDoS:Distributed Denial of Service)攻擊指藉助於客戶/服務器技術,將多個計算機聯合起來作爲攻擊平臺,
對一個或多個目標發動DDoS攻擊,從而成倍地提高拒絕服務攻擊的威力。

區別:DDOS是DOS攻擊中的一種方法,DOS攻擊一般是一對一攻擊,而DDOS攻擊是多臺機器同時向一個或多個目標進行攻擊,
就是控制多臺電腦對同一目標進行DOS攻擊。

  1. DMA原理:
    原理
    一個設備接口試圖通過總線直接向另一個設備發送數據(一般是大批量的數據),
    它會先向CPU發送DMA請求信號。外設通過DMA的一種專門接口電路――DMA控制器(DMAC),
    向CPU提出接管總線控制權的總線請求,CPU收到該信號後,在當前的總線週期結束後,
    會按DMA信號的優先級和提出DMA請求的先後順序響應DMA信號。CPU對某個設備接口響應DMA請求時,
    會讓出總線控制權。於是在DMA控制器的管理下,外設和存儲器直接進行數據交換,而不需CPU干預。
    數據傳送完畢後,設備接口會向CPU發送DMA結束信號,交還總線控制權。

基本操作
實現DMA傳送的基本操作如下:
1、外設可通過DMA控制器向CPU發出DMA請求;
2、CPU響應DMA請求,系統轉變爲DMA工作方式,並把總線控制權交給DMA控制器;
3、由DMA控制器發送存儲器地址,並決定傳送數據塊的長度;
4、執行DMA傳送;
5、DMA操作結束,並把總線控制權交還CPU。

  1. 鍵盤上輸入字符到屏幕上顯示它,期間發生了什麼?
    鍵盤掃描,掃描碼,產生中斷,有一箇中斷號,查找中斷向量,鍵盤的中斷處理程序,將掃描碼轉換爲ASCII碼,然後就是顯示的過程,通過bios調用,
    字符發生器將該字符以及屬性轉換爲一個光點矩陣,該光點矩陣可以作爲顯示器的輸入信號。每個點都有3把電子槍發射(分別發rgb)的光疊加成的。

15.Unix/Windows中進程通信的幾種方式

現在linux使用的進程間通信方式:
(1)管道(pipe)和有名管道(FIFO)
(2)信號(signal)
(3)消息隊列
(4)共享內存
(5)信號量
(6)套接字(socket)

Windows中進程通信的幾種方式
1)剪貼板
2)文件映射
3)使用共享內存方式
4)windows套接字
5)管道
6)郵件槽

  1. 臨界區與信號量的區別
    保證在某一時刻只有一個線程能訪問數據的簡便辦法。在任意時刻只允許一個線程對共享資源進行訪問。
    信號允許多個線程同時使用共享資源 ,這與操作系統中的PV操作相同。它指出了同時訪問共享 資源的線程 最大數目。
    它允許多個線程在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大線程數目。

  2. BST樹轉換爲雙向鏈表

  3. 單例模式實現

class CSingleInstance
{
private:
    CSingleInstance(){}
    CSingleInstance( const CSingleInstance & ){}
    CSingleInstance& operator=( const CSingleInstance & ){}
    static CSingleInstance *pInstance;

public:
    static CSingleInstance *getInstance()
    {
        return pInstance;
    }
};

CSingleInstance * CSingleInstance::pInstance = new CSingleInstance();
  1. string類簡單實現
    assert(index>=0 && index<=strLength);

  2. free和delete如何知道應該釋放多少內存
    分配內存的算法有關,依賴於實現,在分配每塊內存的時候,都有額外的空間來記錄分配內存的大小的
    用前面四個字節來保存大小
    分配時會簿記,刪除時先查詢是否存在
    一般的做法是在分配的內存前邊加一個長度值,假如你申請4字節的內存,那系統很可能是分配了8字節,然後4字節記錄長度,
    另4字節給你用。但這個是compiler specific的,也不排除有編譯器會使用查表法或其它更高明的辦法。

21.new和malloc的區別
運算符和函數的區別
返回值不同,一個需要強制轉換,一個不需要
是否調用構造函數
分配失敗一個拋出異常,一個不拋出而返回NULL
new自動計算需要分配的空間,而malloc需要手工計算字節數
new是類型安全的,而malloc不是,比如:
int* p = new float[2]; // 編譯時指出錯誤
int* p = malloc(2*sizeof(float)); // 編譯時無法指出錯誤

既然new/delete的功能完全覆蓋了malloc/free,爲什麼C++還保留malloc/free呢?因爲C++程序經常要調用C函數,
而C程序只能用malloc/free管理動態內存。

  1. 堆和棧的區別
    堆區,棧區,全局區,代碼區,常量區

23.寫個類A,聲明類A指針指向NULL,調用類A的方法會有什麼後果,編譯通過嗎?
可以編譯通過,但是運行時,如果調用的是普通函數,如果普通函數中訪問了非靜態成員變量,則運行時會出現
錯誤,沒有訪問成員變量或訪問的是靜態成員變量的話,正常調用而沒有錯誤,但是如果調用的是是virtual函數,
則會出現運行時錯誤。
原因:普通函數放在全局內存區中,可以訪問,虛函數是通過虛函數表來調用的,首先要查找到虛函數表,而
指向虛函數表的指針存放在對象中,又此時沒有對象生成,故出現錯誤;

  1. sizeof空類爲多少?爲什麼這麼設計?
    爲1,因爲空類也可以實例化,我們可以對其取地址,故要分配一個字節,如果sizeof(A)等於0則無法分
    配內存來在運行時區分多個同類型的對象

25.vim的替換語句,abc換成def
語法爲 :[addr]s/源字符串/目的字符串/[option]
全局替換命令爲::%s/源字符串/目的字符串/g

  1. 疑問:
    已解決:2^31表示2與31異或,而不是2的31次方,2^31-3按照優先級應該爲: 2^(31-3)
#include <iostream>
using namespace std;


void func( int m )
{
    cout << m << endl;
    return;
}

int main()
{
    int ret = 0;
    int n = 2^31;
    cout << sizeof(n) << endl; // 4

    long long n1 = 2^31; 
    cout << sizeof(n1) << endl; // 8

    cout << n1 << endl; // 29
    cout << n << endl;  // 29
    func( 2^31 - 3 );  // 30

    return ret;
}

==================================================================================
360 企業安全集團,服務端開發工程師-C++——珠海
本來視頻面試,後來他說他那邊有問題,就電話面試
1. 自我介紹
2. 問了下hadoop, 研究生有學過沒,storm和spark的區別
3. 講下memcached, memcached集羣中有一個節點失效了,怎麼處理,哈希一致性算法實現;
4. 看了libevent,講下同步,異步,阻塞,非阻塞的區別
5. 有了解服務端開發的相關技術嗎,講下;
6. 有github賬號嗎,有看源代碼嗎
7. 問了我喜歡做服務端的哪方面;
8. 有用過哪些數據庫沒,我說研究生期間沒有
9. 最後他說沒有什麼問題 ,就結束了,全程14分鐘左右。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章