c++面試題目

C++和C中的struct和class的區別

  1. C++的struct可以當作class來用,他和C++中class的唯一的區別是,class中的成員默認是private,而struct的成員默認爲public。

  2. C中的struct只能是一些變量的集合體,可以封裝數據卻不可以隱藏數據,而且成員不可以是函數。

  3. 關於使用大括號初始化, class和struct如果定義了構造函數的話,都不能用大括號進行初始化。 如果沒有定義構造函數,
    struct可以用大括號初始化。如果沒有定義構造函數,且所有成員變量全是public的話,可以用大括號初始化。

  4. 關於默認訪問權限,class中默認的成員訪問權限是private的,而struct中則是public的。

  5. 關於繼承方式,class繼承默認是private繼承,而struct繼承默認是public繼承

    在這裏插入圖片描述

虛函數

//實現多態的三個條件

//1 要有繼承

//2 要有虛函數重寫

//3 用父類指針(父類引用)指向子類對象

//c++規定,當一個成員函數被聲明爲虛函數後,其派生類中的同名函數都自動成爲虛函數。因此,在子類重新聲明該虛函數時,可以加,也可以不加,但習慣上每一層聲明函數時都加virtual,使程序更加清晰。
成員函數被重載的特徵是:

(1)具有相同的作用域(即同一個類定義中);

(2)函數名字相同

(3)參數類型,順序 或 數目不同(包括const參數和非const函數)

(4)virtual關鍵字可有可無。

覆蓋是指派生類重新實現(或者改寫即重寫)了基類的成員函數,其特徵是:

(1)不同的作用域(分別位於派生類和基類中);(即虛函數的重寫)

(2)函數名稱相同

(3)參數列表完全相同;

(4)基類函數必須是虛函數。

(即我們可以得到,覆蓋只是針對於虛函數)

隱藏是指派生類的成員函數遮蔽了與其同名的基類成員函數,具體規則如下:

(1) 派生類的函數與基類的函數同名,但是參數列表有所差異。此時,不論有無virtual關鍵字,基類的函數在派生類中將被隱藏。(注意別與重載混合)

(2)派生類的函數與基類的函數同名,參數列表也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數在派生類中將被隱藏。(注意別與覆蓋混合)

  • 1.虛函數(impure virtual)

C++的虛函數主要作用是“運行時多態”,即動態聯編,父類中提供虛函數的實現,爲子類提供默認的函數實現。

子類可以重寫父類的虛函數實現子類的特殊化。

  • 2.純虛函數(pure virtual)

C++中包含純虛函數的類,被稱爲是“抽象類”。抽象類不能使用new出對象,只有實現了這個純虛函數的子類才能new出對象。

C++中的純虛函數更像是“只提供申明,沒有實現”,是對子類的約束,是“接口繼承”。

C++中的純虛函數也是一種“運行時多態”,動態聯編。

class A
{
public:
    virtual void out1(string s)=0;//純虛函數
    virtual void out2(string s)//虛函數
    {
        cout<<"A(out2):"<<s<<endl;
    }
};
  • 3.普通函數(no-virtual)

普通函數是靜態編譯的,沒有運行時多態,只會根據指針或引用的“字面值”類對象,調用自己的普通函數。

普通函數是父類爲子類提供的“強制實現”。

因此,在繼承關係中,子類不應該重寫父類的普通函數,因爲函數的調用至於類對象的字面值有關。

虛函數的機制

只有數據成員的對象
類實現如下:

class Base1
{
public:
    int base1_1;
    int base1_2;
};

可知對象佈局:
在這裏插入圖片描述
可以看到, 成員變量是按照定義的順序來保存的, 最先聲明的在最上邊, 然後依次保存!類對象的大小就是所有成員變量大小之和。
虛函數在類中,需要一個虛函數表,無論類中友多少個虛函數,指針的大小都是4,因爲多一個虛函數只是在虛函數表中增加一個項罷了。
同一個類的不同實例共用同一份虛函數表, 她們都通過一個所謂的虛函數表指針__vfptr(定義爲void**類型)指向該虛函數表.

  • 1、她是編譯器在編譯時期爲我們創建好的, 只存在一份
  • 2、定義類對象時, 編譯器自動將類對象的__vfptr指向這個虛函數表
  • 3、類的佈局:虛函數表指針+成員變量定義.
  • 4、無論是通過Derive1的指針還是Base1的指針來調用此方法, 調用的都將是被繼承類重寫後的那個方法(函數), 多態發生了!!!
  • 5、子類中的虛函數被放到基類的虛函數表的後面
  • 6、基類都有虛函數的多繼承中,Derive1的虛函數表保存到第1個擁有虛函數表的那個基類的後面.
  • 7 、多集成中,哪個基類有虛函數,那個基類就放在前面

最終的實現機制如下:
如果不是虛函數, 直接調用指針對應的基本類的那個函數
如果是虛函數, 則查找虛函數表, 並進行後續的調用. 虛函數表在定義一個時, 編譯器就爲我們創建好了的. 所有的, 同一個類, 共用同一份虛函數表.

友元函數

  • 訪問1
    這三個訪問說明符分別是public、private和protected。其中,public表示該成員可以被所有的人訪問;private則與public剛好相反,它表示該成員只能被類內的成員函數訪問;而protected表示該成員只能被類內函數和該類的派生類對象訪問。

  • 友元函數
    在“訪問1”中提到,類的private和protected成員不能被外部函數訪問。如果外部函數確實需要訪問這些成員,則可以將外部函數聲明爲該類的友元。

  • 友元的作用:提高了程序的運行效率(使得普通函數可以直接訪問類的保護數據,避免了類成員函數的頻繁調用,即減少了類型檢查和安全性檢查等都需要時間開銷),但它破壞了類的封裝性和隱藏性,使得非成員函數可以訪問類的私有成員。

在C++中友元可以使友元函數也可以是友元函數

  • 友元函數:
    友元函數是可以直接訪問類的私有成員的非成員函數。是定義在類外的普通函數,不屬於任何類,但需要在類的定義中聲明,聲明時在函數前加friend關鍵字即可: friend 類型 函數名(形式參數)

  • 友元類:
    友元的所有成員函數都是另一個類的友元函數,都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員),當希望一個類可以存取另一個類的私有成員時,可以將該類聲明爲另一類的友元類: friend class 類名;

  • 使用友元類注意:

    1、友元關係不能被繼承。

    2、友元關係是單向的,不具有交換性。

    3、友元關係不具有傳遞性。
    4、成員函數有this指針,友元函數沒有this指針

    5、友元函數是不能被繼承的

    new和malloc區別

    new和malloc的區別是C/C++一道經典的面試題,我也遇到過幾次,回答的都不是很好,今天特意整理了一下。

  1.   屬性
    

new/delete是C++關鍵字,需要編譯器支持。malloc/free是庫函數,需要頭文件支持。

  1.   參數
    

使用new操作符申請內存分配時無須指定內存塊的大小,編譯器會根據類型信息自行計算。而malloc則需要顯式地指出所需內存的尺寸。

  1.   返回類型
    

new操作符內存分配成功時,返回的是對象類型的指針,類型嚴格與對象匹配,無須進行類型轉換,故new是符合類型安全性的操作符。而malloc內存分配成功則是返回void * ,需要通過強制類型轉換將void*指針轉換成我們需要的類型。

  1.   分配失敗
    

new內存分配失敗時,會拋出bac_alloc異常。malloc分配內存失敗時返回NULL。

  1. 自定義類型
 new會先調用operator new函數,申請足夠的內存(通常底層使用malloc實現)。然後調用類型的構造函數,初始化成員變量,最後返回自定義類型指針。delete先調用析構函數,然後調用operator delete函數釋放內存(通常底層使用free實現)。

malloc/free是庫函數,只能動態的申請和釋放內存,無法強制要求其做自定義類型對象構造和析構工作。

  1.  重載
    

C++允許重載new/delete操作符,特別的,佈局new的就不需要爲對象分配內存,而是指定了一個地址作爲內存起始區域,new在這段內存上爲對象調用構造函數完成初始化工作,並返回此地址。而malloc不允許重載。

//operator new 和 operator delete的原型如下:
void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an object

void *operator new[](size_t);     //allocate an array
void *operator delete[](void *);    //free an array
前面兩個均是 C++ 標準庫函數,你可能會覺得這是函數嗎?請不要懷疑,這就是函數!C++ Primer 
書上說這不是重載 new 和 delete 表達式(如 operator= 就是重載 = 操作符),
因爲 new 和 delete 是不允許重載的。她們都是用來申請和釋放內存的,
並且 operator new 申請內存之後不對內存進行初始化,直接返回申請內存的指針。



new和delete作爲運算符的時候纔可以被重載。
6. 內存區域

new操作符從自由存儲區(free store)上爲對象動態分配內存空間,而malloc函數從堆上動態分配內存。自由存儲區是C++基於new操作符的一個抽象概念,凡是通過new操作符進行內存申請,該內存即爲自由存儲區。而堆是操作系統中的術語,是操作系統所維護的一塊特殊內存,用於程序的內存動態分配,C語言使用malloc從堆上分配內存,使用free釋放已分配的對應內存。自由存儲區不等於堆,如上所述,佈局new就可以不位於堆中。

申請超大空間

C++中,在函數(包括main函數)中定義超大數組,內存爲棧所分配的最大空間爲4M,因此在子函數或者main函數中定義超大數組的方式是萬萬行不通的。
有時候覺得數組使用起來不方便,可以採用定義vector的方式,但需要注意的是,vector通常只能分配出幾百MB的空間。
由於vector在所需要的是一片連續的內存空間。

vector <char> vec0;
vector <string> vec1;
vector <int> vec2;
vector <double> vec3;
 
cout<<vec0.max_size()<<endl;//4294967295,即4G個元素
cout<<vec1.max_size()<<endl;//134217727,即128M個元素
cout<<vec2.max_size()<<endl;//1073741823,即1G個元素
cout<<vec3.max_size()<<endl;//536870911,即512M個元素
//注意max_size()函數返回的是vector容器最大能存放的元素的個數,並不是字節數
 

RT-THREAD介紹

RT-Thread中支持靜態和動態兩種定義方式。

  • 用線程來舉例的話,rt_thread_init對應靜態定義方式,rt_thread_create對應動態定義方式。

  • 使用靜態定義方式時,必須先定義靜態的線程控制塊,並且定義好堆棧空間,然後調用rt_thread_init來完成線程的初始化工作。採用這種方式,線程控制塊和堆棧佔用的內存會放在RW段,這段空間在編譯時就已經確定,它不是可以動態分配的,所以不能被釋放。而只能使用rt_thread_detach函數將該線程控制塊從對象管理器中脫離。

  • 使用動態定義方式rt_thread_create時,RT-Thread會動態申請線程控制塊和堆棧空間。在編譯時,編譯器是不會感知到這段空間的,只有在程序運行時,RT-Thread纔會從系統堆中申請分配這段內存空間,當不需要使用該線程時,調用rt_thread_delete函數就會將這段申請的內存空間重新釋放到內存堆中。

  • 這兩種方式各有利弊,
    靜態定義方式會佔用RW空間,但是不需要動態分配內存,運行時效率高。
    動態方式不會佔用額外的RW空間,佔用空間小,但是運行時需要動態分配內存,效率沒有靜態方式高。
    總的來說,這兩種方式就是空間和時間效率的平衡,可以根據實際環境需求選擇採用具體的分配方式。
    1.components.c文件的140行看到#ifdef RT_USING_USER_MAIN宏定義判斷,這個宏是定義在rtconfig.h文件內的,而且處於開啓狀態。同時我們可以在146行看到#if defined (__CC_ARM)的宏定義判斷,__CC_ARM就是指keil的交叉編譯器名稱。
    2.除中斷處理函數、調度器上鎖部分的代碼和禁止中斷的代碼是不可搶佔之外,其他部分是可以搶佔的,包括線程調度器本身。
    3.線程總共支持256個優先級,數值越小優先級越高。
    4.線程控制塊由結構體struct rt_thread表示
    rt_thread_t表示線程的句柄。
    5.線程的五種狀態:
    RT_THREAD_INIT 初始狀態
    RT_THREAD_READY 就緒狀態
    RT_THREAD_RUNNIG 運行狀態
    RT_THREAD_SUSPEND 掛起狀態
    RT_THREAD_CLOSE 結束狀態
    6.當系統中無其他線程運行時,調度器將調度到空閒線程。空閒線程通常是一個死循環,永遠不會被掛起。

概述

RT-Thread實時操作系統是一個分層的操作系統,它包括了:

• 組件層components,這些是基於RT-Thread核心基礎上的外圍組件,把一些功能模塊劃分成獨立的一個個組件模塊,做到組件與組件之間的低耦合,組件內部的高內聚。

例如文件系統,命令行shell接口,lwIP輕型TCP/IP協議棧,GUI圖形用戶界面等。

• 硬實時內核kernel,這層是RT-Thread的核心,包括了內核系統中對象的實現,例如多線程及其調度,信號量,郵箱,消息隊列,內存管理,定時器等實現。

•分支接口porting,主要由libcpu以及不同硬件平臺的bsp構成,即RT-Thread支持的一個個芯片移植,外設驅動等
在這裏插入圖片描述
RT-Thread是一個集實時操作系統(RTOS)內核、中間件組件和開發者社區於一體的技術平臺,由熊譜翔先生帶領並集合開源社區力量開發而成,RT-Thread也是一個組件完整豐富、高度可伸縮、簡易開發、超低功耗、高安全性的物聯網操作系統。RT-Thread具備一個IoT OS平臺所需的所有關鍵組件,例如GUI、網絡協議棧、安全傳輸、低功耗組件等等。經過11年的累積發展,RT-Thread已經擁有一個國內最大的嵌入式開源社區,同時被廣泛應用於能源、車載、醫療、消費電子等多個行業,累積裝機量超過兩千萬臺,成爲國人自主開發、國內最成熟穩定和裝機量最大的開源RTOS。

RT-Thread擁有良好的軟件生態,支持市面上所有主流的編譯工具如GCC、Keil、IAR等,工具鏈完善、友好,支持各類標準接口,如POSIX、CMSIS、C++應用環境、Javascript執行環境等,方便開發者移植各類應用程序。商用支持所有主流MCU架構,如ARM Cortex-M/R/A, MIPS, X86, Xtensa, C-Sky, RISC-V,幾乎支持市場上所有主流的MCU和Wi-Fi芯片。

3.個人使用體會

1)支持的CPU衆多,支持當前應用中的主流架構的CPU,並且都移植好相關bsp包,幾乎是獲取源碼即可運行,免去新手複雜的環境構建步驟。
2)佔用資源小,官方介紹最小配置時,內核可以到3K ROM和1K RAM 的佔用。正常使用一片STM32F103C8T6(20k RAM,64 k flash)都能跑起來。
3)支持的任務(線程)數量無限制,256個優先級,支持時間片輪詢。這對處理多個任務或者對於時間片要求比較嚴格的任務非常有優勢。
4)完善的外設,如spi、i2c、uart等總線驅動,及TCP/IP協議棧、文件系統、數據庫(sqlite3)等資源的支持。使用時,常用的底層不需經過大量修改,只需根據框架增加特殊的驅動,然後將主要的精力花在應用開發上。
5)Linux風格,熟悉Linux的可以很快上手。提供finsh shell、mash shell,類似Linux的命令行模式,能夠解析C代碼,在命令終端執行代碼,對於調試、打log、內存查看等非常有用。爲了節省資源,代碼發行時關閉finsh shell即可。
6)本人已在使用RTT的項目有3款,以及內部使用的工具2款,有帶GUI的也有隻使用內核的,產品經過長時間的連續不斷電使用,目前爲止沒有發現因爲是RTT的bug而導致的問題。因此RTT經過十多年的沉澱還是非常穩定的。
7)寫該文章的目的之一,就是讓嵌入式同行者知道並使用RTT,而且RTT是經過實際領域應用考驗的,打破他們在此之前對RTT穩定性的懷疑態度。

vector 和list區別

  • 1.vector數據結構
    vector和數組類似,擁有一段連續的內存空間,並且起始地址不變。
    因此能高效的進行隨機存取,時間複雜度爲o(1);
    但因爲內存空間是連續的,所以在進行插入和刪除操作時,會造成內存塊的拷貝,時間複雜度爲o(n)。
    另外,當數組中內存空間不夠時,會重新申請一塊內存空間並進行內存拷貝。

  • 2.list數據結構
    list是由雙向鏈表實現的,因此內存空間是不連續的。
    只能通過指針訪問數據,所以list的隨機存取非常沒有效率,時間複雜度爲o(n);
    但由於鏈表的特點,能高效地進行插入和刪除。
    **- 3、區別**
    vector擁有一段連續的內存空間,能很好的支持隨機存取,
    因此vector::iterator支持“+”,“+=”,“<”等操作符。

list的內存空間可以是不連續,它不支持隨機訪問,
因此list::iterator則不支持“+”、“+=”、“<”等

vector::iterator和list::iterator都重載了“++”運算符。

總之,如果需要高效的隨機存取,而不在乎插入和刪除的效率,使用vector;
如果需要大量的插入和刪除,而不關心隨機存取,則應使用list。

常用的軟件設計模式

設計模式的六大原則:
總原則:開閉原則(Open Close Principle)開閉原則就是說對擴展開放,對修改關閉。

  • 1、單一職責原則
  • 2、里氏替換原則
  • 3、依賴倒轉原則
  • 4、接口隔離原則
  • 5、迪米特法則
  • 6、合成複用原則

1.1. 創建型
創建對象時,不再由我們直接實例化對象;而是根據特定場景,由程序來確定創建對象的方式,從而保證更大的性能、更好的架構優勢。創建型模式主要有:

  • 簡單工廠模式(並不是23種設計模式之一)
  • 工廠方法
  • 抽象工廠模式
  • 單例模式、
  • 生成器模式
  • 原型模式。

1.2. 結構型
用於幫助將多個對象組織成更大的結構。結構型模式主要有:

  • 適配器模式adapter、
  • 橋接模式bridge、
  • 組合器模式component、
  • 裝飾器模式decorator、
  • 門面模式、
  • 亨元模式flyweight
  • 代理模式proxy。

1.3. 行爲型
用於幫助系統間各對象的通信,以及如何控制複雜系統中流程。行爲型模式主要有:

  • 命令模式command、
  • 解釋器模式、
  • 迭代器模式、
  • 中介者模式、
  • 備忘錄模式、
  • 觀察者模式、
  • 狀態模式state、
  • 策略模式、
  • 模板模式
  • 訪問者模式。

單列模式的實現方式-代碼
它的核心結構中只包含一個被稱爲單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。即一個類只有一個對象實例。顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。從具體實現角度來說,就是以下三點:一是單例模式的類只提供私有的構造函數,二是類定義中含有一個該類的靜態私有對象,三是該類提供了一個靜態的公有的函數用於創建或獲取它本身的靜態私有對象

因爲靜態初始化在程序開始時,也就是進入主函數之前,由主線程以單線程方式完成了初始化,所以靜態初始化實例保證了線程安全性。在性能要求比較高時,就可以使用這種方式,從而避免頻繁的加鎖和解鎖造成的資源浪費。由於上述三種實現,都要考慮到實例的銷燬,關於實例的銷燬,

#include <iostream>
using namespace std;
 
class Singleton
{
public:
    static Singleton *GetInstance()
    {
        static Singleton m_Instance;
        return &m_Instance;
    }
 
    int GetTest()
    {
        return m_Test++;
    }
 
private:
    Singleton(){ m_Test = 10; };
    int m_Test;
};
 
int main(int argc , char *argv [])
{
    Singleton *singletonObj = Singleton ::GetInstance();
    cout<<singletonObj->GetTest()<<endl;
 
    singletonObj = Singleton ::GetInstance();
    cout<<singletonObj->GetTest()<<endl;
}

大端小段

  • 大端模式:
    大端模式就是指把數據的高字節保存在內存的低地址中,數據的低字節保存在內存的高地址中,這和我們一般的閱讀順序是一致的。

  • 小端模式:
    小端模式與大端模式相反,數據的高字節位置保存在內存的高地址處,數據的低字節保存在內存的低地址處。這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低。
    在這裏插入圖片描述

  • 判斷
    在32位平臺下,int佔4個字節,而char類型的指針是佔一個字節的,如果我們把int強傳爲char類型的指針,只會保存一個字節的數據,那麼我們只需要判斷char裏面的第一個字節和int裏面的第一個字節是否是一致即可判斷。
    如果一致則爲小端模式,反之爲大端模式。

  • 1、方法一

//下面代碼我們令 int a=1 如果是小端模式,int下1會存放在在低地址處,而強傳爲char類型的指針,1也在低地址處,所以可以判斷。
//移位也可也實現
#include <iostream>
using namespace std;
int main()
{
    int a = 1;
    char b=(char *)&a;
    if (b == 1)
        cout << "小端模式" << endl;
    else
        cout << "大端模式" << endl;
    return 0;
}

  • 2、聯合體
#include <iostream>
using namespace std;
union Test
{
    int a;
    char b;
};
int main()
{
    Test t;
    t.a = 1;
    if (t.b == 1)
        cout << "小端機器" << endl;
    else
        cout << "大端機器" << endl;
    return 0;
}

絕對地址賦值

int *ptr;

ptr = (int *)0x67a9;//ptr的地址是0x67a9

*ptr = 0xaa55;//地址x67a9上保存的值是0xaa55

海康面試1

  • 1、計算機網絡有幾層(傳輸層有哪些協議)
    7層
    應用層(Application)、表示層(Presentation)、會話層(Session)、傳輸層(Transport)、網絡層(Network)、數據鏈路層(Data Link)、物理層(Physical)。
    5層

在這裏插入圖片描述

  • 2、TCP/UDP區別
    TCP是面向連接的、可靠的流協議。
    UDP是不具有可靠的、無連接的數據報協議。

  • 3、 TCP三次握手協議過程
    TCP是個面向連接的協議,傳輸數據之前需要建立連接,結束後需要斷開連接。
    建立連接一般稱爲三次握手:
    一般服務器端處於監聽狀態LISTEN,而客戶端處於CLOSED狀態。
    ①客戶端先向服務器建立連接,向服務端發送SYN報文段請求建立連接,序列號爲x,然後客戶端進入SYN_SENT狀態
    ②接着服務端發出SYN+ACK報文段,序列號爲y,確認序列號爲x+1,SYN代表請求建立連接,ACK代表收到了之前的報文端。
    ③當客戶端收到了SYN+ACK報文段,接着發出ACK確認,序列號爲y+1。最終雙方都進入ESTABLISHED建立狀態。這樣就完成了三次握手。
    斷開連接—四次揮手
    TCP是個全雙工的通信協議,數據傳輸都是雙向的。所以關閉連接需要關閉從客戶端到服務端的連接,還要關閉服務端到客戶端的連接。
    ①客戶端向服務器發送FIN的報文段,進入FIN_WAIT1狀態。
    ②服務端收到FIN的報文段,發出ACK確認後進入CLOSE_WAIT狀態,客戶端收到ACK後進去FIN_WAIT狀態。
    ③服務端發出FIN的報文段進入LAST_ACK狀態。
    ④客戶端收到FIN報文段後,發出ACK確認報文段後,進入TIME_WAIT狀態。服務端收到ACK後就關閉連接。

  • 4、數組和鏈表區別
    數組:
    數組在內存上是一塊連續的內存空間,這也是爲什麼可以隨機訪問的原因,因爲在內存上是連續的,所以元素都是緊鄰的,只要一個一個訪問即可。
    數組容量大小一但確定了就不能改變,所以這也是我們常常在編寫程序時常範的錯誤,數組越界
    數組在增加和刪除上效率是比較低的,爲什麼呢?因爲每次增加或者刪除數組中的元素所在的內存地址都要重新進行移動。
    數組的查詢速度比鏈表的查詢速度要快

鏈表:
鏈表在內存上是不連續的,是分散的,所有鏈表中的元素不是緊鄰的,我們只有知道了上一個節點的信息,才能知道下一個節點的內容,當然了鏈表也有單鏈表和雙鏈表之分,單鏈表即節點中存放了下一個節點的地址,雙鏈表顧名思義,節點中不但存放了下一個節點的地址,也存放了上一個節點地址。
大小可以隨意改變,鏈表可以動態的去改變自己的容量大小,而不會擔心會數組越界。
鏈表在增加和刪除上效率還是蠻高的,因爲只要改變節點中保存的地址值即可,而不用進行內存地址的移動。

  • 5、重寫和重載的概念
    重載是編寫函數名相同,參數列表不同的函數。覆蓋也叫重寫,重寫的函數必須有一致的參數表和返回值。什麼情況下用重載,什麼情況下用覆蓋?派生類重寫基類的虛函數用覆蓋。實現同一功能有不同參數列表的函數用重載。他們是怎麼實現的?重載是在編譯階段就知道要調用的函數地址,屬於早綁定,是靜態的;覆蓋要在運行時綁定函數地址,屬於晚綁定,是動態的。

  • 6、C++多態的機制
    多態就是一個接口多種狀態,用虛函數實現,根據具體對象調用具體的函數。在運行時才知道要調用的到底是哪個函數。

  • 7、給一個集合,集合中有0-9共10個數,有多少個子集
    如集合{1,2,3}的所有子集爲 空集,{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}數一數,一共8個,由此推測爲2的三次方,即2的三次冪。
    證明:
    方法1:

一共集合有n個元素,它的子集的個數就是對這n個元素做組合,一共有n個位置可以組合,每個位置上該元素可以出現也可以不出現,所以最後總的個數爲2的n次方。

方法2:

具有n個元素的集合的子集其實就是空集,含有一個元素的集合,含有兩個元素的集合…含有n個元素集合,這集合的和就是,如圖1所示。
在這裏插入圖片描述

  • 8、 鏈表中在某個位置中插入一個節點,如何插入
    結點在插入過程中存在以下幾種情況:
    (1)如果原表是空表,只需使鏈表的頭指針head指向被插結點即可。
    (2)如果如果被插結點值最小,則應插入第一個結點之前,這種情況下使頭指針head指向被插結點,被插結點的指針域指向原來的第一結點即可。
    (3)如果在鏈表中某位置插入,使插入位置的前一結點的指針域指向被插結點,被插結點的指針域指向插入位置的後一結點即可。
    (4)如果被插結點值最大,則在表尾插入,使原表尾結點指針域指向被插結點,被插結點指針域指向NULL即可。
node * insert(node * head, int x)
{
     node * last, * current, * p;
     //要插入的結點
     p = (node *)malloc(sizeof(node));
     p->num = x;
     //空表插入
     if(head == NULL)
     {
         head = p;
         p->next = NULL;
         return head;
     }
     //找插入位置
     current = head;
     while(x > current->num && current->next != NULL)
     {
          last = current;
          current = current->next;
     }
     if(x <= current->num)
     {
         if(head == current)//在第一結點之前插入
         {
             p->next = head;
             head = p;
             return head;
         }
         else//中間位置插入
         {
             p->next = current;
             last->next = p;
             return head;
         }
     }
     else//鏈尾插入
     {
         current->next = p;
         p->next = NULL;
         return head;
     }
}
  • 9、線程和多線程的區別
    什麼是線程?
    線程是程序中的一個執行流,每個線程都有自己的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,
    即不同的線程可以執行同樣的函數。

什麼是多線程?
多線程是指程序中包含多個執行流,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,
也就是說允許單個程序創建多個並行執行的線程來完成各自的任務。

多線程的好處:
可以提高CPU的利用率。在多線程程序中,一個線程必須等待的時候,CPU可以運行其它的線程而不是等待,
這樣就大大提高了程序的效率。

多線程的不利方面:
線程也是程序,所以線程需要佔用內存,線程越多佔用內存也越多;
多線程需要協調和管理,所以需要CPU時間跟蹤線程;
線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題;
線程太多會導致控制太複雜,最終可能造成很多Bug
多線程與單線程的區別
生活舉例
你早上上班,正要打卡的時候,手機響了。。你如果先接了電話,等接完了,在打卡,就是單線程。
如果你一手接電話,一手打卡。就是多線程。
2件事的結果是一樣的。。你接了電話且打了卡。

  • 10 、代碼中常用的數據結構有哪些
    所有的容器歸根到底都是內存空間的排列方式和在空間上施加各種各種不同的限制所得的。空間排列方式只有線性和鏈式兩種方式,鏈式是通過記錄每一個數據的地址來實現查找下一位數據的。而每一個容器所具有的特性就決定了它所適用的情況,總的來看容器常用的無非是增刪改查操作,下面將從適用場景、常用操作來進行總結。
    array數組
    內存空間爲連續的一段地址,適用於提前已知所要存儲的數據類型和數量、進行大量的查、改操作,不適用於含有大量交換、刪除、增加數據的操作,該容器無法動態改變大小,所以說提前已知存儲數據類型和數量。圖片介紹了數組的初始化、賦值、遍歷、獲取大小、獲取特定位置數據的方法。
    array <int 10> a;
    a[i]=1;
    queue隊列
    該容器內存結構最好爲鏈式結構,最知名的特點是先進先出,能動態調整大小,適用於包含大量增、刪操作的情況,但不適用於含有大量查找操作的數據。圖片介紹了隊列初始化、賦值、彈出操作。
    push(i)
    pop();
    stack 棧
    棧在內存上可爲連續或者鏈式,於隊列相反的是它爲先進後出,適用於壓棧出棧操作,如可用於圖的遍歷、遞歸函數的改寫等,圖片介紹了棧的創始化、壓棧、出棧等操作。
    stack a;
    a.push(i);
    a.pop();
    list 鏈表
    鏈表在內存結構上爲鏈式結構,也就決定它可以動態增加,適用於包含大量增加、刪除的應用,但不適用於包含大量查詢的操作,圖片介紹了鏈表的創建、添加數據、刪除數據、獲取數據等操作
    map
    map爲關聯式容器,提供一對一服務,每個關鍵字在容器中只能出現一次,適用於一對一服務。
    set 集合
    set集合最大的特點是裏面的元素按序排列不重複,圖片演示集合初始化、插入、刪除、查找等操作。
    vector向量
    vector向量和array不同,它可以根據數據的大小而進行自動調整,圖片僅展示初始化、插入、刪除等操作。

  • 11、報文中前幾個字節包含什麼

TCP報文一般有20個字節。
1.16位的源端口號加上16位的目的端口號,這兩個值加上IP首部中的源端IP地址和目的端IP地址,用於多路複用或多路分解來自或送至上層應用的數據。
2. 32位的序列號,其用來標識從TCP發送方向TCP接收端發送的數據字節流,它表示該報文段首字節的字節流編號。
3. 32位的確認序號標識發送確認一段所期望收到的下一個序號。序號字段和確認號字段用於TCP發送方和接收方來實現可靠數據傳輸服務。
4. 4比特的首部長度指示了以32比特爲單位的TCP的首部長度。一般TCP首部長度爲20字節,則該字段爲0110。
5. 6位作爲保留字段。
6. 6比特的標誌字段。URG比特用來指示報文段存在着被髮送方的上層實體置爲“緊急”的數據。ACK比特用於指示確認字段中的值是有效的。RST, SYN, FIN用於連接的建立和拆除。PSH比特指示接受方應立即將數據交給上層。
7. 16位的接收窗口字段,該字段指示接收方願意接受的字節數量,用於流量控制。
8. 16位校驗和。
9. 16位緊急數據指針爲緊急數據的最後一個字節。
在這裏插入圖片描述
IP 數據報
TCP數據一般是被封裝在一個IP數據報中,如下圖
在這裏插入圖片描述
在這裏插入圖片描述

  1. 4比特版本號。規定了數據報IP協議的版本。如IPv4和IPv6。通過查看版本號,路由器能夠確定如何解釋IP數據報剩餘部分。
  2. 4位首部長度。用於確定IP數據報中數據部分從哪裏開始。
  3. 8位服務類型。用於區分不同類型的數據報。
  4. 16位數據報長度,爲IP數據報的總長度(首部加數據)。最大長度理論爲65535字節,但是通常數據報很少超過1500字節。
  5. 16位比特標誌,3位標誌,13位片偏移。這三個字段與IP分片有關。1)標識字段唯一地標識一個IP數據包,每發送一份報文,其值會加1。2)標誌位字段佔3位,第一位爲保留位,第二位爲DF,爲1表示禁止IP分片,爲0代表允許分片。第三位爲MF,爲1代表此IP數據報不是最後一片,爲0代表IP數據報是最後一片或者未分片。3)片偏移字段佔13位,表示一個IP分組分片封裝原IP分組數據的相對偏移量,以8字節爲單位。
  6. 8位TTL生存時間字段設置了數據包可以經過的最多路由數。路由器每轉發一次分組,TTL減1,如果TTL=0,則路由器丟棄該分組。
  7. 協議字段佔8位,指示IP分組封裝的是哪個協議的數據包。1表示ICMP協議,2表示IGMP協議,6代表TCP協議,17表示UDP協議。
  8. 首部檢驗和字段佔16位,實現對IP分組首部的差錯檢測。
  9. 源IP地址和目的IP地址字段各佔32位,分別標識發送分組的源主機/路由器和接受分組的目的主機/路由器的IP地址
  10. 選項字段一個可變長的可選信息,攜帶安全,源選路徑,時間戳和路由記錄等內容。其中包含填充字段,用於32位對齊。

變量和0比較

bool: if (!var){ ... } //bool
int : if (var == 0){ ... } //int 
float :if ( (var >= -0.00001) && (var <= 0.00001)) //float
if(p = NULL) //指針
if(p != NULL)

錯誤做法

//bool
if(flag == TRUE)
if(flag == 1)
if(flag == FALSE)
if(flag == 0)
//int 
if(value)
if(!value)
//float 
if(x == 0.0)
//指針
if(p)
if(!p)

mysql

  • 1、mysql 中的升序和降序
    mySQL裏desc和asc的意思
    desc是descend 降序意思
    asc 是ascend 升序意思
    sql = “select 表內容名 from 數據庫表名 Putout=true order by 讀取的排序表名 asc”

例如
sql = “select * from user where Putout=true order by time desc” //按最新時間來排序
sql = “select * from user where Putout=true order by time asc” //按早時間來排序

  • 2、效率select *與select
    在數據量不大的表中 如果列沒有索引 select id 和select * 執行的代價是一樣的 但是操作的數據量不同
    如果列有索引 那麼select id 比select * 以及select 無索引列的執行代價要小很多 操作的數據量也少很多

在數據量較大的表中 select id 比select * 執行的代價要低 操作的數據量要少 查詢的時間要少

如果列有索引 那麼select 有索引列 比select *以及select 無索引列 的執行代價要小很多 操作的數據量也少很多 查詢的時間要少很多。

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