『Linux從入門到精通』第 ? 期 - 動靜態庫

在這里插入圖片描述

文章目錄

  • 💐專欄導讀
  • 💐文章導讀
  • 🐧什么是庫?
  • 🐧為什么要有庫?
  • 🐧寫一個自己的庫
    • 🐦方法一
    • 🐦方法二 靜態庫
    • 🐦標準化
    • 🐦方法三 動態庫
    • 🐦配置動態庫
      • 🐱環境變量
      • 🐱軟鏈接
      • 🐱配置文件
  • 🐧靜態庫與動態庫的區別
  • 🐧動態庫的運作原理
      • 🐔為什么進程可以在運行時加載動態庫?
      • 🐔為什么多個進程可以共享一個動態庫

💐專欄導讀

🌸作者簡介:花想云 ,在讀本科生一枚,C/C++領域新星創作者,新星計劃導師,阿里云專家博主,CSDN內容合伙人…致力于 C/C++、Linux 學習。

🌸專欄簡介:本文收錄于 Linux從入門到精通,本專欄主要內容為本專欄主要內容為Linux的系統性學習,專為小白打造的文章專欄。

🌸相關專欄推薦:C語言初階系列C語言進階系列C++系列數據結構與算法

💐文章導讀

本章我們將深入學習Linux中動靜態庫的使用及其原理。

在這里插入圖片描述

🐧什么是庫?

在學習生涯中,我們總是能談到庫,例如C語言標準庫、C++標準庫。那么到底什么是庫呢?

在計算機科學中,術語“庫”通常指的是庫文件(Library),它是一組預編譯的、可重用的代碼和資源的集合,用于支持軟件開發。庫的目的是為開發人員提供一組常用的功能,以便在應用程序中進行調用,從而避免重復編寫相同的代碼。

庫可以分為兩大類:

  • 靜態庫(Static Library): 靜態庫在編譯時被鏈接到應用程序中,形成一個獨立的可執行文件。在程序運行時,靜態庫的代碼被完全復制到應用程序中,因此應用程序不再依賴于原始的庫文件。靜態庫的文件擴展名通常是.a(在Unix/Linux系統中)或.lib(在Windows系統中)。

  • 動態庫(Dynamic Library): 動態庫在運行時加載到內存中,多個應用程序可以共享同一個動態庫的實例。這可以減小應用程序的大小,因為動態庫的代碼只需要存在一份,而且可以在運行時更新。動態庫的文件擴展名通常是.so(在Unix/Linux系統中)或.dll(在Windows系統中)。

在編程中,開發人員通過包含庫的頭文件、鏈接庫文件,以及在代碼中調用庫提供的函數或方法,可以輕松地利用庫的功能。

在我們的Linxu機器上,系統已經為我們預裝了C/C++的頭文件和庫文件。頭文件提供方法說明,庫文件提供方法的實現。頭文件與庫文件是有對應關系的,需要組合在一起使用。

一個可執行程序生成需要經歷四個階段:預處理、編譯、匯編、鏈接。頭文件在預處理階段被引入,庫文件在鏈接階段被引入。

C/C++庫文件

$ ls /usr/lib64/libc*
/usr/lib64/libc-2.17.so         /usr/lib64/libc_nonshared.a        /usr/lib64/libcroco-0.6.so.3.0.1    /usr/lib64/libc.so
/usr/lib64/libcap-ng.so.0       /usr/lib64/libcollection.so.2      /usr/lib64/libcrypt-2.17.so         /usr/lib64/libc.so.6
/usr/lib64/libcap-ng.so.0.0.0   /usr/lib64/libcollection.so.2.1.1  /usr/lib64/libcrypto.so.10          /usr/lib64/libcupscgi.so.1
/usr/lib64/libcap.so.2          /usr/lib64/libcom_err.so.2         /usr/lib64/libcrypto.so.1.0.2k      /usr/lib64/libcupsimage.so.2
/usr/lib64/libcap.so.2.22       /usr/lib64/libcom_err.so.2.1       /usr/lib64/libcryptsetup.so.12      /usr/lib64/libcupsmime.so.1
/usr/lib64/libcgroup.so.1       /usr/lib64/libcpupower.so.0        /usr/lib64/libcryptsetup.so.12.3.0  /usr/lib64/libcupsppdc.so.1
/usr/lib64/libcgroup.so.1.0.41  /usr/lib64/libcpupower.so.0.0.0    /usr/lib64/libcryptsetup.so.4       /usr/lib64/libcups.so.2
/usr/lib64/libcidn-2.17.so      /usr/lib64/libcrack.so.2           /usr/lib64/libcryptsetup.so.4.7.0   /usr/lib64/libcurl.so.4
/usr/lib64/libcidn.so           /usr/lib64/libcrack.so.2.9.0       /usr/lib64/libcrypt.so              /usr/lib64/libcurl.so.4.3.0
/usr/lib64/libcidn.so.1         /usr/lib64/libcroco-0.6.so.3       /usr/lib64/libcrypt.so.1

C/C++頭文件

$ ls /usr/include/stdio.h
/usr/include/stdio.h
$ ls /usr/include/c++/4.8.5/iostream 
/usr/include/c++/4.8.5/iostream

🐧為什么要有庫?

引入庫的概念有很多重要的原因,它們有助于提高軟件開發的效率、可維護性和可擴展性。以下是一些主要的原因:

  1. 代碼重用: 庫提供了一組通用的功能或工具,可以在多個項目中被重復使用。這樣可以避免開發人員重復編寫相同的代碼,提高了開發效率。

  2. 模塊化開發: 庫使軟件能夠以模塊化的方式構建。通過將不同的功能分解成獨立的庫,開發人員可以更容易地理解和維護代碼。模塊化開發還使得團隊能夠并行工作,每個成員專注于特定的任務或功能。

  3. 抽象和封裝: 庫提供了對底層實現的抽象,使開發人員可以專注于高層次的問題而不必關心底層的細節。這種抽象和封裝的概念有助于隱藏復雜性,提高代碼的可讀性和可維護性。

  4. 提高可靠性: 庫經過充分測試和驗證,可以提供高質量的代碼。開發人員使用庫時,可以信任這些已經驗證過的功能,減少了潛在的錯誤和漏洞。

  5. 快速開發: 使用庫可以加速開發過程。通過利用現成的庫,開發人員可以更快地構建應用程序,而不必從頭開始編寫每一個功能。

  6. 標準化: 某些庫成為行業或社區標準,提供了一致的接口和實現。這種標準化有助于確保代碼的一致性,同時使得不同項目之間更容易進行集成和交互。

  7. 可擴展性: 庫使得軟件的架構更具擴展性。通過將不同的模塊組織為庫,可以更容易地添加新功能、升級現有功能或替換特定的實現,而無需修改整個應用程序。

總體而言,引入庫的概念有助于構建更可靠、可維護和可擴展的軟件系統,提高了軟件開發的效率和質量。

🐧寫一個自己的庫

為了更深入的理解庫運作的原理,我們嘗試自己寫一個庫,并交給其他小伙伴使用。

接下來,我們將實現一個加減運算的程序,并將給程序的源代碼與頭文件進行打包,并交給小伙伴——小黑使用。

$ touch add.c
$ touch add.h
$ touch sub.c
$ touch sub.h
/* add.h */
#pragma once 
int add(int a, int b);
/* add.c */
#include "add.h"
int add(int a, int b){return a + b;
}
/* sub.h */
#pragma once 
int sub(int a, int b);
/* sub.c */
#include "sub.h"
int sub(int a, int b){return a - b;
}

現在我們已經將計算器的源代碼寫好,現在我們想讓小黑使用我們的成果,倘若我們直接把源文件以及頭文件發給小黑,這種做法肯定是沒問題的。

但是我們又想讓小黑使用我們的成果,又不想讓小黑看到我們的源代碼,現在該怎么辦呢?

🐦方法一

第一種方法是我們可以將源代碼經過預處理、編譯、匯編后形成二進制文件。小黑拿到該二進制文件后,再將它自己寫的程序同樣經過預處理、編譯、匯編形成二進制文件,然后將兩個二進制文件進行鏈接即可。

$ gcc -c *.c
$ ll
total 28
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:25 add.c
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:25 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 add.o
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:20 sub.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:19 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 sub.o
drwxrwxr-x 2 hxy hxy 4096 Feb 28 16:40 xiaohei
$ cp *h xiaohei/
$ cp *o xiaohei//*小黑視角*/
ll
total 20
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:43 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:45 add.o
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:43 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:45 sub.o
/*小黑視角*/
$ gcc -c main.c 
$ gcc -o test add.o sub.o main.o
$ ls
add.h  add.o  main.c  main.o  sub.h  sub.o  test
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐦方法二 靜態庫

方法一中我們需要將許多的 .o文件以及.h文件打包給對方,這種做法明顯感覺不是特別優雅。接下來我們就是用靜態庫的方式。

  1. 先將我們的.o文件打包生成一個靜態庫,并發送給小黑;
$ ar -rc libcalculate.a *.o
$ ll
total 32
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:25 add.c
-rw-rw-r-- 1 hxy hxy   38 Feb 28 16:25 add.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 add.o
-rw-rw-r-- 1 hxy hxy 2688 Feb 28 18:31 libcalculate.a
-rw-rw-r-- 1 hxy hxy   60 Feb 28 16:20 sub.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 16:19 sub.h
-rw-rw-r-- 1 hxy hxy 1240 Feb 28 16:40 sub.o
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:23 xiaohei
$ cp libcalculate.a xiaohei/* 小黑視角 */
$ ll
total 16
-rw-rw-r-- 1 hxy hxy   38 Feb 28 18:35 add.h
-rw-rw-r-- 1 hxy hxy 2688 Feb 28 18:33 libcalculate.a
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rw-rw-r-- 1 hxy hxy   39 Feb 28 18:35 sub.h

注意

這里我們需要注意庫的命名規則。庫的命名是以lib為開頭,以.a或.so為結尾。例如 libcalculate.a 的真實名稱為 calculate

在小黑拿到我們的庫文件后,他就可以編譯生成自己的程序了。但是這里有幾個細節需要注意:

  • 因為我們的庫是第三方的,編譯器并不知道這個庫的存在,所以我們需要指明庫所在的路徑;
  • 同樣,我們需要告訴編譯器該鏈接哪一個庫;
  • 同理,我們還需指明頭文件所在的路徑。但是目前頭文件就在當前路徑下,所以可省略;

2.小黑進行編譯鏈接;

/* 小黑視角 */
$ gcc -o test main.c -L . -l calculate -I .
$ ./test 
10 + 3 = 13
10 - 3 = 7
  • -L 選項:指明庫所在的路徑;
  • -l 選項: 告訴編譯器鏈接哪一個庫;
  • -I 選項:告訴編譯器頭文件的位置;

🐦標準化

上面方法二中我們演示了一個庫文件的使用原理。在實際的項目開發中,我們并不會這么隨意潦草。
再以小黑為例:

  1. 將頭文件全部移至一個目錄下;
  2. 將庫文件全部移至一個目錄下;
  3. 將頭文件與庫文件進行打包;
  4. 將打包好的文件上傳至云端;
$ mkdir lib
$ mkdir include
$ cp *.h include/
$ cp *.a lib
$ tar -czf calcuate.tgz include lib

遠在海外的小黑想用我們寫好的庫,于是在云端將壓縮包下載到了本地;

/* 小黑視角 */
$ ll
total 8
-rw-rw-r-- 1 hxy hxy 788 Feb 28 19:01 calcuate.tgz
-rw-rw-r-- 1 hxy hxy 200 Feb 28 16:26 main.c

小黑將它進行解壓看到了頭文件與庫文件;

$ tar xzf calcuate.tgz 
$ ll
total 16
-rw-rw-r-- 1 hxy hxy  788 Feb 28 19:01 calcuate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c

最后小黑進行了編譯鏈接等一系列操作,成功運行了自己的程序;

$ gcc -o test main.c -I ./include -L ./lib -l calculate
$ ./test 
10 + 3 = 13
10 - 3 = 7

以后的小黑會經常用到這個庫,但是他覺得每次都要寫這么長的指令有些麻煩。于是他將這個庫的頭文件全部移至系統的/usr/include目錄下;將庫文件移至/usr/lib目錄下;

$ sudo cp include/*.h /usr/include/
$ sudo cp lib/* /lib64/

以后他每次使用這個庫時,編譯器會自動在這兩個目錄下尋找所程序所依賴的頭文件與庫文件;

$ gcc -o test main.c -lcalculate
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐦方法三 動態庫

以上我們嘗試將自己的源文件制作為一個靜態庫供小黑使用,接下來我們在嘗試制作一個動態庫。

$ gcc -fPIC -c add.c sub.c
  • fPIC:產生位置無關碼(position independent code);
gcc -shared -o libcalculate.so *.o
  • shared: 表示生成共享庫格式;

接著我們把生成的.so文件放在lib目錄下,將.h文件放到include目錄下,并打包發給小黑(重復之前的操作)。

$ rm lib/libcalculate.a
$ rm calcuate.tgz 
$ cp *.so lib
$ tar -czf calculate.tgz lib include
$ cp calculate.tgz xiaohei/

小黑拿到壓縮文件,解壓后得到libinclude于是用我們的庫來鏈接自己的程序。

/*小黑視角*/
$ tar xzf calculate.tgz 
$ ll
total 16
-rw-rw-r-- 1 hxy hxy 2343 Feb 29 16:39 calculate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 29 16:38 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
$
$ gcc -o test main.c -I include -L lib -lcalculate
$ ll
total 28
-rw-rw-r-- 1 hxy hxy 2343 Feb 29 16:39 calculate.tgz
drwxrwxr-x 2 hxy hxy 4096 Feb 28 18:56 include
drwxrwxr-x 2 hxy hxy 4096 Feb 29 16:38 lib
-rw-rw-r-- 1 hxy hxy  200 Feb 28 16:26 main.c
-rwxrwxr-x 1 hxy hxy 8432 Feb 29 16:49 test
$
$ ./test 
./test: error while loading shared libraries: libcalculate.so: cannot open shared object file: No such file or directory

小黑發現了事情的不妙,心想剛才不是還好好的嗎?怎么運行時提示找不到庫文件呢?

原來是因為在程序運行時,calculate.so并沒有在系統的默認路徑下,所以OS找不到!那么如何才能讓OS找到我們的庫呢?答案是需要我們自己來配置。

🐦配置動態庫

配置動態庫有三種方法:

  1. 環境變量:LD_LIBRARY_PATH (臨時方案);
  2. 軟鏈接方案;
  3. 配置文件方案

🐱環境變量

導入環境變量LD_LIBRARY_PATH:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/..../lib/ # 你的.so文件存放路徑

查看環境變量LD_LIBRARY_PATH:

$ echo $LD_LIBRARY_PATH
:/usr/local/protobuf/lib/:/home/hxy/.VimForCpp/vim/bundle/YCM.so/el7.x86_64:/home/..../lib/ # 你的.so

現在再來運行程序就可以成功運行了:

$ ./test 
10 + 3 = 13
10 - 3 = 7

🐱軟鏈接

上面我們說在程序運行時,calculate.so并沒有在系統的默認路徑下,所以OS找不到我們的庫,那么這個默認路徑在哪里呢?

# 一般在這兩個路徑下
$ /lib
$ /lib64/

所以我們直接將庫文件移動到這兩個路徑下也可以,但是還有比較優雅一點的方案,那就是為我們的庫文件建立軟鏈接。

$ sudo ln -s lib/libcalculate.so /lib64/libcalculate.so
$ ./test 
10 + 3 = 13
10 - 3 = 7

🐱配置文件

  1. 在系統的 /etc/ld.so.conf.d/ 目錄下創建一個配置文件;
$ sudo touch /etc/ld.so.conf.d/calculate.conf
  1. 將動態庫所在路徑寫入配置文件;
$ sudo vim /etc/ld.so.conf.d/calculate.conf
$ sudo cat /etc/ld.so.conf.d/calculate.conf
/home/.../lib # 你的.so文件路徑
  1. 讓配置文件生效;
$ sudo ldconfig

現在小黑的程序也能成功運行了;

$ ./test 
10 + 3 = 13
10 - 3 = 7

🐧靜態庫與動態庫的區別

以上我們通過制作一個自己的靜態庫,對庫文件有了基礎的了解。那么庫為何又要分為靜態庫與動態庫呢?二者有何區別?

  1. 鏈接時機和方式:

    • 靜態庫: 在編譯時被鏈接到目標程序中,鏈接器將庫的代碼和數據拷貝到最終的可執行文件中。因此,可執行文件在運行時獨立于庫文件。
    • 動態庫: 在編譯時并不直接鏈接到目標程序,而是在運行時由操作系統動態加載到內存中。動態庫的鏈接發生在程序啟動時(靜態加載)或在運行時(動態加載)。
  2. 文件大小和內存占用:

    • 靜態庫: 鏈接時會將庫的代碼和數據完全復制到目標程序中,可能導致可執行文件較大。每個使用該庫的可執行文件都包含一份庫的拷貝。
    • 動態庫: 多個程序可以共享同一個動態庫的實例,因此相同的庫只需要在內存中存在一份,可以減小程序的大小。
  3. 更新和維護:

    • 靜態庫: 如果庫的代碼或數據發生變化,需要重新編譯并重新鏈接所有使用該庫的程序。每個程序都需要更新以包含最新版本的庫。
    • 動態庫: 如果庫的代碼或數據發生變化,只需要替換庫文件而無需重新編譯和鏈接使用該庫的程序。這使得動態庫更容易更新和維護。
  4. 跨平臺兼容性:

    • 靜態庫: 可執行文件與庫的鏈接是在編譯時完成的,因此在不同平臺上可能需要不同版本的庫。
    • 動態庫: 由于動態庫的加載是在運行時由操作系統完成的,因此相同的動態庫文件可以在多個平臺上使用。
  5. 運行時靈活性:

    • 靜態庫: 執行文件在編譯時固定了對靜態庫的依賴,無法在運行時更改。
    • 動態庫: 可以在運行時加載或替換動態庫,這使得系統更加靈活。

🐧動態庫的運作原理

🐔為什么進程可以在運行時加載動態庫?

我們知道每個程序在運行時就變成一個進程,一個進程擁有自己的虛擬地址空間。

在程序運行時,我們只需要將庫加載到內存當中,經過頁表映射到進程的地址空間中,我們的代碼執行庫中的方法就依舊還是在自己的地址空間中進行函數跳轉。

🐔為什么多個進程可以共享一個動態庫

當多個進程同時運行時,按照同樣的方式,將庫中的地址映射到每個進程的地址空間中,那么如果每個程序使用的地址都是相同的,不會產生沖突嗎?

還記得我們在用 gcc 生成動態庫時用到的參數 - fPIC 嗎?

-fPIC 是 GCC 編譯器選項,用于生成位置無關碼(Position Independent Code,PIC)。位置無關碼是一種在內存中加載時不依賴于特定內存地址的機器碼,通常用于共享庫(動態鏈接庫)的編譯。

具體來說,使用 -fPIC 選項的目的是允許將生成的目標文件用于共享庫,而這些庫可以被多個進程加載到內存的不同地址上,而不會發生地址沖突。

它主要特點包括:

  1. 位置獨立性: 生成的代碼不依賴于特定的內存地址,可以在不同的內存地址空間中運行。這對于動態鏈接庫是必要的,因為它們可能在不同的進程中加載并映射到不同的地址。

  2. 全局偏移表(Global Offset Table,GOT): 在運行時,PIC 代碼使用全局偏移表,其中包含指向全局和共享庫中的符號的指針。這些指針在加載庫時進行重定位,以便正確地找到符號的位置。

  3. 避免絕對地址: 使用相對尋址或基于 GOT 的尋址,而不是絕對地址。這使得代碼更容易在不同的地址空間中重定位。

本章的內容到這里就結束了!如果覺得對你有所幫助的話,歡迎三連~

在這里插入圖片描述

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

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

相關文章

python中自定義報錯

class MyError(Exception):def __init__(self,num):#錄入的數Exception.__init__(self)self.numnumdef __str__(self):return 這是我定義的第%d個異常 %(self.num)使用 try:raise MyError(4) except MyError as e:print(e)raise 其作用是指定拋出的異常名稱,以及異常…

題目 1240: 生日日數

題目描述: CCC老師的生日是YY年MM月DD日,他想知道自己出生后第一萬天紀念日的日期(出生日算第0天)。 代碼: package lanqiao;import java.time.LocalDate; import java.util.*;public class Main {public static void main(String[] args)…

數據結構題目①——數組

前言 本篇文章為博主進行代碼隨想錄——數組練習后的總結會涉及到每一道題目的詳細的思路整理,以及本人的易錯點,希望對大家有所幫助 數組介紹: 數組在C語言中就已經有所涉及,它是一個最基礎的數據結構,而在數據結構中…

Java學習—FileInputStream

在Java編程中,文件操作是日常任務之一。無論是讀取配置文件、處理圖像,還是讀寫日志文件,理解如何有效地進行文件讀取都是非常重要的。Java提供了多種方式來操作文件,而FileInputStream是其中最基礎也是最直接的一種。本文將深入探…

Spring面試系列-01

1. 什么是 Spring 框架? Spring中文翻譯過來是春天的意思,被稱為J2EE的春天,是一個開源的輕量級的Java開發框架, 具有控制反轉(IoC)和面向切面(AOP)兩大核心。Java Spring框架通過聲明式方式靈活地進行事務的管理,提高開發效率和質量。 Spring框架不僅限于服務器端的…

three 層級模型

group.remove(mesh1,mesh2);Vector3與模型位置、縮放屬性 Group層級模型(樹結構) 創建了兩個網格模型mesh1、mesh2,通過THREE.Group類創建一個組對象group,然后通過add方法把網格模型mesh1、mesh2作為設置為組對象group的子對象,然后在通過執行scene.a…

jenkins部署maven項目

流程: jenkins從代碼倉庫讀取代碼,將代碼文件放入jenkins的工作空間,將jenkins工作空間的代碼進行打包,將jar包遠程發送給服務器。 一:所需插件二:Tools 三:System: 配置ssh連接的…

github要求2fa身份驗證

前言 github登陸的時候發現要求2fa驗證, 2fa是啥?咋驗證? 解決 2FA(Two-Factor Authentication,雙因素身份驗證) 就是在賬戶和密碼的基礎上增加一次驗證碼驗證,這樣即使密碼被竊取,由于黑客沒有你的驗證碼也無法登陸 就像是銀行的u盾一樣…

python63-Python的循環之循環使用else

Python的循環都可以定義else代碼塊&#xff0c;當循環條件為False 時&#xff0c;程序會執行else代碼塊。如下代碼示范了為while循環定義else代碼塊。 # !/usr/bin/env python# -*- coding: utf-8 -*-# Time : 2024/01# Author : Laopicount_i 0while count_i < 5:print(c…

Java集合相關面試題(2024大廠高頻面試題系列)

1、說一說Java提供的常見集合&#xff1f;&#xff08;畫一下集合結構圖&#xff09; 在java中提供了量大類的集合框架&#xff0c;主要分為兩類&#xff1a; 第一個是Collection 屬于單列集合&#xff0c;第二個是Map 屬于雙列集合 在Collection中有兩個子接口List和Set。…

【力扣hot100】刷題筆記Day14

前言 又是新的一周&#xff0c;快樂的周一&#xff0c;快樂地刷題&#xff0c;今天把鏈表搞完再干活&#xff01; 114. 二叉樹展開為鏈表 - 力扣&#xff08;LeetCode&#xff09; 前序遍歷 class Solution:def flatten(self, root: Optional[TreeNode]) -> None:if not r…

回溯 Leetcode 51 N皇后

N皇后 Leetcode 51 學習記錄自代碼隨想錄 按照國際象棋的規則&#xff0c;皇后可以攻擊與之處在同一行或同一列或同一斜線上的棋子。 n 皇后問題 研究的是如何將 n 個皇后放置在 nn 的棋盤上&#xff0c;并且使皇后彼此之間不能相互攻擊。 給你一個整數 n &#xff0c;返回所…

Linux —— 鏈接文件

硬鏈接 一般情況下&#xff0c;文件名和inode號碼是"一一對應"關系&#xff0c;每個inode號碼對應一個文件名。但是&#xff0c;Unix/Linux系統允許&#xff0c;多個文件名指向同一個inode號碼。 這意味著&#xff0c;可以用不同的文件名訪問同樣的內容&#xff1b;對…

軟件開發常見模型解析

軟件開發常見模型解析 摘要&#xff1a;本文將為您詳細介紹軟件開發過程中常見的幾種模型&#xff0c;包括瀑布模型、敏捷開發模型、螺旋模型、迭代模型和原型模型。通過了解這些模型的原理、優缺點&#xff0c;幫助您在不同的軟件項目中選擇最適合的開發模型。 一、引言 在…

【IC前端虛擬項目】inst_buffer子模塊DS與RTL編碼

【IC前端虛擬項目】數據搬運指令處理模塊前端實現虛擬項目說明-CSDN博客 需要說明一下的是,在我所提供的文檔體系里,并沒有模塊的DS文檔哈,因為實際項目里我也不怎么寫DS畢竟不是每個公司都和HISI一樣對文檔要求這么嚴格的。不過作為一個培訓的虛擬項目,還是建議在時間充裕…

Docker技術概論(3):Docker 中的基本概念

Docker技術概論&#xff08;3&#xff09; Docker 中的基本概念 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://…

基于java+springboot女士電商平臺系統源碼+文檔設計

基于javaspringboot女士電商平臺系統源碼文檔設計 博主介紹&#xff1a;多年java開發經驗&#xff0c;專注Java開發、定制、遠程、文檔編寫指導等,csdn特邀作者、專注于Java技術領域 作者主頁 央順技術團隊 Java畢設項目精品實戰案例《1000套》 歡迎點贊 收藏 ?留言 文末獲取源…

C語言----動態內存管理(2)

1.這里總結動態內存管理里面的錯誤 &#xff08;1&#xff09;使用malloc開辟空間以后直接賦值 這個就是malloc開辟失敗返回空指針&#xff0c;直接給空指針賦值就是錯誤的&#xff0c; tip1:使用malloc開辟空間以后一定要判斷是否為空 &#xff08;2&#xff09; 越界訪問…

Python批量提取文件夾中圖片的名稱及路徑到指定的.txt文件中

目錄 一、代碼二、提取效果 一、代碼 import os# 定義要保存的文件名 file_name "TestImage/Image_Visible_Gray.txt"# 讀取文件夾路徑 folder_path "TestImage/Image_Visible_Gray"# 遍歷文件夾中的所有文件 with open(file_name, "w") as f…

IO進程線程day1

編寫鏈表&#xff0c;鏈表里面隨便搞點數據&#xff0c;使用fprintf將鏈表中所有的數據保存到文件中&#xff0c;用fscanf讀取文件中的數據寫入鏈表中 #include <stdio.h> #include <stdlib.h>typedef struct Node {int data;struct Node* next; } Node;// 創建新…