最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文徹底搞懂IO底層原理

 更新時(shí)間:2021年06月17日 11:00:25   作者:vivo互聯(lián)網(wǎng)技術(shù)  
我們今天要給大家講的底層的IO看上去簡(jiǎn)單,實(shí)則抽象。并且在它之上衍生出了語(yǔ)言層面用于實(shí)戰(zhàn)的技術(shù),比如我們熟悉的java語(yǔ)言中的NIO或者像Netty這樣的框架

一、混亂的 IO 概念

IO是Input和Output的縮寫(xiě),即輸入和輸出。廣義上的圍繞計(jì)算機(jī)的輸入輸出有很多:鼠標(biāo)、鍵盤(pán)、掃描儀等等。而我們今天要探討的是在計(jì)算機(jī)里面,主要是作用在內(nèi)存、網(wǎng)卡、硬盤(pán)等硬件設(shè)備上的輸入輸出操作。

談起IO的模型,大多數(shù)人腦子里肯定是一坨混亂的概念,“阻塞”、“非阻塞”,“同步”、“異步”有什么區(qū)別?很多同學(xué)傻傻分不清,有嘗試去搜索相關(guān)資料去探究真相,結(jié)果又被淹沒(méi)在茫茫的概念之中。

這里嘗試簡(jiǎn)單地去解釋下為啥會(huì)出現(xiàn)這種現(xiàn)象,其中一個(gè)很重要的原因就是大家看到的資料對(duì)概念的解釋都站在了不同的角度,有的站在了底層內(nèi)核的視角,有的直接在java層面或者Netty框架層面給大家介紹API,所以給大家造成了一定程度的困擾。

所以在開(kāi)篇之前,還是要說(shuō)下本文所站的視角,我們將會(huì)從底層內(nèi)核的層面給大家講解下IO。因?yàn)槿f(wàn)變不離其宗,只有了解了底層原理,不管語(yǔ)言層面如何花里胡哨,我們都能以不變應(yīng)萬(wàn)變。

二、用戶空間和內(nèi)核空間

為了便于大家理解復(fù)雜的IO以及零拷貝相關(guān)的技術(shù),我們還是得花點(diǎn)時(shí)間在回顧下操作系統(tǒng)相關(guān)的知識(shí)。這一節(jié)我們重點(diǎn)看下用戶空間和內(nèi)核空間,基于此后面我們才能更好地聊聊多路復(fù)用和零拷貝。

硬 件 層(Hardware)

包括和我們熟知的和IO相關(guān)的CPU、內(nèi)存、磁盤(pán)和網(wǎng)卡幾個(gè)硬件;

內(nèi)核空間(Kernel Space)

計(jì)算機(jī)開(kāi)機(jī)后首先會(huì)運(yùn)行內(nèi)核程序,內(nèi)核程序占用的一塊私有的空間就是內(nèi)核空間,并且可支持訪問(wèn)CPU所有的指令集(ring0 - ring3)以及所有的內(nèi)存空間、IO及硬件設(shè)備;

用戶空間(User Space)

每個(gè)普通的用戶進(jìn)程都有一個(gè)單獨(dú)的用戶空間,用戶空間只能訪問(wèn)受限的資源(CPU的“保護(hù)模式”)也就是說(shuō)用戶空間是無(wú)法直接操作像內(nèi)存、網(wǎng)卡和磁盤(pán)等硬件的;

如上所述,那我們可能會(huì)有疑問(wèn),用戶空間的進(jìn)程想要去訪問(wèn)或操作磁盤(pán)和網(wǎng)卡該怎么辦呢?

為此,操作系統(tǒng)在內(nèi)核中開(kāi)辟了一塊唯一且合法的調(diào)用入口“System Call Interface”,也就是我們常說(shuō)的系統(tǒng)調(diào)用,系統(tǒng)調(diào)用為上層用戶提供了一組能夠操作底層硬件的API。這樣一來(lái),用戶進(jìn)程就可以通過(guò)系統(tǒng)調(diào)用訪問(wèn)到操作系統(tǒng)內(nèi)核,進(jìn)而就能夠間接地完成對(duì)底層硬件的操作。這個(gè)訪問(wèn)的過(guò)程也即用戶態(tài)到內(nèi)核態(tài)的切換。常見(jiàn)的系統(tǒng)調(diào)用有很多,比如:內(nèi)存映射mmap()、文件操作類(lèi)的open()、IO讀寫(xiě)read()、write()等等。

三、IO模型

3.1、BIO(Blocking IO)

我們先看一下大家都熟悉的BIO模型的 Java 偽代碼:

ServerSocket serverSocket = new ServerSocket(8080);        // step1: 創(chuàng)建一個(gè)ServerSocket,并監(jiān)聽(tīng)8080端口
while(true) {                                              // step2: 主線程進(jìn)入死循環(huán)
    Socket socket = serverSocket.accept();                 // step3: 線程阻塞,開(kāi)啟監(jiān)聽(tīng)
      
    BufferedReader reader = new BufferedReader(nwe InputStreamReader(socket.getInputStream()));
    System.out.println("read data: " + reader.readLine()); // step4: 數(shù)據(jù)讀取
  
  
    PrintWriter print = new PrintWriter(socket.getOutputStream(), true);
    print.println("write data");                           // step5: socket數(shù)據(jù)寫(xiě)入
}

這段代碼可以簡(jiǎn)單理解成一下幾個(gè)步驟:

  • 創(chuàng)建ServerSocket,并監(jiān)聽(tīng)8080端口;
  • 主線程進(jìn)入死循環(huán),用來(lái)阻塞監(jiān)聽(tīng)客戶端的連接,socket.accept();
  • 數(shù)據(jù)讀取,socket.read();
  • 寫(xiě)入數(shù)據(jù),socket.write();

問(wèn)題:

以上三個(gè)步驟:accept(...)、read(...)、write(...)都會(huì)造成線程阻塞。上述這個(gè)代碼使用了單線程,會(huì)導(dǎo)致主線程會(huì)直接夯死在阻塞的地方。

優(yōu)化:

我們要知道一點(diǎn)“進(jìn)程的阻塞是不會(huì)消耗CPU資源的”,所以在多核的環(huán)境下,我們可以創(chuàng)建多線程,把接收到的請(qǐng)求拋給多線程去處理,這樣就有效地利用了計(jì)算機(jī)的多核資源。甚至為了避免創(chuàng)建大量的線程處理請(qǐng)求,我們還可以進(jìn)一步做優(yōu)化,創(chuàng)建一個(gè)線程池,利用池化技術(shù),對(duì)暫時(shí)處理不了的請(qǐng)求做一個(gè)緩沖。

3.2、“C10K”問(wèn)題

“C10K”即“client 10k”用來(lái)指代數(shù)量龐大的客戶端;

BIO看上去非常的簡(jiǎn)單,事實(shí)上采用“BIO+線程池”來(lái)處理少量的并發(fā)請(qǐng)求還是比較合適的,也是最優(yōu)的。但是面臨數(shù)量龐大的客戶端和請(qǐng)求,這時(shí)候使用多線程的弊端就逐漸凸顯出來(lái)了:

  • 嚴(yán)重依賴線程,線程還是比較耗系統(tǒng)資源的(一個(gè)線程大約占用1M的空間);
  • 頻繁地創(chuàng)建和銷(xiāo)毀代價(jià)很大,因?yàn)樯婕暗綇?fù)雜的系統(tǒng)調(diào)用;
  • 線程間上下文切換的成本很高,因?yàn)榘l(fā)生線程切換前,需要保留上一個(gè)任務(wù)的狀態(tài),以便切回來(lái)的時(shí)候,可以再次加載這個(gè)任務(wù)的狀態(tài)。如果線程數(shù)量龐大,會(huì)造成線程做上下文切換的時(shí)間甚至大于線程執(zhí)行的時(shí)間,CPU負(fù)載變高。

3.3、NIO非阻塞模型

下面開(kāi)始真正走向Java NIO或者Netty框架所描述的“非阻塞”,NIO叫Non-Blocking IO或者New IO,由于BIO可能會(huì)引入的大量線程,所以可以簡(jiǎn)單地理解NIO處理問(wèn)題的方式是通過(guò)單線程或者少量線程達(dá)到處理大量客戶端請(qǐng)求的目的。為了達(dá)成這個(gè)目的,首先要做的就是把阻塞的過(guò)程非阻塞化。要想做到非阻塞,那必須得要有內(nèi)核的支持,同時(shí)需要對(duì)用戶空間的進(jìn)程暴露系統(tǒng)調(diào)用函數(shù)。所以,這里的“非阻塞”可以理解成系統(tǒng)調(diào)用API級(jí)別的,而真正底層的IO操作都是阻塞的,我們后面會(huì)慢慢介紹。

事實(shí)上,內(nèi)核已經(jīng)對(duì)“非阻塞”做好了支持,舉個(gè)我們剛剛說(shuō)的的accept()方法阻塞的例子(Tips:java中的accept方法對(duì)應(yīng)的系統(tǒng)調(diào)用函數(shù)也叫accept),看下官方文檔對(duì)其非阻塞部分的描述。

官方文檔對(duì)accetp()系統(tǒng)調(diào)用的描述是通過(guò)把"flags"參數(shù)設(shè)成"SOCK_NONBLOCK"就可以達(dá)到非阻塞的目的,非阻塞之后線程會(huì)一直處理輪詢調(diào)用,這時(shí)候可以通過(guò)每次返回特殊的異常碼“EAGAIN”或"EWOULDBLOCK"告訴主程序還沒(méi)有連接到達(dá)可以繼續(xù)輪詢。

我們可以很容易想象程序非阻塞之后的一個(gè)大致過(guò)程。所以,非阻塞模式有個(gè)最大的特點(diǎn)就是:用戶進(jìn)程需要不斷去主動(dòng)詢問(wèn)內(nèi)核數(shù)據(jù)準(zhǔn)備好了沒(méi)有!

下面我們通過(guò)一段偽代碼,看下這個(gè)調(diào)用過(guò)程:

// 循環(huán)遍歷
while(1) {
    // 遍歷fd集合
    for (fdx in range(fd1, fdn)) {
        // 如果fdx有數(shù)據(jù)
        if (null != fdx.data) {
            // 進(jìn)行讀取和處理
            read(fdx)&handle(fdx);
        }
    }
}

這種調(diào)用方式也暴露出非阻塞模式的最大的弊端,就是需要讓用戶進(jìn)程不斷切換到內(nèi)核態(tài),對(duì)連接狀態(tài)或讀寫(xiě)數(shù)據(jù)做輪詢。有沒(méi)有一種方式來(lái)簡(jiǎn)化用戶空間for循環(huán)輪詢的過(guò)程呢?那就是我們下面要重點(diǎn)介紹的IO多路復(fù)用模型。

3.4、IO多路復(fù)用模型

非阻塞模型會(huì)讓用戶進(jìn)程一直輪詢調(diào)用系統(tǒng)函數(shù),頻繁地做內(nèi)核態(tài)切換。想要做優(yōu)化其實(shí)也比較簡(jiǎn)單,我們假想個(gè)業(yè)務(wù)場(chǎng)景,A業(yè)務(wù)系統(tǒng)會(huì)調(diào)用B的基礎(chǔ)服務(wù)查詢單個(gè)用戶的信息。隨著業(yè)務(wù)的發(fā)展,A的邏輯變復(fù)雜了,需要查100個(gè)用戶的信息。很明顯,A希望B提供一個(gè)批量查詢的接口,用集合作為入?yún)?,一次性把?shù)據(jù)傳遞過(guò)去就省去了頻繁的系統(tǒng)間調(diào)用。

多路復(fù)用實(shí)際也差不多就是這個(gè)實(shí)現(xiàn)思路,只不過(guò)入?yún)⑦@個(gè)“集合”需要你注冊(cè)/填寫(xiě)感興趣的事件,讀fd、寫(xiě)fd或者連接狀態(tài)的fd等,然后交給內(nèi)核幫你進(jìn)行處理。

那我們就具體來(lái)看看多路復(fù)用里面大家都可能聽(tīng)過(guò)的幾個(gè)系統(tǒng)調(diào)用- select()、poll()、epoll()。

3.4.1、select()

select()構(gòu)造函數(shù)信息如下所示:

/**
 * select()系統(tǒng)調(diào)用
 *
 * 參數(shù)列表:
 *     nfds       - 值為最大的文件描述符+1
 *    *readfds    - 用戶檢查可讀性
 *    *writefds   - 用戶檢查可寫(xiě)性
 *    *exceptfds  - 用于檢查外帶數(shù)據(jù)
 *    *timeout    - 超時(shí)時(shí)間的結(jié)構(gòu)體指針
 */
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

官方文檔對(duì)select()的描述:

DESCRIPTION

select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g.,input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

select()允許程序監(jiān)控多個(gè)fd,阻塞等待直到一個(gè)或多個(gè)fd到達(dá)"就緒"狀態(tài)。

內(nèi)核使用select()為用戶進(jìn)程提供了類(lèi)似批量的接口,函數(shù)本身也會(huì)一直阻塞直到有fd為就緒狀態(tài)返回。下面我們來(lái)具體看下select()函數(shù)實(shí)現(xiàn),以便我們更好地分析它有哪些優(yōu)缺點(diǎn)。在select()函數(shù)的構(gòu)造器里,我們很容易看到"fd_set"這個(gè)入?yún)㈩?lèi)型。它是用位圖算法bitmap實(shí)現(xiàn)的,使用了一個(gè)大小固定的數(shù)組(fd_set設(shè)置了FD_SETSIZE固定長(zhǎng)度為1024),數(shù)組中的每個(gè)元素都是0和1這樣的二進(jìn)制byte,0,1映射fd對(duì)應(yīng)位置上是否有讀寫(xiě)事件,舉例:如果fd == 5,那么fd_set = 000001000。

同時(shí)fd_set定義了四個(gè)宏來(lái)處理bitmap:

  • FD_ZERO(&set);// 初始化,清空的作用,使集合中不含任何fd
  • FD_SET(fd, &set);//將fd加入set集合,給某個(gè)位置賦值的操作
  • FD_CLR(fd, &set);//將fd從set集合中清除,去掉某個(gè)位置的值
  • FD_ISSET(fd, &set);//校驗(yàn)?zāi)澄恢玫膄d是否在集合中

使用bitmap算法的好處非常明顯,運(yùn)算效率高,占用內(nèi)存少(使用了一個(gè)byte,8bit)。我們用偽代碼和圖片來(lái)描述下用戶進(jìn)程調(diào)用select()的過(guò)程:

假設(shè)fds為{1, 2, 3, 5, 7}對(duì)應(yīng)的bitmap為"01110101",拋給內(nèi)核空間輪詢,當(dāng)有讀寫(xiě)事件時(shí)重新標(biāo)記同時(shí)停止阻塞,然后整體返回用戶空間。由此我們可以看到select()系統(tǒng)調(diào)用的弊端也是比較明顯的:

  • 復(fù)雜度O(n),輪詢的任務(wù)交給了內(nèi)核來(lái)做,復(fù)雜度并沒(méi)有變化,數(shù)據(jù)取出后也需要輪詢哪個(gè)fd上發(fā)生了變動(dòng);
  • 用戶態(tài)還是需要不斷切換到內(nèi)核態(tài),直到所有的fds數(shù)據(jù)讀取結(jié)束,整體開(kāi)銷(xiāo)依然很大;
  • fd_set有大小的限制,目前被硬編碼成了1024;
  • fd_set不可重用,每次操作完都必須重置;

3.4.2、poll()

poll()構(gòu)造函數(shù)信息如下所示:

/**
 * poll()系統(tǒng)調(diào)用
 *
 * 參數(shù)列表:
 *    *fds         - pollfd結(jié)構(gòu)體
 *     nfds        - 要監(jiān)視的描述符的數(shù)量
 *     timeout     - 等待時(shí)間
 */
int poll(struct pollfd *fds, nfds_t nfds, int *timeout);
  
  
### pollfd的結(jié)構(gòu)體
struct pollfd{
 int fd;// 文件描述符
 short event;// 請(qǐng)求的事件
 short revent;// 返回的事件
}

官方文檔對(duì)poll()的描述:

DESCRIPTION

poll() performs a similar task to select(2): it waits for one of a set of file descriptors to become ready to perform I/O.

poll() 非常像select(),它也是阻塞等待直到一個(gè)或多個(gè)fd到達(dá)"就緒"狀態(tài)。

看官方文檔描述可以知道,poll()和select()是非常相似的,唯一的區(qū)別在于poll()摒棄掉了位圖算法,使用自定義的結(jié)構(gòu)體pollfd,在pollfd內(nèi)部封裝了fd,并通過(guò)event變量注冊(cè)感興趣的可讀可寫(xiě)事件(POLLIN、POLLOUT),最后把pollfd交給內(nèi)核。當(dāng)有讀寫(xiě)事件觸發(fā)的時(shí)候,我們可以通過(guò)輪詢pollfd,判斷revent確定該fd是否發(fā)生了可讀可寫(xiě)事件。

老樣子我們用偽代碼來(lái)描述下用戶進(jìn)程調(diào)用poll()的過(guò)程:

poll()相對(duì)于select(),主要的優(yōu)勢(shì)是使用了pollfd的結(jié)構(gòu)體:

  • 沒(méi)有了bitmap大小1024的限制;
  • 通過(guò)結(jié)構(gòu)體中的revents置位;

但是用戶態(tài)到內(nèi)核態(tài)切換及O(n)復(fù)雜度的問(wèn)題依舊存在。

3.4.3、epoll()

epoll()應(yīng)該是目前最主流,使用范圍最廣的一組多路復(fù)用的函數(shù)調(diào)用,像我們熟知的Nginx、Redis都廣泛地使用了此種模式。接下來(lái)我們重點(diǎn)分析下,epoll()的實(shí)現(xiàn)采用了“三步走”策略,它們分別是epoll_create()、epoll_ctl()、epoll_wait()。

epoll_create()
/**
 * 返回專(zhuān)用的文件描述符
 */
int epoll_create(int size);

用戶進(jìn)程通過(guò)epoll_create()函數(shù)在內(nèi)核空間里面創(chuàng)建了一塊空間(為了便于理解,可以想象成創(chuàng)建了一塊白板),并返回了描述此空間的fd。

epoll_ctl()
/**
 * epoll_ctl()系統(tǒng)調(diào)用
 *
 * 參數(shù)列表:
 *     epfd       - 由epoll_create()返回的epoll專(zhuān)用的文件描述符
 *     op         - 要進(jìn)行的操作例如注冊(cè)事件,可能的取值:注冊(cè)-EPOLL_CTL_ADD、修改-EPOLL_CTL_MOD、刪除-EPOLL_CTL_DEL
 *     fd         - 關(guān)聯(lián)的文件描述符
 *     event      - 指向epoll_event的指針
 */
int epoll_ctl(int epfd, int op, int fd , struce epoll_event *event );

剛剛我們說(shuō)通過(guò)epoll_create()可以創(chuàng)建一塊具體的空間“白板”,那么通過(guò)epoll_ctl()我們可以通過(guò)自定義的epoll_event結(jié)構(gòu)體在這塊"白板上"注冊(cè)感興趣的事件了。

  • 注冊(cè) - EPOLL_CTL_ADD
  • 修改 - EPOLL_CTL_MOD
  • 刪除 - EPOLL_CTL_DEL
epoll_wait()
/**
 * epoll_wait()返回n個(gè)可讀可寫(xiě)的fds
 *
 * 參數(shù)列表:
 *     epfd           - 由epoll_create()返回的epoll專(zhuān)用的文件描述符
 *     epoll_event    - 要進(jìn)行的操作例如注冊(cè)事件,可能的取值:注冊(cè)-EPOLL_CTL_ADD、修改-EPOLL_CTL_MOD、刪除-EPOLL_CTL_DEL
 *     maxevents      - 每次能處理的事件數(shù)
 *     timeout        - 等待I/O事件發(fā)生的超時(shí)值;-1相當(dāng)于阻塞,0相當(dāng)于非阻塞。一般用-1即可
 */
int epoll_wait(int epfd, struce epoll_event *event , int maxevents, int timeout);

epoll_wait()會(huì)一直阻塞等待,直到硬盤(pán)、網(wǎng)卡等硬件設(shè)備數(shù)據(jù)準(zhǔn)備完成后發(fā)起硬中斷,中斷CPU,CPU會(huì)立即執(zhí)行數(shù)據(jù)拷貝工作,數(shù)據(jù)從磁盤(pán)緩沖傳輸?shù)絻?nèi)核緩沖,同時(shí)將準(zhǔn)備完成的fd放到就緒隊(duì)列中供用戶態(tài)進(jìn)行讀取。用戶態(tài)阻塞停止,接收到具體數(shù)量的可讀寫(xiě)的fds,返回用戶態(tài)進(jìn)行數(shù)據(jù)處理。

整體過(guò)程可以通過(guò)下面的偽代碼和圖示進(jìn)一步了解:

epoll()基本上完美地解決了poll()函數(shù)遺留的兩個(gè)問(wèn)題:

  • 沒(méi)有了頻繁的用戶態(tài)到內(nèi)核態(tài)的切換;
  • O(1)復(fù)雜度,返回的"nfds"是一個(gè)確定的可讀寫(xiě)的數(shù)量,相比于之前循環(huán)n次來(lái)確認(rèn),復(fù)雜度降低了不少;

四、同步、異步

細(xì)心的朋友可能會(huì)發(fā)現(xiàn),本篇文章一直在解釋“阻塞”和“非阻塞”,“同步”、“異步”的概念沒(méi)有涉及,其實(shí)在很多場(chǎng)景下同步&異步和阻塞&非阻塞基本上是一個(gè)同義詞。阻塞和非阻塞適合從系統(tǒng)調(diào)用API層面來(lái)看,就像我們本文介紹的select()、poll()這樣的系統(tǒng)調(diào)用,同步和異步更適合站在應(yīng)用程序的角度來(lái)看。應(yīng)用程序在同步執(zhí)行代碼片段的時(shí)候結(jié)果不會(huì)立即返回,這時(shí)候底層IO操作不一定是阻塞的,也完全有可能是非阻塞。所以說(shuō):

  • 阻塞和非阻塞:讀寫(xiě)沒(méi)有就緒或者讀寫(xiě)沒(méi)有完成,函數(shù)是否要一直等待還是采用輪詢;
  • 同步和異步:同步是讀寫(xiě)由應(yīng)用程序完成。異步是讀寫(xiě)由操作系統(tǒng)來(lái)完成,并通過(guò)回調(diào)的機(jī)制通知應(yīng)用程序。

這邊順便提兩種大家可能會(huì)經(jīng)常聽(tīng)到的模式:Reactor和Preactor。

  • Reactor 模式:主動(dòng)模式。
  • Preactor 模式:被動(dòng)模式。

五、總結(jié)

本篇文章從底層講解了下從BIO到NIO的一個(gè)過(guò)程,著重介紹了IO多路復(fù)用的幾個(gè)系統(tǒng)調(diào)用select()、poll()、epoll(),分析了下各自的優(yōu)劣,技術(shù)都是持續(xù)發(fā)展演進(jìn)的,目前也有很多的痛點(diǎn)。

以上就是一文徹底搞懂IO底層原理的詳細(xì)內(nèi)容,更多關(guān)于IO底層原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • c++中cin/cout與scanf/printf的區(qū)別比較

    c++中cin/cout與scanf/printf的區(qū)別比較

    這篇文章主要介紹了c++中cin/cout與scanf/printf的區(qū)別比較,需要的朋友可以參考下
    2017-06-06
  • C語(yǔ)言超詳細(xì)解析函數(shù)棧幀

    C語(yǔ)言超詳細(xì)解析函數(shù)棧幀

    在C語(yǔ)言中,每一個(gè)正在運(yùn)行的函數(shù)都有一個(gè)棧幀與其對(duì)應(yīng),棧幀中存儲(chǔ)的是該函數(shù)的返回地址和局部變量。從邏輯上講,棧幀就是一個(gè)函數(shù)執(zhí)行的環(huán)境:函數(shù)參數(shù)、函數(shù)的局部變量、函數(shù)執(zhí)行完后返回到哪里等等
    2022-03-03
  • Qt中正則表達(dá)式的常見(jiàn)用法(QRegularExpression類(lèi))

    Qt中正則表達(dá)式的常見(jiàn)用法(QRegularExpression類(lèi))

    正則表達(dá)式即一個(gè)文本匹配字符串的一種模式,Qt中使用QRegExp類(lèi)進(jìn)行模式匹配,下面這篇文章主要給大家介紹了關(guān)于Qt中正則表達(dá)式的常見(jiàn)用法,文中介紹的是QRegularExpression類(lèi)的相關(guān)資料,需要的朋友可以參考下
    2024-05-05
  • 數(shù)據(jù)結(jié)構(gòu)與算法中二叉樹(shù)子結(jié)構(gòu)的詳解

    數(shù)據(jù)結(jié)構(gòu)與算法中二叉樹(shù)子結(jié)構(gòu)的詳解

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)與算法中二叉樹(shù)子結(jié)構(gòu)的詳解的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • C++音樂(lè)播放按鈕的封裝過(guò)程詳解

    C++音樂(lè)播放按鈕的封裝過(guò)程詳解

    此篇文章用于記錄學(xué)習(xí)C++封裝音樂(lè)播放按鈕,封裝將對(duì)象的屬性和行為作為一個(gè)整體,表現(xiàn)生活中的事物、將屬性和行為加以權(quán)限控制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • c++中bitset的常見(jiàn)用法示例詳解

    c++中bitset的常見(jiàn)用法示例詳解

    C++ 中的?bitset?是一個(gè)用于處理固定大小位序列的模板類(lèi),提供高效的位操作功能,這篇文章主要介紹了c++中bitset的常見(jiàn)用法,需要的朋友可以參考下
    2025-02-02
  • 帶你了解C++的動(dòng)態(tài)內(nèi)存分配

    帶你了解C++的動(dòng)態(tài)內(nèi)存分配

    今天小編就為大家分享一篇關(guān)于關(guān)于C++動(dòng)態(tài)分配內(nèi)存的介紹,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2021-08-08
  • C語(yǔ)言返回值指針的函數(shù)詳解

    C語(yǔ)言返回值指針的函數(shù)詳解

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言返回值指針的函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-02-02
  • 手把手帶你學(xué)習(xí)C++的運(yùn)算符

    手把手帶你學(xué)習(xí)C++的運(yùn)算符

    這篇文章主要為大家介紹了C++運(yùn)算符,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助,希望能夠給你帶來(lái)幫助
    2021-11-11
  • C++深入探索內(nèi)聯(lián)函數(shù)inline與auto關(guān)鍵字的使用

    C++深入探索內(nèi)聯(lián)函數(shù)inline與auto關(guān)鍵字的使用

    本篇文章主要包括內(nèi)聯(lián)函數(shù)和auto關(guān)鍵字。其中,內(nèi)斂函數(shù)包括概念,特性等;auto關(guān)鍵字的使用規(guī)則,使用場(chǎng)景等,接下來(lái)讓我們深入了解
    2022-05-05

最新評(píng)論

杭锦旗| 万安县| 肥城市| 苗栗市| 樟树市| 和田县| 沅陵县| 遂溪县| 安龙县| 伊春市| 辉县市| 东阿县| 建水县| 奉贤区| 临澧县| 通城县| 山阳县| 凭祥市| 毕节市| 绍兴市| 固原市| 通州市| 阿拉善左旗| 武汉市| 滁州市| 泾川县| 汝阳县| 五河县| 郎溪县| 蒙山县| 中江县| 福建省| 尉犁县| 南康市| 辽阳市| 玛曲县| 横山县| 泰顺县| 洪湖市| 宜阳县| 开阳县|