簡單程序語言理論與編譯技術·22 實現一個從AST到RISCV的編譯器

本文是記錄專業課“程序語言理論與編譯技術”的部分筆記。

LECTURE 22(實現一個從AST到RISCV的編譯器)

一、問題分析

1、完整的編譯器(如LLVM)需先完成AST到IR的轉換,并進行代碼優化,再到匯編,如下圖:

本次實驗實現從AST到RISC-V(不考慮優化)的翻譯。對于匯編,我們可以訪問Compiler Explorer (godbolt.org)來熟悉一下,或者參看類似下圖的手冊(包括匯編指令和寄存器類型等內容):

對于本次實驗,我們用 x8 作為幀指針(fp) 來固定指向當前函數的棧楨基址,使用 a0 寄存器作為表達式計算結果的默認存放寄存器(函數返回值也約定放在 a0)。為了方便,我們避免寄存器分配,轉而使用棧來保存局部變量和臨時數據。如下圖:

2、首先考慮Binop的翻譯,我們參考下圖:

對于Int 常數類型,我們直接將常數值加載到寄存器,對應RISC-V 提供的?li(load immediate)偽指令,將一個立即數載入寄存器。而對于Binop,我們考慮處理三種情況:
? Num Binop Num E.g. 2 + 7
? Num Binop Num Binop Num E.g. 2 + 6 * 9
? (Num Binop Num) Binop (Num Binop Num) E.g. (2 + 6) * (8 + 7)

基本流程:計算左操作數 -> 保存左值 -> 計算右操作數 -> 運算合成結果。如此之后,我們可以使用兩種方案來保存:
? 將左值保存在臨時寄存器中(例如t0 ),再計算右值到另一個寄存器(例如t1 )
? 采用棧,在計算左值后將其壓棧保存,計算右值后再彈棧取出左值進行運算

3、然后考慮Let的翻譯:

這里我們需要增加對變量和作用域的支持,使編譯器可以處理let綁定和變量引用。我們需要考慮棧幀與變量地址,當進入一個新的let塊,需在棧上分配新的空間保存局部變量的值(調整棧指針fp的offset)。然后在編譯階段也需要一個映射(符號表)跟蹤當前作用域中每個變量名的棧偏移或寄存器位置(env)。

據此,我們簡化邏輯,使fp固定不動,變量x偏移可以按照進入let的次序決定。而固定幀具體而言,使在程序一開始一次性分配最大所需棧,然后fp = sp固定。

我們可以考慮一個流程:先編譯e1得到其值在a0,然后在棧上分配空間保存其值 (addi sp, sp, -8)。接著將變量名x映射到新空間的位置,例如offset += 8表示又用8字節,映射env新增 (“x”, of fset)。隨后編譯內部表達式e2,Var x按env找到偏移,完成后回退棧指針釋放x所占的空間 (addi sp, sp, 8 )。最后恢復符號表,彈出env中x的綁定。

4、然后考慮If的翻譯,我們參考下圖:

我們需要擴展編譯器支持Bool常量和If條件表達式,正確處理程序的控制流,為每個if表達式生成唯一的標簽并正確安插跳轉指令,且保證程序無論哪一種路徑都會結束。

具體的,思考同前面let/變量的交互:使用標簽而非固定地址。條件的各分支內部可以定義自己的變量且作用域僅限于分支內,實現需以遞歸的方式編譯分支表達式并各自管理環境。在分支中寄存器值通過a0返回結果,無殘留的臨時變量,無需專門清理分支的棧。

5、最后考慮Func和App的翻譯。擴展編譯器支持函數定義(Func)和函數調用(App),函數的閉包包含函數代碼和綁定環境的組合體。而編譯Func(x, body)會產生兩部分輸出:全局函數代碼(儲存待后續統一輸出),以及當前表達式計算時創建閉包的代碼。

回顧Riscv寄存器,caller負責傳遞參數(a0-a7寄存器)和保存臨時寄存器;callee可以自由使用臨時寄存器但需保存和恢復caller保存寄存器(如s0等)。我們在實現時僅考慮函數調用一個顯式參數,并需要注意傳遞參數值和環境指針。

閉包的環境結構(僅提供一種思路):分配一塊內存,大小足夠存放一個指針加上所有自由變量的值;將函數的代碼地址寫入這塊內存的開頭;將該函數的自由變量當前環境中的值寫入內存的后續字段;將指向這塊內存的指針作為閉包值放入某寄存器。

注意,Func需要一個唯一的新函數標簽(類似if的標簽)。對函數體進行的編譯,與main形式上相同(env -> local_env + closure_env)。在main函數體換Func編譯結果為上一步的閉包分配代碼。

而App調用有多種方式:jalr、call等。以jalr為例:將a0(閉包指針)保存到t0,a0放參數;使用ld t1 , 0(t 0)加載閉包偏移為0處內容(代碼地址)到t1;jalr ra, t 1進行跳轉調用。

其余包括free、優化等問題不在本節內容之內。

二、前置代碼

1、lib/ast.ml

type binop = | Add| Sub| Mul| Div| Leqtype expr =| Int of int| Var of string| Bool of bool| Binop of binop * expr * expr| Let of string * expr * expr| If of expr * expr * expr| Func of string * expr| App of expr * expr

2、lib/lexer.mll

{open Parser
}rule read = parse | [' ' '\t' '\n'] { read lexbuf }| '+' { PLUS }| '-' { MINUS }| '*' { TIMES }| '/' { DIV }| '(' { LPAREN }| ')' { RPAREN }| "<=" { LEQ }| "true" { TRUE }| "false" { FALSE }| "let" { LET }| "=" { EQUALS }| "in" { IN }| "if" { IF }| "then" { THEN }| "else" { ELSE }| "->" { ARROW }| "fun" { FUNC }| ['0'-'9']+ as num { INT (int_of_string num) }| ['a'-'z' 'A'-'Z']+ as id { ID id }| eof { EOF }| _ { failwith "Invalid character" }

3、lib/parser.mly

%{open Ast(** [make_apply e [e1; e2; ...]] makes the application  [e e1 e2 ...]).  Requires: the list argument is non-empty. *)
let rec make_apply e = function| [] -> failwith "precondition violated"| [e'] -> App (e, e')| h :: ((_ :: _) as t) -> make_apply (App (e, h)) t
%}%token <int> INT
%token <string> ID
%token PLUS MINUS TIMES DIV EOF
%token LPAREN RPAREN
%token LEQ
%token TRUE FALSE
%token LET EQUALS IN
%token IF THEN ELSE 
%token FUNC ARROW%nonassoc IN
%nonassoc ELSE
%left LEQ
%left PLUS MINUS
%left TIMES DIV%start main
%type <Ast.expr> main
%%main:expr EOF { $1 }
;expr:| simpl_expr { $1 }| simpl_expr simpl_expr+ { make_apply $1 $2 }
;simpl_expr:| INT { Int $1 }| ID { Var $1 }| TRUE { Bool true }| FALSE { Bool false}| simpl_expr LEQ simpl_expr   { Binop (Leq, $1, $3) }| simpl_expr TIMES simpl_expr { Binop (Mul, $1, $3) }| simpl_expr DIV simpl_expr   { Binop (Div, $1, $3) }| simpl_expr PLUS simpl_expr  { Binop (Add, $1, $3) }| simpl_expr MINUS simpl_expr { Binop (Sub, $1, $3) }| LET ID EQUALS simpl_expr IN simpl_expr  { Let ($2, $4, $6) }| FUNC ID ARROW expr { Func ($2, $4) }| IF simpl_expr THEN simpl_expr ELSE simpl_expr { If ($2, $4, $6) }| LPAREN expr RPAREN { $2 }
;

4、lib/dune

(library(name Simpl_riscv)(modules parser lexer ast))(ocamllex lexer)
(menhir (modules parser))

5、bin/dune

(executable(public_name Simpl_riscv)(name main)(modules main)(libraries Simpl_riscv)(flags (:standard -w -32-27-26-39-8-37)))

6、bin/main.ml的部分代碼

open Simpl_riscv
open Astlet rec string_of_expr (e : expr) : string = match e with| Int n -> Printf.sprintf "Int %d" n| Var id -> Printf.sprintf "Var %s" id| Bool b -> let b_str = match b with | true -> "true"| false -> "false"inPrintf.sprintf "Bool %s" b_str| Binop (binop, e1, e2) ->let binop_str = match binop with | Add -> "Add"| Mul -> "Mul"| Sub -> "Sub"| Div -> "Div"| Leq -> "Leq"inPrintf.sprintf "Binop (%s, %s, %s)" binop_str (string_of_expr e1) (string_of_expr e2)| Let (var, e1, e2) -> Printf.sprintf "Let (%s, %s, %s)" var (string_of_expr e1) (string_of_expr e2)| If (e1, e2, e3) -> Printf.sprintf "If (%s, %s, %s)" (string_of_expr e1) (string_of_expr e2) (string_of_expr e3)| Func (var, e) -> Printf.sprintf "Func (%s, %s)" var (string_of_expr e)| App (e1, e2) -> Printf.sprintf "App (%s, %s)" (string_of_expr e1) (string_of_expr e2)let parse s : expr =let lexbuf = Lexing.from_string s inlet ast = Parser.main Lexer.read lexbuf inast(* 全局標簽計數器,用于生成唯一標簽 *)
let label_count = ref 0
let fresh_label prefix = incr label_count;Printf.sprintf "%s_%d" prefix !label_count(* 全局列表:保存所有生成的函數代碼,最終附加在程序末尾 *)
let functions : string list ref = ref [](* 簡單的自由變量分析(不去重,僅適用于教學示例) *)
let rec free_vars expr bound = match expr with| Int _ | Bool _ -> []| Var x -> if List.mem x bound then [] else [x]| Binop (_, e1, e2) -> free_vars e1 bound @ free_vars e2 bound| Let (x, e1, e2) -> free_vars e1 bound @ free_vars e2 (x :: bound)| If (cond, e_then, e_else) ->free_vars cond bound @ free_vars e_then bound @ free_vars e_else bound| Func (x, body) -> free_vars body (x :: bound)| App (e1, e2) -> free_vars e1 bound @ free_vars e2 bound(*compile_expr env cur_offset exprenv: (variable, offset) 的關聯列表,其中 offset 是相對于 fp 的偏移(單位:字節)cur_offset: 當前已經分配的 let 變量字節數(每個變量占 8 字節)返回的匯編代碼保證計算結果存放在寄存器 a0 中
*)
let rec compiler_expr (env : (string * int) list) (cur_offset : int) (expr : expr) : string = match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->(trylet offset = List.assoc x env inPrintf.sprintf "\tld a0, -%d(fp)\n" offsetwith Not_found ->failwith ("Unbound variable: " ^ x))
(*——————————————*)and compile_expr_func (local_env : (string * int) list) (closure_env : (string * int) list) (cur_offset : int) (expr : expr) : string =match expr with
(*——————————————*)let compiler_program (e : expr) : string =let body_code = compiler_expr [] 0 e inlet prologue = ".text\n\.global main\n\main:\n\\taddi sp, sp, -64\n\\tmv fp, sp\n"inlet epilogue = "\\tmv sp, fp\n\\taddi sp, sp, 64\n\\tret\n"inlet func_code = String.concat "\n" !functions inprologue ^ body_code ^ epilogue ^ "\n" ^ func_codelet () =let filename = "test/simpl_test4.in" in(* let filename = "test/simpl_test2.in" in *)let in_channel = open_in filename inlet file_content = really_input_string in_channel (in_channel_length in_channel) inclose_in in_channel;(* let res = interp file_content inPrintf.printf "Result of interpreting %s:\n%s\n\n" filename res;let res = interp_big file_content inPrintf.printf "Result of interpreting %s with big-step model:\n%s\n\n" filename res; *)let ast = parse file_content in Printf.printf "AST: %s\n" (string_of_expr ast);let output_file = Sys.argv.(1) inlet oc = open_out output_file inlet asm_code = compiler_program ast inoutput_string oc asm_code;close_out oc;Printf.printf "Generated RISC-V code saved to: %s\n" output_file

三、具體實現

1、bin/main.ml的compiler_expr函數

let rec compiler_expr (env : (string * int) list) (cur_offset : int) (expr : expr) : string = match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->(trylet offset = List.assoc x env inPrintf.sprintf "\tld a0, -%d(fp)\n" offsetwith Not_found ->failwith ("Unbound variable: " ^ x))| Binop (op, e1, e2) ->let code1 = compiler_expr env cur_offset e1 in let push_left = "\taddi sp, sp, -8\n\tsd a0, 0(sp)\n" inlet code2 = compiler_expr env cur_offset e2 inlet pop_left = "\tld t0, 0(sp)\n\taddi sp, sp, 8\n" inlet op_code = match op with| Add -> "\tadd a0, t0, a0\n"| Sub -> "\tsub a0, t0, a0\n"| Mul -> "\tmul a0, t0, a0\n"| Div -> "\tdiv a0, t0, a0\n"| Leq -> "Not implemented"incode1 ^ push_left ^ code2 ^ pop_left ^ op_code| Let (x, e1, e2) ->let code1 = compiler_expr env cur_offset e1 inlet new_offset = cur_offset + 8 inlet alloc = Printf.sprintf "\taddi sp, sp, -8\n\tsd a0, -%d(fp)\n" new_offset inlet env' = (x, new_offset) :: env inlet code2 = compiler_expr env' new_offset e2 inlet free = "\taddi sp, sp, 8\n" incode1 ^ alloc ^ code2 ^ free| If (cond, e_then, e_else) ->let label_else = fresh_label "Lelse" inlet label_end = fresh_label "Lend" inlet code_cond = compiler_expr env cur_offset cond inlet code_then = compiler_expr env cur_offset e_then inlet code_else = compiler_expr env cur_offset e_else incode_cond ^ Printf.sprintf "\tbeq a0, x0, %s\n" label_else ^code_then ^Printf.sprintf "\tj %s\n" label_end ^Printf.sprintf "%s:\n" label_else ^code_else ^Printf.sprintf "%s:\n" label_end| Func (x, body) ->let fvs = free_vars body [x] inlet num_free = List.length fvs inlet func_id = fresh_label "func" inlet local_env = [(x, 8)] inlet closure_env = List.mapi (fun i v -> (v, 8 * i)) fvs inlet func_body_code = compile_expr_func local_env closure_env 0 body inlet func_prologue = Printf.sprintf "%s:\n\taddi sp, sp, -16\n\tsd ra, 8(sp)\n\tsd fp, 0(sp)\n\tmv fp, sp\n" func_idinlet func_epilogue = "\tld ra, 8(sp)\n\tld fp, 0(sp)\n\taddi sp, sp, 16\n\tret\n"inlet func_code = func_prologue ^ func_body_code ^ func_epilogue infunctions := !functions @ [func_code];let closure_size = 8 * (1 + num_free) inlet alloc_code = Printf.sprintf "\tli a0, %d\n\tjal ra, malloc\n" closure_size inlet move_closure = "\tmv t0, a0\n" inlet store_code_ptr = Printf.sprintf "\tla t1, %s\n\tsd t1, 0(t0)\n" func_id inlet store_free_vars = List.mapi (fun i v ->let outer_offset =try List.assoc v env with Not_found -> failwith ("Unbound free var: " ^ v)inPrintf.sprintf "\tld t1, -%d(fp)\n\tsd t1, %d(t0)\n" outer_offset (8 * (i + 1))) fvs |> String.concat ""inlet ret_code = "\tmv a0, t0\n" inalloc_code ^ move_closure ^ store_code_ptr ^ store_free_vars ^ ret_code| App (e1, e2) ->let code_f = compiler_expr env cur_offset e1 inlet save_closure = "\tmv t0, a0\n" inlet code_arg = compiler_expr env cur_offset e2 inlet load_env = "\taddi a1, t0, 8\n" inlet load_code_ptr = "\tld t1, 0(t0)\n" inlet call = "\tjalr ra, 0(t1)\n" incode_f ^ save_closure ^ code_arg ^ load_env ^ load_code_ptr ^ call

2、bin/main.ml的compile_expr_func函數

and compile_expr_func (local_env : (string * int) list) (closure_env : (string * int) list) (cur_offset : int) (expr : expr) : string =match expr with| Int n -> Printf.sprintf "\tli a0, %d\n" n| Bool b ->if b then "\tli a0, 1\n" else "\tli a0, 0\n"| Var x ->if List.mem_assoc x local_env thenPrintf.sprintf "\tld a0, -%d(fp)\n" (List.assoc x local_env)else if List.mem_assoc x closure_env thenPrintf.sprintf "\tld a0, %d(a1)\n" (List.assoc x closure_env)elsefailwith ("Unbound variable in function: " ^ x)| Binop (op, e1, e2) ->let code1 = compile_expr_func local_env closure_env cur_offset e1 inlet push_left = "\taddi sp, sp, -8\n\tsd a0, 0(sp)\n" inlet code2 = compile_expr_func local_env closure_env cur_offset e2 inlet pop_left = "\tld t0, 0, 0(sp)\n\taddi sp, sp, 8\n" inlet op_code = match op with| Add -> "\tadd a0, t0, a0\n"| Sub -> "\tsub a0, t0, a0\n"| Mul -> "\tmul a0, t0, a0\n"| Div -> "\tdiv a0, t0, a0\n"| Leq -> "Not implemented"incode1 ^ push_left ^ code2 ^ pop_left ^ op_code| If _ -> failwith "Not implemented"| Func _ | App _ -> failwith "Nested functions not supported in function bodies" 

?

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

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

相關文章

JavaWeb 課堂筆記 —— 02 JavaScript

本系列為筆者學習JavaWeb的課堂筆記&#xff0c;視頻資源為B站黑馬程序員出品的《黑馬程序員JavaWeb開發教程&#xff0c;實現javaweb企業開發全流程&#xff08;涵蓋SpringMyBatisSpringMVCSpringBoot等&#xff09;》&#xff0c;章節分布參考視頻教程&#xff0c;為同樣學習…

Python 如何高效實現 PDF 內容差異對比

Python 如何高效實現 PDF 內容差異對比 1. 安裝 PyMuPDF 庫2. 獲取 PDF 內容通過文件路徑獲取通過 URL 獲取 3. 提取 PDF 每頁信息4. 內容對比metadata 差異文本對比可視化對比 5. 提升對比效率通過哈希值快速判斷頁面是否相同早停機制多進程機制 6. 其他 最近有接觸到 PDF 內容…

OpenGL學習筆記(簡介、三角形、著色器、紋理、坐標系統、攝像機)

目錄 簡介核心模式與立即渲染模式狀態機對象GLFW和GLAD Hello OpenGLTriangle 三角形頂點緩沖對象 VBO頂點數組對象 VAO元素緩沖對象 EBO/ 索引緩沖對象 IEO 著色器GLSL數據類型輸入輸出Uniform 紋理紋理過濾Mipmap 多級漸遠紋理實際使用方式紋理單元 坐標系統裁剪空間 攝像機自…

MIPI與DVP接口攝像頭:深度解析與應用指南

1、MIPI 1.1 MIPI簡介 MIPI是什么&#xff1f;MIPI&#xff1a;mobile industry processor interface移動行業處理器接口。它是一個由Intel、Motorola、Nokia、NXP、Samsung、ST&#xff08;意法半導體&#xff09;和TI&#xff08;德州儀器&#xff09;等公司發起的開放標準…

35信號和槽_信號槽小結

Qt 信號槽 1.信號槽是啥~~ 尤其是和 Linux 中的信號進行了對比&#xff08;三要素&#xff09; 1) 信號源 2) 信號的類型 3)信號的處理方式 2.信號槽 使用 connect 3.如何查閱文檔. 一個控件&#xff0c;內置了哪些信號&#xff0c;信號都是何時觸發 一…

6547網:藍橋STEMA考試 Scratch 試卷(2025年3月)

『STEMA考試是藍橋青少教育理念的一部分&#xff0c;旨在培養學生的知識廣度和獨立思考能力。考試內容主要考察學生的未來STEM素養、計算思維能力和創意編程實踐能力。』 一、選擇題 第一題 運行下列哪個程序后&#xff0c;飛機會向左移動&#xff1f; ( ) A. …

使用 Python 爬取并打印雙色球近期 5 場開獎數據

使用 Python 爬取并打印雙色球近期 5 場開獎數據 前期準備安裝所需庫 完整代碼代碼解析 1. 導入必要的庫2. 定義函數 get_recent_five_ssq 3. 設置請求的 URL 和 Headers 4. 發送請求并處理響應5. 解析 HTML 內容6. 提取并打印數據7. 錯誤處理 首先看下運行的效果圖&#xff1a…

前端快速入門學習3——CSS介紹與選擇器

1.概述 CSS全名是cascading style sheets,中文名層疊樣式表。 用于定義網頁樣式和布局的樣式表語言。 通過 CSS&#xff0c;你可以指定頁面中各個元素的顏色、字體、大小、間距、邊框、背景等樣式&#xff0c;從而實現更精確的頁面設計。 HTML與CSS的關系&#xff1a;HTML相當…

JVM 內存區域詳解

JVM 內存區域詳解 Java 虛擬機&#xff08;JVM&#xff09;的內存區域劃分為多個部分&#xff0c;每個部分有特定的用途和管理機制。以下是 JVM 內存區域的核心組成及其功能&#xff1a; 一、運行時數據區&#xff08;Runtime Data Areas&#xff09; 1. 線程共享區域 內存…

基于SpringBoot的水產養殖系統【附源碼】

基于SpringBoot的水產養殖系統&#xff08;源碼L文說明文檔&#xff09; 目錄 4 系統設計 4.1 總體功能 4.2 系統模塊設計 4.3 數據庫設計 4.3.1 數據庫設計 4.3.2 數據庫E-R 圖 4.3.3 數據庫表設計 5 系統實現 5.1 管理員功能模塊的實…

從零構建大語言模型全棧開發指南:第五部分:行業應用與前沿探索-5.2.2超級對齊與AGI路徑探討

?? 點擊關注不迷路 ?? 點擊關注不迷路 ?? 點擊關注不迷路 文章大綱 大語言模型全棧開發指南:倫理與未來趨勢 - 第五部分:行業應用與前沿探索5.2.2 超級對齊與AGI路徑探討超級對齊:定義與核心挑戰1. 技術挑戰2. 倫理挑戰AGI發展路徑:從專用到通用智能階段1:`專用智能…

基于大模型的重癥肌無力的全周期手術管理技術方案

目錄 技術方案文檔1. 數據預處理模塊2. 多任務預測模型架構3. 動態風險預測引擎4. 手術方案優化系統5. 技術驗證模塊6. 系統集成架構7. 核心算法清單8. 關鍵流程圖詳述實施路線圖技術方案文檔 1. 數據預處理模塊 流程圖 [輸入原始數據] → [聯邦學習節點數據對齊] → [多模態特…

盲盒小程序開發平臺搭建:打造個性化、高互動性的娛樂消費新體驗

在數字化浪潮席卷消費市場的今天&#xff0c;盲盒小程序以其獨特的趣味性和互動性&#xff0c;迅速成為了年輕人追捧的娛樂消費新寵。盲盒小程序不僅為用戶帶來了拆盒的驚喜和刺激&#xff0c;更為商家提供了創新的營銷手段。為了滿足市場對盲盒小程序日益增長的需求&#xff0…

前端對接下載文件接口、對接dart app

嵌套在dart app里面的前端項目 1.前端調下載接口 ->后端返回 application/pdf格式的文件 ->前端將pdf處理為blob ->blob轉base64 ->調用dart app的 sdk saveFile ->保存成功 async download() {try {// 調用封裝的 downloadEContract 方法獲取 Blob 數據const …

Spring常見問題復習

############Spring############# Bean的生命周期是什么&#xff1f; BeanFactory和FactoryBean的區別&#xff1f; ApplicationContext和BeanFactory的區別&#xff1f; BeanFactoryAware注解&#xff0c;還有什么其它的Aware注解 BeanFactoryAware方法和Bean注解的方法執行順…

C++_類和對象(下)

【本節目標】 再談構造函數Static成員友元內部類匿名對象拷貝對象時的一些編譯器優化再次理解封裝 1. 再談構造函數 1.1 構造函數體賦值 在創建對象時&#xff0c;編譯器通過調用構造函數&#xff0c;給對象中各個成員變量一個合適的初始值。 class Date { public:Date(in…

連續數據離散化與逆離散化策略

數學語言描述&#xff1a; 在區間[a,b]中有一組符合某分布的數據&#xff1a; 1.求相同區間中另一組符合同樣分布的數據與這組數據的均方誤差 2.求區間中點與數據的均方誤差 3.求在區間中均勻分布的一組數據與這組數據的均方誤差 一&#xff1a;同分布數據隨機映射 假設在…

Redash:一個開源的數據查詢與可視化工具

Redash 是一款免費開源的數據可視化與協作工具&#xff0c;可以幫助用戶快速連接數據源、編寫查詢、生成圖表并構建交互式儀表盤。它簡化了數據探索和共享的過程&#xff0c;尤其適合需要團隊協作的數據分析場景。 數據源 Redash 支持各種 SQL、NoSQL、大數據和 API 數據源&am…

FreeRTOS的空閑任務

在 FreeRTOS 中&#xff0c;空閑任務&#xff08;Idle Task&#xff09; 是操作系統自動創建的一個特殊任務&#xff0c;其作用和管理方式如下&#xff1a; 1. 空閑任務創建 FreeRTOS 內核自動創建&#xff1a;當調用 vTaskStartScheduler() 啟動調度器時&#xff0c;內核會自…

Java進階之旅-day05:網絡編程

引言 在當今數字化的時代&#xff0c;網絡編程在軟件開發中扮演著至關重要的角色。Java 作為一門廣泛應用的編程語言&#xff0c;提供了強大的網絡編程能力。今天&#xff0c;我們深入學習了 Java 網絡編程的基礎知識&#xff0c;包括基本的通信架構、網絡編程三要素、IP 地址、…