Linux | 編譯原理、gcc的命令參數、自動化構建工具 make/Makefile

文章目錄

  • 編譯原理
    • 預處理
    • 編譯
    • 匯編
    • 鏈接
    • gcc的常用命令參數
  • make 和 Makefile 的概念
    • make的運行
    • 通配符
    • 自動化變量
    • 偽目標.PHONE:【命令】


編譯原理

在解釋 makefile 前,首先解釋一下 .c 文件變成 .exe 文件要經過的四個步驟——預處理、編譯、匯編和鏈接(參考來源):
在這里插入圖片描述
windows 系統下最后生成的可執行文件為 .exe ,但 Linux 系統下為 .out 。此處的可執行文件僅針對一般 .c/.cpp 代碼而言。


預處理

預處理分為四步:

  • 展開所有的宏定義 #define
  • 處理含有 # 部分的代碼。如:
    1. 條件編譯 “#if”、“#ifdef”、“#elif”、“#else”、“#endif”
    2. 預編譯指令 #include ,將被包含的頭文件插入到該編譯指令的位置。(這個過程是遞歸進行的,因為被包含的文件可能還包含了其他文件)
  • 刪除所有的注釋 “//”“/* */”
  • 添加行號和文件名標識,方便后續編譯時 編譯器產生調試用的行號 以及 在產生編譯錯誤或警告時能夠顯示行號。
  • 保留所有的 #pragma 編譯指令,因為編譯器需要使用它們。

編譯

編譯過程是整個程序構建的核心部分,編譯成功,會將源代碼由 文本形式轉換成機器語言 ,編譯過程就是把預處理完的文件進行一系列 詞法分析、語法分析、語義分析以及優化后生成相應的匯編代碼文件(.s)。

  • 詞法分析: 使用一種叫做 lex 的程序實現詞法掃描,它會按照用戶之前描述好的詞法規則將輸入的字符串分割成一個個記號。產生的記號一般分為:關鍵字、標識符、字面量(包含數字、字符串等)和特殊符號(運算符、等號等),然后他們放到對應的表中。

  • 語法分析: 語法分析器根據用戶給定的語法規則,將詞法分析產生的記號序列進行解析,然后將它們構成一棵語法樹。對于不同的語言,只是其語法規則不一樣。用于語法分析也有一個現成的工具,叫做:yacc。

  • 語義分析: 語法分析完成了對表達式語法層面的分析,但是它不了解這個語句是否真正有意義。有的語句在語法上是合法的,但是卻是沒有實際的意義,比如說兩個指針的做乘法運算,這個時候就需要進行語義分析,但是編譯器能分析的語義也只有靜態語義。

    1. 靜態語義:在編譯期就可以確定的語義。 通常包括聲明與類型的匹配、類型的轉換。比如當一個浮點型的表達式賦值給一個整型的表達式時,其中隱含一個從浮點型到整型的轉換,而語義分析就需要完成這個轉換,而將一個浮點型的表達式賦值給一個指針,這肯定是不行的,語義分析的時候就會發現兩者類型不匹配,編譯器就會報錯。
    2. 動態語義:只有在運行期才能確定的語義。 比如說兩個整數做除法,語法上沒問題,類型也匹配,聽著好像沒毛病,但是,如果除數是0的話,這就有問題了,而這個問題事先是不知道的,只有在運行的時候才能發現他是有問題的,這就是動態語義。
  • 中間代碼生成: 初始代碼是可以進行優化的,對于一些在編譯期間就能確定的值,可以直接直接進行處理,比如說 2+6,在編譯期間就可以確定他的值為8了,但是直接在語法上進行優化的話比較困難,這時優化器會先將語法樹轉成中間代碼。中間代碼一般與目標機器和運行環境無關。(不包含數據的尺寸、變量地址和寄存器的名字等)。中間代碼在不同的編譯器中有著不同的形式,比較常見的有三地址碼和P-代碼。
    中間代碼使得編譯器可以分為前端和后端。編譯器前端負責產生于機器無關的中間代碼,編譯器后端將中間代碼換成機器代碼。

  • 目標代碼生成與優化: 代碼生成器將中間代碼轉成機器代碼,這個過程是依賴于目標機器的,因為不同的機器有著不同的字長、寄存器、數據類型等。
    最后目標代碼優化器對目標代碼進行優化,比如選擇合適的尋址方式、使用唯一來代替乘除法、刪除出多余的指令等。


匯編

匯編過程調用 匯編器 as 來完成,將匯編代碼轉換成機器可以執行的指令,每一個匯編語句幾乎都對應一條機器指令。

使用命令 as hello.s -o hello.o 或者使用 gcc -c hello.s -o hello.o 來執行匯編,對應生成的文件是 .o 文件。


鏈接

鏈接的主要內容就是將各個模塊之間相互引用的部分正確的銜接起來。它的工作就是把一些指令對其他符號地址的引用加以修正。

鏈接過程主要包括了地址和空間分配、符號決議和重定向:

  • 符號決議: 有時候也被叫做符號綁定、名稱綁定、名稱決議、或者地址綁定,其實就是指用符號來去標識一個地址。

              比如說 int a = 6;這樣一句代碼,用a來標識一個塊4個字節大小的空間,空間里邊存放的內容就是4.
    
  • 重定位: 重新計算各個目標的地址過程叫做重定位。

鏈接有兩種模式:

  • 靜態鏈接: 程序運行前,將每個模塊的源代碼文件編譯成目標文件(Linux:.o Windows:.obj),然后將 目標文件 和 庫 一起鏈接形成最后的可執行文件。

    庫其實就是一組目標文件的包,就是一些最常用的代碼變異成目標文件后打包存放。最常見的庫就是運行時庫,它是支持程序運行的基本函數的集合。

  • 動態鏈接: 程序運行期間,系統調用動態鏈接器(ld-linux.so)自動鏈接的過程。

舉例描述:

  • 靜態鏈接: 如果鏈接到可執行文件中的是 靜態連接庫 libmyprintf.a ,那么 虛擬內存代碼段中的 .rodata 節區 在鏈接后需要被重定位到一個絕對的虛擬內存地址,以便程序運行時能夠正確訪問該節區中的字符串信息。
  • 動態鏈接: 而對于puts,因為它是動態連接庫 libc.so 中定義的函數,所以會在程序運行時通過 動態符號鏈接 找出 puts 函數內存 中的地址,以便程序調用該函數。

gcc的常用命令參數

上面提到的四個步驟可以由 編程語言譯器 gcc 來完成,gcc軟件 通過 gcc這條命令 來實現各種功能,下面來看一下 gcc命令 的常用選項:

  1. 無選項: 編譯鏈接
gcc test.c // 會默認生成a.out可執行程序
  1. -o :對生成的目標進行重命名,gcc 編譯出來的默認文件名是 a.out
gcc test.c -o test  // 會生成名字是test可執行文件而不是默認的a.out
  1. -E :進行預處理,不生成文件, 需要通過 -o 把它重定向到一個輸出文件里面。
gcc -E test.c -o test.i //會生成test.i文件
  1. -C :在預處理的時候不刪除注釋信息,一般和 -E 使用。
  2. -S :進行預處理、編譯,生成 .s 文件
gcc -S test.c //會生成test.s文件
  1. -c :進行預處理、編譯、和匯編,生成二進制(機器指令).o 文件。
gcc -c test.c //會生成test.o文件
  1. -O :使用編譯優化級別1編譯程序。級別為0~3(0即無優化),級別越大優化效果越好,但編譯時間越長。
gcc -O1 test.c -o test
  1. -g :在編譯的時候加入 debug 調試信息,用于 gdb 調試
  2. -pipe :使用管道代替編譯中的臨時文件。
gcc -pipe -o test test.c
  1. -include file :包含某個代碼。相當于在文件中加入 #include<file>
gcc test.c -include /root/file.h
  1. -Idir :當你使用 #include”file” 的時候:

    如果使用 -I 指定了目錄,gcc/g++ 會先在指定的目錄查找;否則,在當前目錄查找指定的頭文件。

    如果沒有找到,回到默認的頭文件目錄查找。

  2. -idirafter dir :在 -I 的目錄里面查找失敗,則到這個目錄里面查找。

  3. -llibrary :定制編譯的時候使用的庫。

gcc -lpthread test.c // 在編譯的時候要依賴pthread這個庫
  1. -Ldir :指定編譯的時候搜索庫的路徑。如果有自己的庫,可以用它來定制搜索目錄,否則編譯器只在標準庫目錄里面找。dir 是目錄的名字。

  2. -M :生成文件關聯信息。包含目標文件所依賴的所有源代碼。

gcc -M hello.c
  1. -MM :和 -M 一樣,只不過忽略由 #include 所造成的依賴關系。
  2. -MD :和 -M 相同,只不過將輸出導入到 .d 文件里面。
  3. -MMD :和 -MM 相同,將輸出導入到 .d 文件里面。
  4. -static :鏈接時使用靜態鏈接,但是要保證系統中有靜態庫。編譯出來的東西,一般都很大。
  5. -share :此選項盡量的使用動態庫,所以生成文件比較小,但是必須是系統有動態庫。
  6. -shared :生成共享目標文件,通常用在建立共享庫。
gcc -shared test.c -o libtest.so // 編譯動態庫
  1. -w :不生成任何警告信息。
  2. -Wall :生成所有警告信息。

make 和 Makefile 的概念

推薦一個非常全的關于 Makefile 的文章:跟我一起學寫 Makefile

在我們日常寫代碼中,一個工程的源文件不計其數, 按照類型、功能、模塊等分別放在若干個目錄中,這時候我們就可以利用 Makefile 來指定哪些文件先編譯,哪些后編譯,以及更復雜的操作。

make 是一個命令工具,它解釋 Makefile 中的指令。我們只需要在 Makefile 里指定所有的操作,再用 make 這個操作,即可讓整個工程自動編譯。


makefile 的格式如下:

target : prerequisitescommand
  • target: 目標文件 ,可以是多個文件,以空格分開,可以使用通配符。可以是 Object File執行文件 。甚至還可以是一個 標簽(Label),如:clean
  • prerequisites: target依賴對象 。如果其中的 某個文件 要比 目標文件 要新,那么,目標文件 就被認為是 過時的 ,需要重新生成。
  • command: 命令行 ,如果其不與 target:prerequisites 在一行,那么,必須以 [Tab鍵] 開頭,如果在一行,那么可以用分號做為分隔。

一般來說,make會以UNIX的標準Shell,也就是/bin/sh來執行命令。


寫一個 makefile 文件為例:
在這里插入圖片描述

目標程序:
在這里插入圖片描述

執行 make 指令:
在這里插入圖片描述
這樣就生成了 .i,.s.o.out 文件。那么 make 是怎么運行的呢?


make的運行

  1. 在當前目錄下依次找三個文件—— GNUmakefilemakefileMakefile 。其按順序找這三個文件,一旦找到,就開始讀取這個文件并執行。

也可以給 make 命令 指定一個 特殊名字Makefile 。這需要使用 make-f 或是 --file 參數( --makefile 參數也行)。例如,我們有個 Makefile 的名字是 hchen.mk ,則可以這樣執行 make 命令:

make –f hchen.mk

如果在 make 的命令行中,不只一次地使用了 -f 參數,那么,所有指定的 Makefile 將會被連在一起傳遞給 make 執行。

  1. 接下來,它會找文件中的第一個 target (上面例子中的 test ),并把這個目標文件作為最終生成的文件。
  2. 如果 test 文件尚未生成;或是雖然 test 已經生成,但后面的依賴對象 test.o 文件的最后修改時間要比 test 這個文件新(可以用命令 touch 測試),那么,make 就會重新生成 test 這個文件。
  3. 如果 test 所依賴的 test.o 文件不存在,那么 make 會在當前文件中找目標文件為 test.o 的規則,如果找到則再根據那一個規則生成test.o文件。
  4. 如果沒有目標文件為 test.o 的規則,則提前退出;否則,生成 test 文件并退出。

這就是整個 make 的運行過程,make 會一層又一層地去找文件的依賴關系,直到:

  • 最終編譯出第一個目標文件(默認目標)并返回退出碼;
  • 或者因為缺少必要規則而直接返回退出碼。

make命令執行后有三個退出碼:

  • 0 :表示成功執行。
  • 1 :如果 make 運行時出現任何錯誤,返回 1
  • 2 :如果你使用了 make-q 參數,導致一些目標不需要更新,那么返回 2

而對于所定義的命令的錯誤,或是編譯不成功,make根本不理。


通配符

可以通過通配符來簡化命令行:

  • ~ :Unix下, ~/test 表示當前用戶的 $HOME 目錄下的 test 目錄。而 ~hchen/test 則表示用戶 hchen 的宿主目錄下的 test 目錄。而在 Windows 或是 MS-DOS下用戶沒有宿主目錄 ,那么波浪號所指的目錄則根據環境變量 HOME 而定。(make支持UNIX下的通配符用法)
  • * :表示任意長度的字符串,*.c 表示所有后綴為c的文件。而當文件名中有通配符,如: ~ ,那么可以用轉義字符 \ ,如 \~ 來表示真實的 ~ 字符。
  • ? :表示任意一個字符串。

自動化變量

shell 中的 自動化變量(又名:特殊變量) ,make 也是支持的,經常用到下面前三個自動化變量

  • $@目標對象 。在模式規則中,如果有多個目標,那么, $@ 就是匹配于目標中模式定義的集合。
  • $^ :所有 依賴對象 ,以空格分隔。如果在依賴目標中有多個重復的,那么這個變量會去除重復的依賴目標,只保留一份。
  • $< :所有 依賴對象第一個 。如果依賴目標是以 模式(即 % )定義的,那么 $< 將是符合模式的一系列的文件集。注意,其是一個一個取出來的。
  • $? :所有比 目標對象 依賴對象 的集合。以空格分隔。
  • $+ : 這個變量很像 $^ ,也是所有 依賴對象 的集合。只是它不去重。
  • $% :僅當 目標對象 是函數庫文件中、表示規則中的目標成員名。例如,如果一個目標是 foo.a(bar.o) ,那么, $% 就是 bar.o$@ 就是 foo.a 。如果目標不是函數庫文件(Unix下是 .a ,Windows下是 .lib ),那么,其值為空。
  • $* :這個變量表示目標模式中 % 及其之前的部分。(如果 目標對象dir/a.foo.b ,并且 目標對象模式a.%.b ,那么, $* 的值就是 dir/a.foo 。)
    • 這個變量對于構造有關聯的文件名是比較有用的。(如果 目標對象 中沒有 模式 的定義,那么 $* 也就不能被推導出,但是,如果 目標文件 的后綴是 make 所識別的,那么 $* 就是除了后綴的那一部分。)

例如:如果 目標對象foo.c ,因為 .cmake 所能識別的后綴名,所以, $* 的值就是 foo 。這個特性是 GNU make 的,很有可能不兼容于其它版本的 make ,所以,盡量避免使用 $* ,除非是在 隱含規則 或是 靜態模式 中。如果 目標對象 中的后綴是 make 所不能識別的,那么 $* 就是空值。

我們可以利用 自動化變量 簡化 makefile 文件:
在這里插入圖片描述
執行 make 命令:
在這里插入圖片描述

我們還能進一步再簡化,可以利用通配符來表示,在多個 目標對象依賴對象命令行 都相似時,利用通配符 % 來減少工作量,這樣就可以不用一個個寫出每個文件的生成規則了。


偽目標.PHONE:【命令】

.PHONE: [命令]  // 聲明偽目標,無論目標是否最新,每次都重新生成。

舉個 偽目標 的例子:

clean:rm *.o temp

既然我們生成了許多編譯文件,那么我們也應該提供一個清除它們的 目標 以備完整地重編譯。 (以“make clean”來使用該目標)

之所以將 clean 稱為 偽目標 , 是因為我們并不生成 clean 這個文件。偽目標 并不是一個 文件 ,只是一個 標簽 ,由于 偽目標 不是 文件 ,所以 make 無法生成它的 依賴對象 ,無法決定它是否要執行 命令行 。我們只有顯式地指明這個 目標 才能讓其生效。當然,偽目標 的取名不能和 文件名 重名,不然其就失去了 偽目標 的意義了。

因此我們需要用 .PHONY 聲明 偽目標 ,從而區分 偽目標目標文件

.PHONY : clean

而只要有 .PHONY:clean 這個聲明,不管是否有 clean 文件,只要執行 make clean 命令,就會運行 clean 。因此,我們要在聲明后面跟上 clean 的具體內容:

.PHONY : clean
clean :rm *.o temp

通常需要生成的程序不會設置偽對象,因為每個項目的構建需要很長的時間,所以盡可能判斷不需要生成就不用重新生成。

偽目標一般沒有依賴的文件。但是,我們也可以為偽目標指定所依賴的文件。

一個示例就是,如果你的 Makefile 需要一口氣生成若干個可執行文件,但你只想簡單地敲一個 make 完事,并且,所有的目標文件都寫在一個 Makefile 中,那么你可以這樣做:

all : prog1 prog2 prog3
.PHONY : allprog1 : prog1.o utils.occ -o prog1 prog1.o utils.oprog2 : prog2.occ -o prog2 prog2.oprog3 : prog3.o sort.o utils.occ -o prog3 prog3.o sort.o utils.o

Makefile 中的第一個目標會被作為其默認目標。 我們聲明了一個 all 的偽目標,其依賴于其它三個目標。由于 默認目標總是被執行的 ,而上面的 Makefile 文件中的第一個目標(默認目標) all 又是一個偽目標。因此 all 是一定會被執行的,但又因為偽目標只是一個標簽不會生成文件,所以不會有 all 文件產生。于是,其它三個目標的規則總是會被執行。也就達到了我們一口氣生成多個目標的目的。 .PHONY : all 聲明 all 這個目標為 偽目標 。(注:這里的顯式 .PHONY : all 不寫的話一般情況也可以正確的執行,這樣 make 可通過隱式規則推導出, all 是一個偽目標,執行 make 不會生成 all 文件,而是執行后面的多個目標。建議:顯式寫出是一個好習慣。)

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

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

相關文章

全排列變種:限定 排列的差值范圍 及 排列中的元素個數

文章目錄題目描述思路代碼實現題目描述 詳細描述&#xff1a;字節跳動2019春招研發部分編程題——萬萬沒想到之抓捕孔連順 輸入描述&#xff1a; 第一行包含空格分隔的兩個數字 N和D(1?≤?N?≤?1000000; 1?≤?D?≤?1000000) 第二行包含N個整數&#xff08;取值區間為…

Linux | 進程概念、進程狀態(僵尸進程、孤兒進程、守護進程)、進程地址空間

文章目錄進程和程序操作系統如何控制和調度程序進程控制塊–PCB子進程進程狀態僵尸進程孤兒進程守護進程&#xff08;精靈進程&#xff09;進程地址空間引言頁表進程和程序 程序&#xff1a; 一系列有序的指令集合&#xff08;就是我們寫的代碼&#xff09;。進程&#xff1a;…

Linux 進程控制 :進程創建,進程終止,進程等待,程序替換

文章目錄進程創建進程等待程序替換進程終止進程創建 fork函數&#xff1a; 操作系統提供的創建新進程的方法&#xff0c;父進程通過調用 fork函數 創建一個子進程&#xff0c;父子進程代碼共享&#xff0c;數據獨有。 當調用 fork函數 時&#xff0c;通過 寫時拷貝技術 來拷貝…

Linux 內存管理 | 連續分配方式 和 離散分配方式

文章目錄前言連續分配單一連續分配分區式分配固定分區分配動態分區分配可重定位分區分配離散分配分段分頁多級頁表快表(TLB)段頁式Linux前言 Linux 內存管理 | 虛擬內存管理&#xff1a;虛擬內存空間、虛擬內存分配 Linux 內存管理 | 物理內存、內存碎片、伙伴系統、SLAB分配器…

操作系統 | 用戶態和內核態的切換(中斷、系統調用與過程(庫函數)調用)

文章目錄中斷過程調用系統調用過程調用和系統調用的區別中斷 用戶態、內核態之間的切換是怎么實現的? 用戶態→內核態 是通過中斷實現的。并且 中斷是唯一途徑 。核心態→用戶態 的切換是通過執行一個特權指令&#xff0c;將程序狀態字 (PSW) 的標志位設置為 用戶態 。 中斷…

管道實現父子進程的信息傳遞(二)【標準流和其文件描述符、fwrite函數、perror函數】

文章目錄代碼實現標準流 和 標準流文件描述符代碼中用到的函數fwrite()perror()在復習進程間的通信方式時又寫了一遍&#xff0c;和 管道實現父子進程的信息傳遞&#xff08;一&#xff09;【fork函數、pipe函數、write/read操作、wait函數】 的區別不是特別大&#xff0c;只是…

JAVA隨機生成文件名:當前年月日時分秒+五位隨機數

代碼如下&#xff1a; package cn.gov.csrc.util;import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random;public class RandomUtil {/*** 生成隨機文件名&#xff1a;當前年月日時分秒五位隨機數* * return*/public static String getRandomFile…

命名管道實現進程的信息傳遞【mkfifo函數、open函數】

文章目錄代碼實現mkfifo函數open函數代碼實現 #include<fcntl.h> // open() #include<sys/wait.h> // wait() #include<sys/types.h> // mkfifo() #include<sys/stat.h> // mkfifo() #include<iostream> #include<unistd.h> // fork()usi…

Linux 進程 | 進程間的通信方式

文章目錄管道匿名管道 pipe命名管道 FIFO共享內存共享內存的使用流程&#xff1a;消息隊列信號量套接字在之前的博客中講過&#xff0c;虛擬空間出現的其中一個目的就是解決 進程沒有獨立性&#xff0c;可能訪問同一塊物理內存 的問題。因為這種獨立性&#xff0c;進程之間無法…

Linux網絡編程 | socket介紹、網絡字節序與主機字節序概念與兩者的轉換、TCP/UDP 連接中常用的 socket 接口

文章目錄套接字socket 地址通用 socket 地址專用 socket 地址網絡字節序與主機字節序地址轉換TCP/UDP 連接中常用的 socket 接口套接字 什么是套接字&#xff1f; 所謂 套接字 (Socket) &#xff0c;就是對網絡中 不同主機 上的應用進程之間進行雙向通信的端點的抽象。 UNIX/L…

網絡協議分析 | 傳輸層 :史上最全UDP、TCP協議詳解,一篇通~

文章目錄UDP概念格式UDP如何實現可靠傳輸基于UDP的應用層知名協議TCP概念格式保證TCP可靠性的八種機制確認應答、延時應答與捎帶應答超時重傳滑動窗口滑動窗口協議后退n協議選擇重傳協議流量控制擁塞控制發送窗口、接收窗口、擁塞窗口快速重傳和快速恢復連接管理機制三次握手連…

JDom,jdom解析xml文件

1.要解析的文件模板如下&#xff1a; <?xml version"1.0" encoding"GBK"?> <crsc> <data><舉報信息反饋><R index"1"><舉報編號>1</舉報編號><狀態>1</狀態><答復意見>填寫…

網絡協議分析 | 應用層:HTTP協議詳解、HTTP代理服務器

文章目錄概念URLHTTP協議的特點HTTP協議版本格式請求報文首行頭部空行正文響應報文首行頭部空行正文Cookie與SessionHTTP代理服務器正向代理服務器反向代理服務器透明代理服務器概念 先了解一下 因特網&#xff08;Internet&#xff09; 與 萬維網&#xff08;World Wide Web&…

MySQL命令(一)| 數據類型、常用命令一覽、庫的操作、表的操作

文章目錄數據類型數值類型字符串類型日期/時間類型常用命令一覽庫的操作顯示當前數據庫創建數據庫使用數據庫刪除數據庫表的操作創建表顯示當前庫中所有表查看表結構刪除表數據類型 mysql 的數據類型主要分為 數值類型、日期/時間類型、字符串類型 三種。 數值類型 數值類型可…

C++ 繼承 | 對象切割、菱形繼承、虛繼承、對象組合

文章目錄繼承繼承的概念繼承方式及權限using改變成員的訪問權限基類與派生類的賦值轉換回避虛函數機制派生類的默認成員函數友元與靜態成員多繼承菱形繼承虛繼承組合繼承 繼承的概念 繼承可以使得子類具有父類的屬性和方法或者重新定義、追加屬性和方法等。 當創建一個類時&…

博弈論 | 博弈論簡談、常見的博弈定律、巴什博弈

文章目錄博弈論什么是博弈論&#xff1f;博弈的前提博弈的要素博弈的分類非合作博弈——有限兩人博弈囚徒困境合作博弈——無限多人博弈囚徒困境常見的博弈定律零和博弈重復博弈智豬博弈斗雞博弈獵鹿博弈蜈蚣博弈酒吧博弈槍手博弈警匪博弈海盜分金巴什博弈博弈論 什么是博弈論…

MySQL命令(二)| 表的增刪查改、聚合函數(復合函數)、聯合查詢

文章目錄新增 (Create)全列插入指定列插入查詢 (Retrieve)全列查詢指定列查詢條件查詢關系元素運算符模糊查詢分頁查詢去重&#xff1a;DISTINCT別名&#xff1a;AS升序 or 降序更新 (Update)刪除 (Delete)分組&#xff08;GROUP BY&#xff09;聯合查詢內連接&#xff08;inne…

Spring3.1+Quertz1.8實現多個計劃任務

1.主要是配置文件&#xff1a;如下&#xff1a;(這里說明一下主要是看紅色部分的配置&#xff0c;其他的可以根據自己的實際情況修改&#xff0c;這里只是個思路。) <?xml version"1.0"?> <beans xmlns"http://www.springframework.org/schema/beans…

MySQL | 數據庫的六種約束、表的關系、三大范式

文章目錄數據庫約束NOT NULL&#xff08;非空約束&#xff09;UNIQUE&#xff08;唯一約束&#xff09;DEFAULT&#xff08;缺省約束&#xff09;PRIMARY KEY&#xff08;主鍵約束&#xff09;AUTO_INCREMENT 自增FOREIGN KEY&#xff08;外鍵約束&#xff09;CHECK&#xff08…

哈希 :哈希沖突、負載因子、哈希函數、哈希表、哈希桶

文章目錄哈希哈希&#xff08;散列&#xff09;函數常見的哈希函數字符串哈希函數哈希沖突閉散列&#xff08;開放地址法&#xff09;開散列&#xff08;鏈地址法/拉鏈法&#xff09;負載因子以及增容對于閉散列對于開散列結構具體實現哈希表&#xff08;閉散列&#xff09;創建…