欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OpenMP?Parallel?Construct的實(shí)現(xiàn)原理詳解

 更新時(shí)間:2023年01月28日 09:17:54   作者:一無是處的研究僧  
在本篇文章當(dāng)中我們將主要分析?OpenMP?當(dāng)中的?parallel?construct?具體時(shí)如何實(shí)現(xiàn)的,以及這個(gè)?construct?調(diào)用了哪些運(yùn)行時(shí)庫函數(shù),并且詳細(xì)分析這期間的參數(shù)傳遞,需要的可以參考一下

Parallel 分析——編譯器角度

在本小節(jié)當(dāng)中我們將從編譯器的角度去分析該如何處理 parallel construct 。首先從詞法分析和語法分析的角度來說這對(duì)編譯器并不難,只需要加上一些處理規(guī)則,關(guān)鍵是編譯器將一個(gè) parallel construct 具體編譯成了什么?

下面是一個(gè)非常簡(jiǎn)單的 parallel construct。

#pragma?omp?parallel
{
??body;
}

編譯器在遇到上面的 parallel construct 之后會(huì)將代碼編譯成下面的樣子:

void?subfunction?(void?*data)
{
??use?data;
??body;
}

setup?data;
GOMP_parallel_start?(subfunction,?&data,?num_threads);
subfunction?(&data);
GOMP_parallel_end?();

首先 parallel construct 中的代碼塊會(huì)被編譯成一個(gè)函數(shù) sub function,當(dāng)然了函數(shù)名不一定是這個(gè),然后會(huì)在使用 #pragma omp parallel 的函數(shù)當(dāng)中將一個(gè) parallel construct 編譯成 OpenMP 動(dòng)態(tài)庫函數(shù)的調(diào)用,在上面的偽代碼當(dāng)中也指出了,具體會(huì)調(diào)用 OpenMP 的兩個(gè)庫函數(shù) GOMP_parallel_start 和 GOMP_parallel_end ,并且主線程也會(huì)調(diào)用函數(shù) subfunction ,我們?cè)诤竺娴奈恼庐?dāng)中在仔細(xì)分析這兩個(gè)動(dòng)態(tài)庫函數(shù)的源代碼。

深入剖析 Parallel 動(dòng)態(tài)庫函數(shù)參數(shù)傳遞

動(dòng)態(tài)庫函數(shù)分析

在本小節(jié)當(dāng)中,我們主要去分析一下在 OpenMP 當(dāng)中共享參數(shù)是如何傳遞的,以及介紹函數(shù) GOMP_parallel_start 的幾個(gè)參數(shù)的含義。

首先我們分析函數(shù) GOMP_parallel_start 的參數(shù)含義,這個(gè)函數(shù)的函數(shù)原型如下:

void?GOMP_parallel_start?(void?(*fn)(void?*),?void?*data,?unsigned?num_threads)

上面這個(gè)函數(shù)一共有三個(gè)參數(shù):

第一個(gè)參數(shù) fn 是一個(gè)函數(shù)指針,主要是用于指向上面編譯出來的 subfunction 這個(gè)函數(shù)的,因?yàn)樾枰鄠€(gè)線程同時(shí)執(zhí)行這個(gè)函數(shù),因此需要將這個(gè)函數(shù)傳遞過去,讓不同的線程執(zhí)行。

第二個(gè)參數(shù)是傳遞的數(shù)據(jù),我們?cè)诓⑿杏虍?dāng)中會(huì)使用到共享的或者私有的數(shù)據(jù),這個(gè)指針主要是用于傳遞數(shù)據(jù)的,我們?cè)诤竺鏁?huì)仔細(xì)分析這個(gè)參數(shù)的使用。

第三個(gè)參數(shù)是表示 num_threads 子句指定的線程個(gè)數(shù),如果不指定這個(gè)子句默認(rèn)的參數(shù)是 0 ,但是如果你使用了 IF 子句并且條件是 false 的話,那么這個(gè)參數(shù)的值就是 1 。

這個(gè)函數(shù)的主要作用是啟動(dòng)一個(gè)或者多個(gè)線程,并且執(zhí)行函數(shù) fn 。

void?GOMP_parallel_end?(void)

這個(gè)函數(shù)的主要作用是進(jìn)行線程的同步,因?yàn)橐粋€(gè) parallel 并行域需要等待所有的線程都執(zhí)行完成之后才繼續(xù)往后執(zhí)行。除此之外還需要釋放線程組的資源并行返回到之前的 omp_in_parallel() 表示的狀態(tài)。

參數(shù)傳遞分析

我們現(xiàn)在使用下面的代碼來具體分析參數(shù)傳遞過程:

#include?<stdio.h>
#include?"omp.h"

int?main()
{
??int?data?=?100;
??int?two??=?-100;
??printf("start\n");
#pragma?omp?parallel?num_threads(4)?default(none)?shared(data,?two)
??{
????printf("tid?=?%d?data?=?%d?two?=?%d\n",?omp_get_thread_num(),?data,?two);
??}

??printf("finished\n");
??return?0;
}

我們首先來分析一下上面的兩個(gè)變量 data 和 two 的是如何被傳遞的,我們首先用圖的方式進(jìn)行表示,然后分析一下匯編程序并且對(duì)圖進(jìn)行驗(yàn)證。

上面的代碼當(dāng)中兩個(gè)變量 data 和 two 在內(nèi)存當(dāng)中的布局結(jié)構(gòu)大致如下所示(假設(shè) data 的初始位置時(shí) 0x0):

那么在函數(shù) GOMP_parallel_start 當(dāng)中傳遞的參數(shù) data 就是 0x0 也就是指向 data 的內(nèi)存地址,如下圖所示:

那么根據(jù)上面參數(shù)傳遞的情況,我們就可以在 subfunction 當(dāng)中使用 *(int*)data 得到 data 的值,使用 *((int*) ((char*)data + 4)) 得到 two 的值,如果是 private 傳遞的話我們就可以先拷貝這個(gè)數(shù)據(jù)再使用,如果是 shared 的話,那么我們就可以直接使用指針就行啦。

上面的程序我們用 pthread 大致描述一下,則 pthread 對(duì)應(yīng)的代碼如下所示:

#include?"pthread.h"
#include?"stdio.h"
#include?"stdint.h"

typedef?struct?data_in_main_function{
????int?data;
????int?two;
}data_in_main_function;

pthread_t?threads[4];

void*?subfunction(void*?data)
{
??int?two?=?((data_in_main_function*)data)->two;
??int?data_?=?((data_in_main_function*)data)->data;
??printf("tid?=?%ld?data?=?%d?two?=?%d\n",?pthread_self(),?data_,?two);
??return?NULL;
}

int?main()
{
??//?在主函數(shù)申請(qǐng)?8?個(gè)字節(jié)的棧空間
??data_in_main_function?data;
??data.data?=?100;
??data.two?=?-100;
??for(int?i?=?0;?i?<?4;?++i)
??{
????pthread_create(&threads[i],?NULL,?subfunction,?&data);
??}
??for(int?i?=?0;?i?<?4;?++i)
??{
????pthread_join(threads[i],?NULL);
??}
??return?0;
}

匯編程序分析

在本節(jié)當(dāng)中我們將仔細(xì)去分析上面的程序所產(chǎn)生的匯編程序,在本文當(dāng)中的匯編程序基礎(chǔ) x86_64 平臺(tái)。在分析匯編程序之前我們首先需要了解一下 x86函數(shù)的調(diào)用規(guī)約,具體來說就是在進(jìn)行函數(shù)調(diào)用的時(shí)候哪些寄存器保存函數(shù)參數(shù)以及是第幾個(gè)函數(shù)參數(shù)。具體的規(guī)則如下所示:

寄存器含義
rdi第一個(gè)參數(shù)
rsi第二個(gè)參數(shù)
rdx第三個(gè)參數(shù)
rcx第四個(gè)參數(shù)
r8第五個(gè)參數(shù)
r9第六個(gè)參數(shù)

我們現(xiàn)在仔細(xì)分析一下上面的程序的 main 函數(shù)的反匯編程序:

00000000004006cd <main>:
  4006cd:       55                      push   %rbp
  4006ce:       48 89 e5                mov    %rsp,%rbp
  4006d1:       48 83 ec 10             sub    $0x10,%rsp
  4006d5:       c7 45 fc 64 00 00 00    movl   $0x64,-0x4(%rbp)
  4006dc:       c7 45 f8 9c ff ff ff    movl   $0xffffff9c,-0x8(%rbp)
  4006e3:       bf f4 07 40 00          mov    $0x4007f4,%edi
  4006e8:       e8 93 fe ff ff          callq  400580 <puts@plt>
  4006ed:       8b 45 fc                mov    -0x4(%rbp),%eax
  4006f0:       89 45 f0                mov    %eax,-0x10(%rbp)
  4006f3:       8b 45 f8                mov    -0x8(%rbp),%eax
  4006f6:       89 45 f4                mov    %eax,-0xc(%rbp)
  4006f9:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  4006fd:       ba 04 00 00 00          mov    $0x4,%edx
  400702:       48 89 c6                mov    %rax,%rsi
  400705:       bf 3d 07 40 00          mov    $0x40073d,%edi
  40070a:       e8 61 fe ff ff          callq  400570 <GOMP_parallel_start@plt>
  40070f:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  400713:       48 89 c7                mov    %rax,%rdi
  400716:       e8 22 00 00 00          callq  40073d <main._omp_fn.0>
  40071b:       e8 70 fe ff ff          callq  400590 <GOMP_parallel_end@plt>
  400720:       8b 45 f0                mov    -0x10(%rbp),%eax
  400723:       89 45 fc                mov    %eax,-0x4(%rbp)
  400726:       8b 45 f4                mov    -0xc(%rbp),%eax
  400729:       89 45 f8                mov    %eax,-0x8(%rbp)
  40072c:       bf fa 07 40 00          mov    $0x4007fa,%edi
  400731:       e8 4a fe ff ff          callq  400580 <puts@plt>
  400736:       b8 00 00 00 00          mov    $0x0,%eax
  40073b:       c9                      leaveq 
  40073c:       c3                      retq   

從上面的反匯編程序我們可以看到在主函數(shù)的匯編代碼當(dāng)中確實(shí)調(diào)用了函數(shù) GOMP_parallel_start 和 GOMP_parallel_end,并且 subfunction 為 main._omp_fn.0 ,它對(duì)應(yīng)的匯編程序如下所示:

000000000040073d <main._omp_fn.0>:
  40073d:       55                      push   %rbp
  40073e:       48 89 e5                mov    %rsp,%rbp
  400741:       48 83 ec 10             sub    $0x10,%rsp
  400745:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
  400749:       e8 52 fe ff ff          callq  4005a0 <omp_get_thread_num@plt>
  40074e:       48 8b 55 f8             mov    -0x8(%rbp),%rdx
  400752:       8b 4a 04                mov    0x4(%rdx),%ecx
  400755:       48 8b 55 f8             mov    -0x8(%rbp),%rdx
  400759:       8b 12                   mov    (%rdx),%edx
  40075b:       89 c6                   mov    %eax,%esi
  40075d:       bf 03 08 40 00          mov    $0x400803,%edi
  400762:       b8 00 00 00 00          mov    $0x0,%eax
  400767:       e8 44 fe ff ff          callq  4005b0 <printf@plt>
  40076c:       c9                      leaveq 
  40076d:       c3                      retq   
  40076e:       66 90                   xchg   %ax,%ax

GOMP_parallel_start 詳細(xì)參數(shù)分析

void (*fn)(void *), 我們現(xiàn)在來看一下函數(shù) GOMP_parallel_start 的第一個(gè)參數(shù),根據(jù)我們前面談到的第一個(gè)參數(shù)應(yīng)該保存在 rdi 寄存器,我們現(xiàn)在分析一下在 main 函數(shù)的反匯編程序當(dāng)中在調(diào)用函數(shù) GOMP_parallel_start 之前 rdi 寄存器的值。我們可以看到在 main 函數(shù)位置為 4006f8 的地方的指令  mov $0x40073d,%edi 可以看到 rdi 寄存器的值為 0x40073d (edi 寄存器是 rdi 寄存器的低 32 位),我們可以看到 函數(shù) main._omp_fn.0 的起始地址就是 0x40073d ,因此我們就可以在函數(shù) GOMP_parallel_start 使用這個(gè)函數(shù)指針了,最終在啟動(dòng)的線程當(dāng)中調(diào)用這個(gè)函數(shù)。

void *data,這是函數(shù) GOMP_parallel_start 的第二個(gè)參數(shù),根據(jù)前面的分析第二個(gè)參數(shù)保存在 rsi 寄存器當(dāng)中,我現(xiàn)在將 main 數(shù)當(dāng)中和 rsi 相關(guān)的指令選擇出來:

00000000004006cd <main>:
  4006cd:       55                      push   %rbp
  4006ce:       48 89 e5                mov    %rsp,%rbp
  4006d1:       48 83 ec 10             sub    $0x10,%rsp
  4006d5:       c7 45 fc 64 00 00 00    movl   $0x64,-0x4(%rbp)
  4006dc:       c7 45 f8 9c ff ff ff    movl   $0xffffff9c,-0x8(%rbp)
  4006ed:       8b 45 fc                mov    -0x4(%rbp),%eax
  4006f0:       89 45 f0                mov    %eax,-0x10(%rbp)
  4006f3:       8b 45 f8                mov    -0x8(%rbp),%eax
  4006f6:       89 45 f4                mov    %eax,-0xc(%rbp)
  4006f9:       48 8d 45 f0             lea    -0x10(%rbp),%rax
  400702:       48 89 c6                mov    %rax,%rsi

上面的匯編程序的??臻g以及在調(diào)用函數(shù)之前 GOMP_parallel_start 部分寄存器的指向如下所示:

最終在調(diào)用函數(shù) GOMP_parallel_start 之前 rsi 寄存器的指向如上圖所示,上圖當(dāng)中 rsi 的指向的內(nèi)存地址作為參數(shù)傳遞過去。根據(jù)上文談到的 subfunction 中的參數(shù)可以知道,在函數(shù) main._omp_fn.0 當(dāng)中的 rdi 寄存器(也就是第一個(gè)參數(shù) *data)的值就是上圖當(dāng)中 rsi 寄存器指向的內(nèi)存地址的值(事實(shí)上也就是 rsi 寄存器的值)。大家可以自行對(duì)照著函數(shù) main._omp_fn.0  的匯編程序?qū)?rdi 寄存器的使用就可以知道這其中的參數(shù)傳遞的過程了。

unsigned num_threads,根據(jù)前文提到的保存第三個(gè)參數(shù)的寄存器是 rdx,在 main 函數(shù)的位置 4006fd 處,指令為 mov $0x4,%edx,這和我們自己寫的程序是一致的都是 4 (0x4)。

動(dòng)態(tài)庫函數(shù)源碼分析

GOMP_parallel_start 源碼分析

我們首先來看一下函數(shù) GOMP_parallel_start 的源代碼:

void
GOMP_parallel_start?(void?(*fn)?(void?*),?void?*data,?unsigned?num_threads)
{
??num_threads?=?gomp_resolve_num_threads?(num_threads,?0);
??gomp_team_start?(fn,?data,?num_threads,?gomp_new_team?(num_threads));
}

在這里我們對(duì)函數(shù) gomp_team_start 進(jìn)行分析,其他兩個(gè)函數(shù) gomp_resolve_num_threads 和 gomp_new_team 只簡(jiǎn)單進(jìn)行作用說明,太細(xì)致的源碼分析其實(shí)是沒有必要的,感興趣的同學(xué)自行分析即可,我們只需要了解整個(gè)執(zhí)行流程即可。

  • gomp_resolve_num_threads,這個(gè)函數(shù)的主要作用是最終確定需要幾個(gè)線程去執(zhí)行任務(wù),因?yàn)槲覀兛赡懿]有使用 num_threads 子句,而且這個(gè)值和環(huán)境變量也有關(guān)系,因此需要對(duì)線程的個(gè)數(shù)進(jìn)行確定。
  • gomp_new_team,這個(gè)函數(shù)的主要作用是創(chuàng)建包含 num_threads 個(gè)線程數(shù)據(jù)的線程組,并且對(duì)數(shù)據(jù)進(jìn)行初始化操作。
  • gomp_team_start,這個(gè)函數(shù)的主要作用是啟動(dòng) num_threads 個(gè)線程去執(zhí)行函數(shù) fn ,這其中涉及一些細(xì)節(jié),比如說線程的親和性(affinity)設(shè)置。

由于 gomp_team_start 的源代碼太長(zhǎng)了,這里只是節(jié)選部分源程序進(jìn)行分析:

??/*?Launch?new?threads.??*/
??for?(;?i?<?nthreads;?++i,?++start_data)
????{
??????pthread_t?pt;
??????int?err;

??????start_data->fn?=?fn;?//?這行代碼就是將?subfunction?函數(shù)指針進(jìn)行保存最終在函數(shù)??gomp_thread_start?當(dāng)中進(jìn)行調(diào)用
??????start_data->fn_data?=?data;?//?這里保存函數(shù)?subfunction?的函數(shù)參數(shù)
??????start_data->ts.team?=?team;?//?線程的所屬組
??????start_data->ts.work_share?=?&team->work_shares[0];
??????start_data->ts.last_work_share?=?NULL;
??????start_data->ts.team_id?=?i;?//?線程的?id?我們可以使用函數(shù)?omp_get_thread_num?得到這個(gè)值
??????start_data->ts.level?=?team->prev_ts.level?+?1;
??????start_data->ts.active_level?=?thr->ts.active_level;
#ifdef?HAVE_SYNC_BUILTINS
??????start_data->ts.single_count?=?0;
#endif
??????start_data->ts.static_trip?=?0;
??????start_data->task?=?&team->implicit_task[i];
??????gomp_init_task?(start_data->task,?task,?icv);
??????team->implicit_task[i].icv.nthreads_var?=?nthreads_var;
??????start_data->thread_pool?=?pool;
??????start_data->nested?=?nested;
???//?如果使用了線程的親和性那么還需要進(jìn)行親和性設(shè)置
??????if?(gomp_cpu_affinity?!=?NULL)
?gomp_init_thread_affinity?(attr);

??????err?=?pthread_create?(&pt,?attr,?gomp_thread_start,?start_data);
??????if?(err?!=?0)
?gomp_fatal?("Thread?creation?failed:?%s",?strerror?(err));
????}

上面的程序就是最終啟動(dòng)線程的源程序,可以看到這是一個(gè) for 循環(huán)并且啟動(dòng) nthreads 個(gè)線程,pthread_create 是真正創(chuàng)建了線程的代碼,并且讓線程執(zhí)行函數(shù) gomp_thread_start 可以看到線程不是直接執(zhí)行 subfunction 而是將這個(gè)函數(shù)指針保存到 start_data 當(dāng)中,并且在函數(shù) gomp_thread_start 真正去調(diào)用這個(gè)函數(shù),看到這里大家應(yīng)該明白了整個(gè) parallel construct 的整個(gè)流程了。

gomp_thread_start 的函數(shù)題也相對(duì)比較長(zhǎng),在這里我們選中其中的比較重要的幾行代碼,其余的代碼進(jìn)行省略。對(duì)比上面線程啟動(dòng)的 pthread_create 語句我們可以知道,下面的程序真正的調(diào)用了 subfunction,并且給這個(gè)函數(shù)傳遞了對(duì)應(yīng)的參數(shù)。

static?void?*
gomp_thread_start?(void?*xdata)
{
??struct?gomp_thread_start_data?*data?=?xdata;
??/*?Extract?what?we?need?from?data.??*/
??local_fn?=?data->fn;
??local_data?=?data->fn_data;
??local_fn?(local_data);
??return?NULL;
}

GOMP_parallel_end 分析

這個(gè)函數(shù)的主要作用就是一個(gè)同步點(diǎn),保證所有的線程都執(zhí)行完成之后再繼續(xù)往后執(zhí)行,這一部分的源代碼比較雜,其核心原理就是使用路障 barrier 去實(shí)現(xiàn)的,這其中是 OpenMP 自己實(shí)現(xiàn)的一個(gè) barrier 而不是直接使用 pthread 當(dāng)中的 barrier ,這一部分的源程序就不進(jìn)行仔細(xì)分析了,感興趣的同學(xué)可以自行閱讀,可以參考 OpenMP 鎖實(shí)現(xiàn)原理 。

總結(jié)

在本篇文章當(dāng)中主要給大家介紹了 parallel construct 的實(shí)現(xiàn)原理,以及他的動(dòng)態(tài)庫函數(shù)的調(diào)用以及源代碼分析,大家只需要了解整個(gè)流程不太需要死扣細(xì)節(jié)(這并無很大的用處)只有當(dāng)我們自己需要去實(shí)現(xiàn) OpenMP 的時(shí)候需要去了解這些細(xì)節(jié),不然我們只需要了解整個(gè)動(dòng)態(tài)庫的設(shè)計(jì)原理即可!

以上就是OpenMP Parallel Construct的實(shí)現(xiàn)原理詳解的詳細(xì)內(nèi)容,更多關(guān)于OpenMP Parallel Construct的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++數(shù)位DP復(fù)雜度統(tǒng)計(jì)數(shù)字問題示例詳解

    C++數(shù)位DP復(fù)雜度統(tǒng)計(jì)數(shù)字問題示例詳解

    這篇文章主要為大家介紹了利用C++數(shù)位DP的復(fù)雜度來統(tǒng)計(jì)數(shù)字問題的示例實(shí)現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升值加薪
    2021-11-11
  • C++中小數(shù)點(diǎn)輸出格式(實(shí)例代碼)

    C++中小數(shù)點(diǎn)輸出格式(實(shí)例代碼)

    下面小編就為大家?guī)硪黄狢++中小數(shù)點(diǎn)輸出格式(實(shí)例代碼)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • C++哈希表之線性探測(cè)法實(shí)現(xiàn)詳解

    C++哈希表之線性探測(cè)法實(shí)現(xiàn)詳解

    線性探測(cè)法的優(yōu)點(diǎn):只要散列表未滿,總能找到一個(gè)不沖突的散列地址;缺點(diǎn):每個(gè)產(chǎn)生沖突的記錄被散列到離沖突最近的空地址上,從而又增加了更多的沖突機(jī)會(huì)
    2022-05-05
  • C++使用鏈表實(shí)現(xiàn)圖書管理系統(tǒng)

    C++使用鏈表實(shí)現(xiàn)圖書管理系統(tǒng)

    這篇文章主要介紹了C++使用鏈表實(shí)現(xiàn)圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Matlab實(shí)現(xiàn)簡(jiǎn)易紀(jì)念碑谷游戲的示例代碼

    Matlab實(shí)現(xiàn)簡(jiǎn)易紀(jì)念碑谷游戲的示例代碼

    《紀(jì)念碑谷》是USTWO公司開發(fā)制作的解謎類手機(jī)游戲,在游戲中,通過探索隱藏小路、發(fā)現(xiàn)視力錯(cuò)覺以及躲避神秘的烏鴉人來幫助沉默公主艾達(dá)走出紀(jì)念碑迷陣。本文將用Matlab編寫簡(jiǎn)易版的紀(jì)念碑谷游戲,感興趣的可以了解一下
    2022-03-03
  • C++實(shí)現(xiàn)四叉樹效果(附源碼下載)

    C++實(shí)現(xiàn)四叉樹效果(附源碼下載)

    這篇文章主要介紹了C++實(shí)現(xiàn)四叉樹效果(附源碼下載),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • C++實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)

    C++實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • 如何利用C語言實(shí)現(xiàn)最簡(jiǎn)單的HTTP服務(wù)器詳解

    如何利用C語言實(shí)現(xiàn)最簡(jiǎn)單的HTTP服務(wù)器詳解

    這篇文章主要給大家介紹了關(guān)于如何利用C語言實(shí)現(xiàn)最簡(jiǎn)單的HTTP服務(wù)器的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C語言具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • C語言 數(shù)據(jù)結(jié)構(gòu)與算法之字符串詳解

    C語言 數(shù)據(jù)結(jié)構(gòu)與算法之字符串詳解

    這篇文章將帶大家深入了解C語言數(shù)據(jù)結(jié)構(gòu)與算法中的字符串,文中主要是介紹了字符串的定義、字符串的比較以及一些串的抽象數(shù)據(jù)類型,感興趣的可以學(xué)習(xí)一下
    2022-01-01
  • C語言 深入淺出講解指針的使用

    C語言 深入淺出講解指針的使用

    指針是C語言中一個(gè)非常重要的概念,也是C語言的特色之一。使用指針可以對(duì)復(fù)雜數(shù)據(jù)進(jìn)行處理,能對(duì)計(jì)算機(jī)的內(nèi)存分配進(jìn)行控制,在函數(shù)調(diào)用中使用指針還可以返回多個(gè)值
    2022-03-03

最新評(píng)論