linux下的RPC

一、概述

在傳統的編程概念中,過程是由程序員在本地編譯完成,并只能局限在本地運行的一段代碼,也即其主程序和過程之間的運行關系是本地調用關系。因此這種結構在網絡日益發展的今天已無法適應實際需求。總而言之,傳統過程調用模式無法充分利用網絡上其他主機的資源(如CPU、 Memory等),也無法提高代碼在實體間的共享程度,使得主機資源大量浪費。
而本文要介紹的RPC編程,正是很好地解決了傳統過程所存在的一系列弊端。通過RPC我們可以充分利用非共享內存的多處理器環境(例如通過局域網連接的多臺工作站),這樣可以簡便地將你的應用分布在多臺工作站上,應用程序就像運行在一個多處理器的計算機上一樣。你可以方便的實現過程代碼共享,提高系統資源的利用率,也可以將以大量數值處理的操作放在處理能力較強的系統上運行,從而減輕前端機的負擔。
二、RPC的結構原理及其調用機制

如前所述RPC其實也是一種C/S的編程模式,有點類似C/S Socket 編程模式,但要比它更高一層。當我們在建立RPC服務以后,客戶端的調用參數通過底層的RPC傳輸通道,可以是UDP,也可以是TCP(也即TI-RPC —無關性傳輸),并根據傳輸前所提供的目的地址及RPC上層應用程序號轉至相應的RPC Application Porgramme Server ,且此時的客戶端處于等待狀態,直至收到應答或Time Out超時信號。具體的流程圖如圖1。當服務器端獲得了請求消息,則會根據注冊RPC時告訴RPC系統的例程入口地址,執行相應的操作,并將結果返回至客戶端。
 ?當一次RPC調用結束后,相應線程發送相應的信號,客戶端程序才會繼續運行。

當然,一臺服務主機上可以有多個遠程過程提供服務,那么如何來表示一個唯一存在的遠程過程呢?一個遠程過程是有三個要素來唯一確定的:程序號、版本號和過程號。程序號是用來區別一組相關的并且具有唯一過程號的遠程過程。一個程序可以有一個或幾個不同的版本,而每個版本的程序都包含一系列能被遠程調用的過程,通過版本的引入,使得不同版本下的RPC能同時提供服務。每個版本都包含有許多可供遠程調用的過程,每個過程則有其唯一標示的過程號。
三、基于RPC的應用系統開發
通過以上對RPC原理的簡介后,我們再來繼續討論如何來開發基于RPC的應用系統。一般而言在開發RPC時,我們通常分為三個步驟:
a、定義說明客戶/服務器的通信協議。
這里所說的通信協議是指定義服務過程的名稱、調用參數的數據類型和返回參數的數據類型,還包括底層傳輸類型(可以是 UDP或TCP),當然也可以由RPC底層函數自動選擇連接類型建立TI-RPC。最簡單的協議生成的方法是采用協議編譯工具,常用的有Rpcgen,我會在后面實例中詳細描述其使用方法。
b、開發客戶端程序。
c、開發服務器端程序。
開發客戶端和服務器端的程序時,RPC提供了我們不同層次的開發例程調用接口。不同層次的接口提供了對RPC不同程度控制。一般可分為5個等級的編程接口,接下來我們分別討論一下各層所提供的功能函數。
1、簡單層例程
簡單層是面向普通RPC應用,為了快速開發RPC應用服務而設計的,他提供了如下功能函數。

函數名

功能描述

Rpc_reg( )

在一特定類型的傳輸層上注冊某個過程,來作為提供服務的?RPC?程序

Rpc_call( )

遠程調用在指定主機上指定的過程

Rpc_Broadcast( )

向指定類型的所有傳輸端口上廣播一個遠程過程調用請求

2、高層例程
在這一層,程序需要在發出調用請求前先創建一個客戶端句柄,或是在偵聽請求前先建立一個服務器端句柄。程序在該層可以自由的將自己的應用綁在所有的傳輸端口上,它提供了如下功能函數。

函數名

功能描述

Clnt_create( )

程序通過這個功能調用,告訴底層?RPC?服務器的位置及其傳輸類型

Clnt_create_timed( )

定義每次嘗試連接的超時最大時間

Svc_create( )

在指定類型的傳輸端口上建立服務器句柄,告訴底層?RPC?事件過程的相應入口地址

Clnt_call()

向服務器端發出一個?RPC?調用請求

3、中間層例程
中間層向程序提供更為詳細的RPC控制接口,而這一層的代碼變得更為復雜,但運行也更為有效,它提供了如下功能函數。

函數名

功能描述

Clnt_tp_create( )

在指定的傳輸端口上建立客戶端句柄

Clnt_tp_create_timed( )

定義最大傳輸時延

Svc_tp_creaet( )

在指定的傳輸端口上建立服務句柄

Clnt_call( )

向服務器端發出?RPC?調用請求

4、專家層例程
這層提供了更多的一系列與傳輸相關的功能調用,它提供了如下功能函數。

函數名

功能描述

Clnt_tli_create( )

在指定的傳輸端口上建立客戶端句柄

Svc_tli_create( )

在指定的傳輸端口上建立服務句柄

Rpcb_set( )

通過調用?rpcbind?將?RPC?服務和網絡地址做映射

Rpcb_unset( )

刪除?rpcb_set( )?所建的映射關系

Rpcb_getaddr( )

調用?rpcbind?來犯會指定?RPC?服務所對應的傳輸地址

Svc_reg( )

將指定的程序和版本號與相應的時間例程建起關聯

Svc_ureg( )

刪除有?svc_reg( )?所建的關聯

Clnt_call( )

客戶端向指定的服務器端發起?RPC?請求

5、底層例程
該層提供了所有對傳輸選項進行控制的調用接口,它提供了如下功能函數。

函數名

功能描述

Clnt_dg_create( )

采用無連接方式向遠程過程在客戶端建立客戶句柄

Svc_dg_create( )

采用無連接方式建立服務句柄

Clnt_vc_create( )

采用面向連接的方式建立客戶句柄

Svc_vc_create( )

采用面向連接的方式建立?RPC?服務句柄

Clnt_call( )

客戶端向服務器端發送調用請求

C語言進行rpc編程時可以使用rpcgen自動生成分布式的程序代碼文件,自己之需要編寫***.x,客戶端、服務器端程序以及相應的接口就可以了。下面根據一個實現遠程文件傳輸的rpc調用程序(客戶端根據文件名請求文件,服務器端傳回相應的文件),具體介紹使用rpcgen編寫rpc調用的過程,最后保證調用成功必須有8個源文件,rpcgen能自動生成3個,過程比較復雜,共分為7步:

?

1.????????根據rpc調用的功能,先不考慮rpc調用,編寫一個平常的實現相應功能的程序。

如一個遠程的文件傳輸的rpc調用,平常程序便是考慮文件存儲在本地,直接打開讀便可,如下:

#include <stdio.h>

#include <stdlib.h>

?

#define MAXNAME 20

#define MAXLENGTH 1024

char * readfile(char *);

?

int main()

{

?? ?char name[MAXNAME];

?

?? ?printf("Enter File Name: ");

?? ?scanf("%s", name);

?? ?printf("%s", readfile(name));

}

?

char * readfile(char * name)

{

?? ?FILE *file = fopen(name, "r");

?? ?char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);

?? ?if (file == NULL)

?? ?{

?? ? ? ?printf("File Cann't Be Open!");

?? ? ? ?return 0;

?? ?}

?? ?printf("The File Content is : /n");

?? ?while (fgets(buf, MAXLENGTH-1, file) != NULL)

?? ?{

?? ? ? ?return buf;

?? ?}

?? ?return NULL;

}

2.????????把程序拆分為兩部分,main函數和readfile函數,帶有main的一部分是主動發起調用的,在rpc中相當于客戶端,帶有readfile函數的部分是提供相應的功能的,相當于服務器端。將代碼拆分后要在客戶端添加相應的rpc調用函數,clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");

FILETRANSPROG便是trans.x中的程序名,FILETRANSVERS是版本名,使用tcp進行rpc調用。

?

拆分后代碼如下:

客戶端client.c

#include <rpc/rpc.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <unistd.h>

#include "trans.h"

?

#define WSVERS MAKEWORD(0, 2)

#define RMACHINE "localhost"

CLIENT *handle;

?

#define MAXNAME 20

#define MAXLENGTH 1024

?

char * readfile(char *);

?

int main()

{

????char name[MAXNAME];

????char * buf;

????printf("Enter File Name: ");

????scanf("%s", name);

???handle = clnt_create(RMACHINE, FILETRANSPROG, FILETRANSVERS,"tcp");

???if (handle == 0) {

?????????printf("Could Not Connect To Remote Server./n");

?????????exit(1);

???}

????buf = readfile(name);

????printf("%s", buf);

????return 0;

}

服務器端server.c

#include <rpc/rpc.h>

#include <stdio.h>

#include <stdlib.h>

#include "trans.h"

?

#define MAXNAME 20

#define MAXLENGTH 1024

?

char * readfile(char * name)

{

????FILE *file = fopen(name, "r");

????char * buf = (char *)malloc(sizeof(char)*MAXLENGTH);

????if (file == NULL)

????{

????????printf("File Cann't Be Open!");

????????return 0;

????}

????printf("The File Content is : /n");

????while (fgets(buf, MAXLENGTH-1, file) != NULL)

????{

????????return buf;

????}

????return NULL;

}

?

3.????????編寫***.x文件。具體步驟可以參考Douglas的那本Internetworking With TCP/IP?的第三卷,客戶端-服務器端編程與應用。

本程序的.x文件命名為trans.x內容如下:

const MAXLENGTH = 1024;

const MAXNAME = 20;

?

program FILETRANSPROG??//程序名

{?

????version FILETRANSVERS??//版本名

????{?

????????string READFILE(string) = 1;??//調用的方法名

????} = 1;?

} = 99;

?

4.????????使用rpcgen編輯.x文件,在linux下輸入命令

rpcgen???trans.x

若格式正確,編譯無錯誤則產生三個文件trans.htrans_svc.c(服務器端),trans_clnt.c(客戶端)。因為上述trans.x中無自定義數據結構,所以沒有xdr文件產生。

trans.h代碼:

/*

?* Please do not edit this file.

?* It was generated using rpcgen.

?*/

?

#ifndef _TRANS_H_RPCGEN

#define _TRANS_H_RPCGEN

?

#include <rpc/rpc.h>

?

?

#ifdef __cplusplus

extern "C" {

#endif

?

#define MAXLENGTH 1024

#define MAXNAME 20

?

#define FILETRANSPROG 99

#define FILETRANSVERS 1

?

#if defined(__STDC__) || defined(__cplusplus)

#define READFILE 1

extern??char ** readfile_1(char **, CLIENT *);

extern??char ** readfile_1_svc(char **, struct svc_req *);

extern int filetransprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

?

#else /* K&R C */

#define READFILE 1

extern??char ** readfile_1();

extern??char ** readfile_1_svc();

extern int filetransprog_1_freeresult ();

#endif /* K&R C */

?

#ifdef __cplusplus

}

#endif

?

#endif /* !_TRANS_H_RPCGEN */

?

trans_svc.c代碼:

/*

?* Please do not edit this file.

?* It was generated using rpcgen.

?*/

?

#include "trans.h"

#include <stdio.h>

#include <stdlib.h>

#include <rpc/pmap_clnt.h>

#include <string.h>

#include <memory.h>

#include <sys/socket.h>

#include <netinet/in.h>

?

#ifndef SIG_PF

#define SIG_PF void(*)(int)

#endif

?

static void

filetransprog_1(struct svc_req *rqstp, register SVCXPRT *transp)

{

???union {

????????????char *readfile_1_arg;

???} argument;

???char *result;

???xdrproc_t _xdr_argument, _xdr_result;

???char *(*local)(char *, struct svc_req *);

?

???switch (rqstp->rq_proc) {

???case NULLPROC:

????????????(void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);

????????????return;

?

???case READFILE:

????????????_xdr_argument = (xdrproc_t) xdr_wrapstring;

????????????_xdr_result = (xdrproc_t) xdr_wrapstring;

????????????local = (char *(*)(char *, struct svc_req *)) readfile_1;

????????????break;

?

???default:

????????????svcerr_noproc (transp);

????????????return;

???}

???memset ((char *)&argument, 0, sizeof (argument));

???if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {

????????????svcerr_decode (transp);

????????????return;

???}

???result = (*local)((char *)&argument, rqstp);

???if (result != NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {

????????????svcerr_systemerr (transp);

???}

???if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {

????????????fprintf (stderr, "%s", "unable to free arguments");

????????????exit (1);

???}

???return;

}

?

int

main (int argc, char **argv)

{

???register SVCXPRT *transp;

?

???pmap_unset (FILETRANSPROG, FILETRANSVERS);

?

???transp = svcudp_create(RPC_ANYSOCK);

???if (transp == NULL) {

????????????fprintf (stderr, "%s", "cannot create udp service.");

????????????exit(1);

???}

???if (!svc_register(transp, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_UDP)) {

????????????fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, udp).");

????????????exit(1);

???}

?

???transp = svctcp_create(RPC_ANYSOCK, 0, 0);

???if (transp == NULL) {

????????????fprintf (stderr, "%s", "cannot create tcp service.");

????????????exit(1);

???}

???if (!svc_register(transp, FILETRANSPROG, FILETRANSVERS, filetransprog_1, IPPROTO_TCP)) {

????????????fprintf (stderr, "%s", "unable to register (FILETRANSPROG, FILETRANSVERS, tcp).");

????????????exit(1);

???}

?

???svc_run ();

???fprintf (stderr, "%s", "svc_run returned");

???exit (1);

???/* NOTREACHED */

}

?

trans_clnt.c代碼:

/*

?* Please do not edit this file.

?* It was generated using rpcgen.

?*/

?

#include <memory.h> /* for memset */

#include "trans.h"

?

/* Default timeout can be changed using clnt_control() */

static struct timeval TIMEOUT = { 25, 0 };

?

char **

readfile_1(char **argp, CLIENT *clnt)

{

???static char *clnt_res;

?

???memset((char *)&clnt_res, 0, sizeof(clnt_res));

???if (clnt_call (clnt, READFILE,

????????????(xdrproc_t) xdr_wrapstring, (caddr_t) argp,

????????????(xdrproc_t) xdr_wrapstring, (caddr_t) &clnt_res,

????????????TIMEOUT) != RPC_SUCCESS) {

????????????return (NULL);

???}

???return (&clnt_res);

}

5.????????編寫客戶端和服務器端接口。此部分可以說是最麻煩的部分,稍不注意便會出錯,同樣可以參考Douglas的那本書,但要注意的是他的服務器接口例程代碼中的每個函數的第二個參數應該是CLIENT *clnt,而非struct svc_req * rqstp

為本程序編寫的代碼如下:

客戶端接口文件trans_cif.c

#include <rpc/rpc.h>

#include <stdio.h>

?

#include "trans.h"/* Client-side stub interface routines written by programmer */

extern CLIENT * handle;

static char **ret;

?

char * readfile(char * name)

{

???char ** arg;

???arg = &name;

???ret = readfile_1(arg, handle);

?

???return ret==NULL ? NULL : *ret;

}

服務器端接口文件trans_sif.c

#include <rpc/rpc.h>

#include <stdio.h>

#include "trans.h"

?

char * readfile(char *);

static char * retcode;

?

char ** readfile_1(char ** w, CLIENT *clnt)

{

???retcode = readfile(*(char**)w);

???return &retcode;

}

?

6.????????編譯鏈接客戶端和服務器端程序

不管是客戶端還是服務器端,都要鏈接三個文件,

客戶端:程序文件+*** _clnt.c+客戶端接口文件。

服務器端:程序文件+*** _svc.c+服務器端接口文件

同時每一段的三個文件都是互相關聯的,編譯出現錯誤時,可以根據提示查看三個文件進行debug

命令如下:

gcc -Wall -o trans_client client.c trans_clnt.c trans_cif.c

gcc -Wall -o trans_server server.c trans_svc.c trans_sif.c

?

7.????????啟動服務器端和客戶端,大功告成。要先運行服務器端程序,再運行客戶端程序。命令如下:

./trans_server

./trans_client

client啟動后,提示輸入要傳輸的文件名,輸入后,server將文件的第一行傳回,大功告成!

相關:

http://hi.baidu.com/%E2%C8%C2%F8%CD%B7%C9%E7%C7%F8/blog/item/6d50b86752957dfef63654cc.html
http://shake863.iteye.com/blog/181249
http://blog.csdn.net/beff2047/article/details/4028568
http://blog.chinaunix.net/uid-20644632-id-2220585.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444943.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444943.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444943.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法(28)--矩陣搜索系列

矩陣搜索1.leetcode-200. 島嶼數量2.leetcode-695. 島嶼的最大面積3.leetcode-463. 島嶼的周長4.劍指 Offer 12. 矩陣中的路徑5.leetcode-329. 矩陣中的最長遞增路徑6.leetcode-1091. 二進制矩陣中的最短路徑1.leetcode-200. 島嶼數量 給你一個由 ‘1’&#xff08;陸地&#…

leetcode213 打家劫舍II

你是一個專業的小偷&#xff0c;計劃偷竊沿街的房屋&#xff0c;每間房內都藏有一定的現金。這個地方所有的房屋都圍成一圈&#xff0c;這意味著第一個房屋和最后一個房屋是緊挨著的。同時&#xff0c;相鄰的房屋裝有相互連通的防盜系統&#xff0c;如果兩間相鄰的房屋在同一晚…

linux下安裝boost

以下是在ubuntu 7.10 (內核 2.6.22-14)下安裝的例子&#xff1a; 一、下載最新的 boost 庫&#xff0c;下載地址&#xff1a; http://www.boost.org/users/download/ 二、在適當的位置解壓 boost 庫&#xff0c;推薦把 boost 庫解壓到 /usr/local/ 下&#xff1a; $ cd dowlo…

PaperNotes(4)-高質量圖像生成-CGAN-StackGAN-Lapgan-Cyclegan-Pix2pixgan

cgan,stackgan,lapgan,cyclegan,pix2pixgan1.Conditional GAN1.1簡介1.2網絡結構與訓練1.3特點與用途2.Stack GAN2.1簡介2.2網絡結構與訓練2.3特點與用途3.Lap GAN3.1簡介3.2網絡結構與訓練3.3特點與用途4.Pix2pix GAN4.1 簡介4.2 網絡結構和訓練4.3 特點和用途5.Patch GAN6.Cy…

關于c++的一些案例

之前做項目的時候,有時候會用到位,也就是將一些數據放在二進制里,然后存在數據庫中或者緩存在服務器上,取出來,然后要判斷某位是不是置0或1,然后再將某位置0或1(比如領多個獎勵的 游戲邏輯),之前有點傻,竟然用 << ,>>這些運算符計算,今天翻起以前好久不…

C++(1)--概況、開發工具、hello word

簡介1. 概況2. 開發工具3. mac 寫hello word4. c 基本概念5.兩個數相加代碼分解5.1編譯預處理命令# include5.2輸入輸出庫iostream6.注釋7.編碼規范《老九學堂C課程》《C primer》學習筆記。《老九學堂C課程》詳情請到B站搜索《老九零基礎學編程C入門》1. 概況 20世紀70年代&a…

class 和 struct的區別

C中的struct對C中的struct進行了擴充&#xff0c;它已經不再只是一個包含不同數據類型的數據結構了&#xff0c;它已經獲取了太多的功能。 struct能包含成員函數嗎&#xff1f; 能&#xff01; struct能繼承嗎&#xff1f; 能&#xff01;&#xff01; struct能實現多態嗎&…

leetcode206 反轉鏈表

反轉一個單鏈表。 示例: 輸入: 1->2->3->4->5->NULL 輸出: 5->4->3->2->1->NULL 進階: 你可以迭代或遞歸地反轉鏈表。你能否用兩種方法解決這道題&#xff1f; 經典題不解釋 /*** Definition for singly-linked list.* public class ListNode…

淺議柔性數組

很多時候,柔性數組應用在了變長結構體中,如: StructPacket {Int state; Int len;

leetcode 152 乘積最大子序列

給定一個整數數組 nums &#xff0c;找出一個序列中乘積最大的連續子序列&#xff08;該序列至少包含一個數&#xff09;。 示例 1: 輸入: [2,3,-2,4] 輸出: 6 解釋: 子數組 [2,3] 有最大乘積 6。 示例 2: 輸入: [-2,0,-1] 輸出: 0 解釋: 結果不能為 2, 因為 [-2,-1] 不是子…

PaperNotes(5)-Conditional Generative Adversarial Nets

Conditional GAN 論文閱讀筆記Abstract1 Introduction2 Related Work3 Conditional Adversarial Nets3.1 Generative Adversarial Nets3.2 Conditional Adversarial Nets4 Experimental Results4.1 Unimodal4.2 Multimodal5 Future Work6.思考文章地址&#xff1a;https://arxi…

蛙泳姿勢教學

偶爾看到分享的一篇日志&#xff0c;記錄下&#xff0c;忙過這段時間努力學蛙泳。 蛙泳配合有一個順口溜&#xff0c;在講解蛙泳動作要領之前先介紹給大家&#xff1a;“劃手腿不動&#xff0c;收手再收腿&#xff0c;先伸胳膊后蹬腿&#xff0c;并攏伸直漂一會兒。”從順口溜中…

leetcode238 除本身以外數組的乘積

給定長度為 n 的整數數組 nums&#xff0c;其中 n > 1&#xff0c;返回輸出數組 output &#xff0c;其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘積。 示例: 輸入: [1,2,3,4] 輸出: [24,12,8,6] 說明: 請不要使用除法&#xff0c;且在 O(n) 時間復雜度內完…

C 和c++的一些雜想,想到哪兒寫到哪兒

關于C和c++一直有好多的程序猿在研究,研究區別研究相似的地方,究竟用那個預言好,沒有確定的說法,要看你做什么了。 初始化操作: 在初始化的時候,我們都知道C語言一般都是這樣處理的: int a=12; C++ 呢,除了這樣復制初始化之外還可以直接初始化: int a(12); 啊…

C++(2)--mac使用VScode 進行C++編譯、運行、調試

mac 使用VScode 進行C開發1.編譯的基礎概念2. mac 編譯c代碼2.1 查看編譯器情況2.2 安裝插件C/C&#xff0c;C/C Clang Command Adapte2.3新建一個C project2.3.1本地新建文件夾2.3.2新建mian.cpp文件2.3.3 編寫hello word demo2.4 代碼編譯&#xff0c;運行&#xff0c;調試2.…

boost庫linux編譯安裝

0.下載 1.解壓boost_1_49_0.tar.g然后放到/opt/ 2. 進入解壓后的文件夾 cd /opt/boost_1_49_0 3.將boost安裝配置在/boost/prefix目錄下 不過之前先 mkdir -p /boost/prefix

leetcode136 只出現一次的數字

給定一個非空整數數組&#xff0c;除了某個元素只出現一次以外&#xff0c;其余每個元素均出現兩次。找出那個只出現了一次的元素。 說明&#xff1a; 你的算法應該具有線性時間復雜度。 你可以不使用額外空間來實現嗎&#xff1f; 示例 1: 輸入: [2,2,1] 輸出: 1 示例 2: …

C++(3)--編譯、gdb調試

3--編譯和執行過程1.編譯2.gdb調試gdb 查coreGCC是一個編譯套件&#xff0c;是一個以"gcc"命令為首的源碼施工隊。施工隊的成員有gcc、cpp、as、ld四個成員 預處理–宏定義展開&#xff0c;頭文件引入-- cpp 等價于 gcc -E編譯–C語言->匯編語言–gcc -S匯編–匯…

leetcode94 二叉樹的中序遍歷

給定一個二叉樹&#xff0c;返回它的中序 遍歷。 示例: 輸入: [1,null,2,3] 1 \ 2 / 3 輸出: [1,3,2] 進階: 遞歸算法很簡單&#xff0c;你可以通過迭代算法完成嗎&#xff1f; 遞歸 /*** Definition for a binary tree node.* public class TreeNode …

使用動態鏈接庫

1. 動態鏈接庫是程序運行時加載的庫,當動態鏈接庫正確安裝后,所有的程序都可以使用動態庫來運行程序。動態鏈接庫是目標文件的集合,目標文件在動態鏈接庫中的組織方式是按照特殊方式形成的。庫中函數和變量的地址是相對地址,不是絕對地址,其真實地址在調用動態庫的程序加載…