基于按鍵開源MultiButton框架深入理解代碼框架(二)(指針的深入理解與應用)

文章目錄

  • 2、針對該開源框架理解
  • 3、分析代碼
    • 3.1 再談指針、數組、數組指針
    • 3.2 繼續分析源碼


2、針對該開源框架理解

在編寫按鍵模塊的框架中,一定要先梳理按鍵相關的結構體、枚舉等變量。這些數據是判斷按鍵按下、狀態跳轉、以及綁定按鍵事件的核心。
這一部分定義是在驅動層文件 "multi_button.h",這個里面的數據類型雖然都是跟按鍵有關的,并且主要是驅動層在使用,但是這個地方需要走出一個誤區:

關于按鍵的相關的結構體、枚舉等變量。這一部分定義是在驅動層文件 "multi_button.h",這個里面的數據類型雖然都是跟按鍵有關的,并且主要是驅動層在使用,但是應用層也需要知道按鍵的樣子是什么樣子,這樣的目的是為了保證數據流的有效流動,是必要的。如果不這樣,每一個模塊的數據都是孤立存在的,產生不了聯系,這不算是使用全局變量。

multi_button.h 中定義的按鍵結構體(如 Button)和事件枚舉(如 PRESS_DOWNLONG_PRESS 等)屬于驅動層對應用層的接口規范

  • ?應用層需要知道數據結構格式?:因應用層需創建按鍵實例(如 Button btn1;)并傳遞給 button_init(),必須了解結構體成員(如 pinevent)以正確初始化和處理事件回調。

  • ?驅動層隱藏實現細節?:雖然結構體定義在頭文件中,但驅動層內部的狀態機邏輯、鏈表管理等實現仍封裝在 .c 文件中,對應用層不可見。
    應用層通過 read_button_gpio() 回調函數向驅動層提供硬件狀態,驅動層通過事件枚舉(如 CLICK)向應用層傳遞抽象結果。結構體/枚舉作為數據傳遞的載體,是層間通信的契約,確保數據格式一致。

  • 按鍵結構體/枚舉在 multi_button.h 中的定義是必要的接口契約,用于保證驅動層與應用層間數據流的有效流動,不屬于全局變量濫用。

  • 這種設計在滿足數據交互需求的同時,通過封裝驅動實現細節(如鏈表管理、狀態機),依然符合分層架構的高內聚、低耦合原則。

  • 若需進一步隔離,可采用“句柄化”或標準化事件接口,但需權衡實現復雜度與資源消耗。

我覺得一定要帶著這種思想才能深入的去理解該按鍵框架的意義,體會作者的用意,知其所以然。

3、分析代碼

// 按鈕事件類型
typedef enum {BTN_PRESS_DOWN = 0,     // 按鈕按下(物理按下動作)BTN_PRESS_UP,           // 按鈕釋放(物理釋放動作)BTN_PRESS_REPEAT,       // 檢測到重復按下(連按事件)BTN_SINGLE_CLICK,       // 單擊完成(短按后釋放)BTN_DOUBLE_CLICK,       // 雙擊完成(兩次快速單擊)BTN_LONG_PRESS_START,   // 長按開始(達到長按閾值)BTN_LONG_PRESS_HOLD,    // 長按保持(持續按住狀態)BTN_EVENT_COUNT,        // 事件總數(用于數組定義)BTN_NONE_PRESS          // 無事件(空閑狀態)
} ButtonEvent;// 按鈕狀態機狀態
typedef enum {BTN_STATE_IDLE = 0,     // 空閑狀態(等待按下)BTN_STATE_PRESS,        // 按下狀態(檢測到有效按下)BTN_STATE_RELEASE,      // 釋放狀態(等待超時以區分單擊/雙擊)BTN_STATE_REPEAT,       // 重復按下狀態(連按計數中)BTN_STATE_LONG_HOLD     // 長按保持狀態(持續檢測長按)
} ButtonState;// 按鈕控制結構體
struct _Button {uint16_t ticks;                     // 時間戳計數器(用于超時判斷)uint8_t  repeat : 4;                // 重復按下計數(4位,范圍0-15)uint8_t  event : 4;                 // 當前事件類型(4位,對應ButtonEvent)uint8_t  state : 3;                 // 狀態機狀態(3位,對應ButtonState)uint8_t  debounce_cnt : 3;          // 消抖計數(3位,范圍0-7)uint8_t  active_level : 1;          // 有效觸發電平(1位,0=低電平有效,1=高電平有效)uint8_t  button_level : 1;          // 當前按鈕電平(1位,實時GPIO狀態)uint8_t  button_id;                 // 按鈕標識符(用于多按鈕區分)uint8_t  (*hal_button_level)(uint8_t button_id);  // HAL層按鈕電平讀取函數指針BtnCallback cb[BTN_EVENT_COUNT];    // 事件回調函數數組(按事件類型注冊)Button* next;                       // 鏈表指針(支持多按鈕管理)
};

主要需要區分的是按鍵狀態機和按鍵事件類型。

按鍵事件的目的是為了和該時間相應的動作綁定,也就是在什么狀態下,執行什么應用邏輯。通過函數 button_attach 進行綁定,

這是調用文件:
button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_handler);具體實現是在multi_button.c文件
void button_attach(Button* handle, ButtonEvent event, BtnCallback cb)
{if (!handle || event >= BTN_EVENT_COUNT) return;  // parameter validationhandle->cb[event] = cb;
}

其中 handle->cb[event] = cb; 這個目的就是接收傳遞的相關事件處理函數。
并且要想保證傳遞的按鍵處理函數有消息,我們其實是需要定義一個指針函數的,只有這樣傳遞進來的按鍵處理函數入口地址才能被編譯器有效識別為函數入口地址,不然編譯器只是知道這是一個地址。這也就是在之前工作中 胡哥 告訴我的。在函數傳遞的時候無所謂什么,只要是一個地址就行 ,但是在函數定義的時候,定義輸入參數的時候,我們必須要對這個參數是什么類型進行說明,例如是地址,我們就要聲明這是什么地址,是函數指針地址、一個指針變量還是單純的一個數組的入口地址。(可以理解為強轉,也可以理解為告訴編譯器或者告訴這個函數我這個地址本質是什么地址)。根本原因就是上述分析。

所以我們能看到在文件 multi_button.h 這行代碼聲明一個函數指針,就用來定義按鍵事件處理函數的入口地址。

typedef void (*BtnCallback)(Button* btn_handle);

3.1 再談指針、數組、數組指針

此外由于這里使用到了==指針、數組、數組指針==,所以有必要進行說明一下其中的區別與聯系:

在 C 語言中,數組的本質是連續內存塊,其內容完全由定義時的類型決定。

在C語言中,數組確實可以存儲任何類型,但必須明確定義元素類型,這樣才能保證正確訪問內存和調用函數。

數組和指針的本質理解:

數組是什么,數組的本質是連續的內存塊,注意是連續,也就是說可以通過下標進行訪問,并且申請的內存空間是和存儲的數據類型強相關的。本質也是一個個的連續地址。

指針是什么,指針就是地址,如果我們需要存儲一個函數,那么就需要將這個函數存儲到一塊內存空間,相當于是申請了一塊內存,那么編譯器在訪問的時候其實是先找到這個函數的入口地址從而進行訪問。而我們的指針變量就是存儲這個函數入口地址的內存空間,縱使這個函數的內存很大,需要很多個地址存儲,但是體現在指針變量里面就只是保存了一個入口地址,至于剩下的怎么執行完整這個函數,就不需要我們操心了。這個地方就是區別于數組,數組存儲一些內容需要的申請完整的內存空間,來存儲我們需要存儲數組里面的所有內容,也就是都要找到對應的地址空間。

數組是申請內存空間存儲內容,數組元素存儲的是實際數據值,除非該數組是指針數組(如 int* arr[5])。

int arr[3] = {10, 20, 30};
那這個10是怎么存儲的吶?  10表示的是0x0A 00 00 00
首先是申請12個字節內存空間,畢竟一個int數據占用的是四個字節,一個地接就需要一個地址存儲。
- 字節 0(低地址):`00001010` → `0x0A`
- 字節 1:`00000000` → `0x00`
- 字節 2:`00000000` → `0x00`
- 字節 3(高地址):`00000000` → `0x00`
這里其實我們經常說的一個字節,指的就是內存空間中的一個地址,對的就是一個實實在在的物理地址。

所以12個字節,其實就是需要12個實實在在的物理地址空間做支撐。

如果是該數組是指針數組,其實數組存儲的原本的10=0x0A 00 00 00此時存儲的是一個地址,畢竟地址也是一個16進制數,所以也只是將這個地址拆分,然后存儲到數組申請的實實在在的物理空間。例如

int a = 10, b = 20;
int* ptr_arr[2] = {&a, &b}; 
// ptr_arr[0]存儲a的地址(如0x2000),ptr_arr[1]存儲b的地址(如0x2004)
`&a`=`0x1000`32 位系統:地址值占 ?**4 字節**?(如 `0x00001000`)  也是四個字節。
雖然我們存儲的是地址,但是有點類似于int數據類型的存儲。

至此我們明白了數組存內容是怎么儲存的,

接下來再看指針:

指針也是申請內存空間存儲內容:存儲的是地址,不會存儲數據。

定義一個指針變量,其實本質也是在物料地址空間,申請位置,但是需要告訴編譯器我們是什么地址,畢竟內容不一樣需要申請的空間不一樣,但是需要注意的是這個指針變量如果是存儲函數或者是數組,僅僅只存儲它們的入口地址或者是首地址。

如果我們把這個這個函數存在數組中,其實也只是存儲的這個函數的入口地址,但是我們需要將這個數組類型進行聲明,因為只有這樣編譯器才知道我們使用的這個數組存儲的是一個指針數組。

從某種程度來說指針和數組沒有區別,但是又存在一些細微的區別。
數組更像是一種數據集合,固定大小,類型一致。
地址更像是一個地址容器,動態指向。
并且他們訪問形式是不一樣的。

3.2 繼續分析源碼

繼續言歸正傳分析按鍵開源框架:

接下來就是分析按鍵的狀態機跳轉,也就是如何判斷按鍵狀態的核心邏輯。

/*** @brief  Button driver core function, driver state machine* @param  handle: the button handle struct* @retval None*/static void button_handler(Button* handle)
{uint8_t read_gpio_level = button_read_level(handle);// Increment ticks counter when not in idle stateif (handle->state > BTN_STATE_IDLE) {handle->ticks++;}/*------------Button debounce handling---------------*/if (read_gpio_level != handle->button_level) {// Continue reading same new level for debounceif (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {handle->button_level = read_gpio_level;handle->debounce_cnt = 0;}} else {// Level not changed, reset counterhandle->debounce_cnt = 0;}/*-----------------State machine-------------------*/switch (handle->state) {case BTN_STATE_IDLE:if (handle->button_level == handle->active_level) {// Button press detectedhandle->event = (uint8_t)BTN_PRESS_DOWN;EVENT_CB(BTN_PRESS_DOWN);handle->ticks = 0;handle->repeat = 1;handle->state = BTN_STATE_PRESS;} else {handle->event = (uint8_t)BTN_NONE_PRESS;}break;case BTN_STATE_PRESS:if (handle->button_level != handle->active_level) {// Button releasedhandle->event = (uint8_t)BTN_PRESS_UP;EVENT_CB(BTN_PRESS_UP);handle->ticks = 0;handle->state = BTN_STATE_RELEASE;} else if (handle->ticks > LONG_TICKS) {// Long press detectedhandle->event = (uint8_t)BTN_LONG_PRESS_START;EVENT_CB(BTN_LONG_PRESS_START);handle->state = BTN_STATE_LONG_HOLD;}break;case BTN_STATE_RELEASE:if (handle->button_level == handle->active_level) {// Button pressed againhandle->event = (uint8_t)BTN_PRESS_DOWN;EVENT_CB(BTN_PRESS_DOWN);if (handle->repeat < PRESS_REPEAT_MAX_NUM) {handle->repeat++;}EVENT_CB(BTN_PRESS_REPEAT);handle->ticks = 0;handle->state = BTN_STATE_REPEAT;} else if (handle->ticks > SHORT_TICKS) {// Timeout reached, determine click typeif (handle->repeat == 1) {handle->event = (uint8_t)BTN_SINGLE_CLICK;EVENT_CB(BTN_SINGLE_CLICK);} else if (handle->repeat == 2) {handle->event = (uint8_t)BTN_DOUBLE_CLICK;EVENT_CB(BTN_DOUBLE_CLICK);}handle->state = BTN_STATE_IDLE;}break;case BTN_STATE_REPEAT:if (handle->button_level != handle->active_level) {// Button releasedhandle->event = (uint8_t)BTN_PRESS_UP;EVENT_CB(BTN_PRESS_UP);if (handle->ticks < SHORT_TICKS) {handle->ticks = 0;handle->state = BTN_STATE_RELEASE;  // Continue waiting for more presses} else {handle->state = BTN_STATE_IDLE;  // End of sequence}} else if (handle->ticks > SHORT_TICKS) {// Held down too long, treat as normal presshandle->state = BTN_STATE_PRESS;}break;case BTN_STATE_LONG_HOLD:if (handle->button_level == handle->active_level) {// Continue holdinghandle->event = (uint8_t)BTN_LONG_PRESS_HOLD;EVENT_CB(BTN_LONG_PRESS_HOLD);} else {// Released from long presshandle->event = (uint8_t)BTN_PRESS_UP;EVENT_CB(BTN_PRESS_UP);handle->state = BTN_STATE_IDLE;}break;default:// Invalid state, reset to idlehandle->state = BTN_STATE_IDLE;break;}
}

逐段代碼進行分析:

static void button_handler(Button* handle)
{}
    uint8_t read_gpio_level = button_read_level(handle);

我們首先要依據GPIO口的高低電平來進行按鍵的判斷按鍵是否按下,因此首先就要將GPIO讀取函數給獲取到,因為在初始化按鍵結構體的時候我們已經裝填了獲取GPIO電平的函數,這個按鍵的結構體包含了按鍵判斷所有的條件,包括但不限于按鍵事件、按鍵狀態、按鍵電平、按鍵什么電平有效等等,前面已經解釋過了,這里就不一一贅述了,只是簡單提醒,要帶著這種思想。并且這些信息都存在了函數的輸入參數里面 handle 并且每一個按鍵的這些時間都是獨立計算的。按鍵與按鍵之間分開計算。

    // Increment ticks counter when not in idle stateif (handle->state > BTN_STATE_IDLE) {handle->ticks++;}

這個時間的遞增是怎么實現的?
通俗來講,如果是 5ms 一次調用這個函數,那就可以理解為 5ms 變量 ticks 就會增加1,以此類推,從而實現了時間累計,并將這個是時間應用到下面代碼的邏輯跳轉。
這里在定時器還需要解決一個問題:定時精度依賴?:若 button_ticks() 調用間隔不穩定(如被高優先級中斷阻塞),會導致時間計算誤差。也就是在滴答定時器章節留下的問題。
ARM單片機滴答定時器理解與應用(一)(詳細解析)-CSDN博客

ARM單片機滴答定時器理解與應用(二)(詳細解析)(完)-CSDN博客

接著是按鍵軟件消抖動:

    /*------------Button debounce handling---------------*/if (read_gpio_level != handle->button_level) {// Continue reading same new level for debounceif (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {handle->button_level = read_gpio_level;handle->debounce_cnt = 0;}} else {// Level not changed, reset counterhandle->debounce_cnt = 0;}

這要結合按鍵結構體的初始化,在初始化的時候我們將 handle->button_level 初始化為有效電平的相反數,

    handle->button_level = !active_level;  // initialize to opposite of active level

假設active_level?:表示按鍵按下時的有效邏輯電平(例如 0 表示低電平觸發)。
并且假設系統啟動時按鍵處于釋放狀態,也就是按鍵沒有按下,此時物理電平應與有效電平相反(例如:若 active_level=0,則釋放時應為高電平 1)。

避免首次掃描的誤觸發?

  • 若初始化時 button_levelactive_level 相同(例如均為 0),則首次調用 button_handler 時:
read_gpio_level == handle->button_level  // 均為0 → 電平“未變化”

即使按鍵實際處于釋放狀態(物理電平應為 1),框架也會誤判為“按鍵已按下”,導致狀態機錯誤觸發 PRESS_DOWN 事件。

確保消抖機制正確啟動?

  • 當按鍵首次被按下時,物理電平從釋放狀態(1)變為按下狀態(0),此時:
read_gpio_level (0) != handle->button_level (1)  // 電平變化 → 啟動消抖計數

若未初始化 button_level = !active_level,首次按下可能因電平“未變化”而跳過消抖計數,直接誤判為穩定狀態。

經過上述的初始化分析:保證了我們的按鍵函數可以準確的進入到消抖動分析,

整體邏輯如下:
A[檢測電平變化] --> B{連續N次相同?}
B – 是 --> C[更新button_level]
B – 否 --> D[重置計數器]

體現在代碼就是:

        if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {handle->button_level = read_gpio_level;handle->debounce_cnt = 0;}

首先進行次數判斷,為何必須用前置 ++ 而非后置?

  • 邏輯一致性?:消抖需在連續 DEBOUNCE_TICKS 次變化后立即響應,前置 ++ 確保本次掃描被計入后立刻判斷。
  • ?避免漏判?:后置 ++ 會延遲計數更新,導致本次掃描的檢測結果未被納入當前判斷。

假設連續三次穩定了,那么我們就認為電平穩定了,覆蓋掉初始化的結果,將當前檢測的結果賦值給 handle->button_level = read_gpio_level; 這樣在下一次掃描就不會再執行這個函數,然后將消抖的計數器給清空。

接著就是狀態機的跳轉:

    case BTN_STATE_IDLE:if (handle->button_level == handle->active_level) {// Button press detectedhandle->event = (uint8_t)BTN_PRESS_DOWN;EVENT_CB(BTN_PRESS_DOWN);handle->ticks = 0;handle->repeat = 1;handle->state = BTN_STATE_PRESS;} else {handle->event = (uint8_t)BTN_NONE_PRESS;}break;

因為前面已經將電平更新為實際檢測到的 handle->button_level = read_gpio_level; 之所以這樣是因為消抖完成,所以在狀態滿足 if 語句 handle->button_level == handle->active_level 就是說我檢測到的電平和有效電平是一致的,那就是有效按鍵,然后開始清空相關內容,并將狀態設置為按鍵按下 handle->state = BTN_STATE_PRESS; 在消抖的時候按鍵事件一直是 handle->event = (uint8_t)BTN_NONE_PRESS;,按鍵狀態一直是空閑狀態(等待按下) handle->state = BTN_STATE_IDLE;

handle->ticks = 0; 清空的時機需要多留心一下。

然后接著跳轉到:

    default:// Invalid state, reset to idlehandle->state = BTN_STATE_IDLE;break;

在消抖過程形成完美閉環。


文章源碼獲取方式:
如果您對本文的源碼感興趣,歡迎在評論區留下您的郵箱地址。我會在空閑時間整理相關代碼,并通過郵件發送給您。由于個人時間有限,發送可能會有一定延遲,請您耐心等待。同時,建議您在評論時注明具體的需求或問題,以便我更好地為您提供針對性的幫助。

【版權聲明】
本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議。這意味著您可以自由地共享(復制、分發)和改編(修改、轉換)本文內容,但必須遵守以下條件:
署名:您必須注明原作者(即本文博主)的姓名,并提供指向原文的鏈接。
相同方式共享:如果您基于本文創作了新的內容,必須使用相同的 CC 4.0 BY-SA 協議進行發布。

感謝您的理解與支持!如果您有任何疑問或需要進一步協助,請隨時在評論區留言。

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

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

相關文章

web前端渡一大師課 CSS屬性計算過程

你是否了解CSS 的屬性計算過程呢? <body> <h1>這是一個h1標題</h1> </body> 目前我們沒有設置改h1的任何樣式,但是卻能看到改h1有一定的默認樣式,例如有默認的字體大小,默認的顏色 那么問題來了,我們這個h1元素上面除了有默認字體大小,默認顏色等…

Redis高頻面試題:利用I/O多路復用實現高并發

Redis 通過 I/O 多路復用&#xff08;I/O Multiplexing&#xff09;技術實現高并發&#xff0c;這是其單線程模型能夠高效處理大量客戶端連接的關鍵。以下是通俗易懂的解釋&#xff0c;結合 Redis 的工作原理&#xff0c;詳細說明其實現過程。 1. 什么是 I/O 多路復用&#xff…

爬蟲小知識(二)網頁進行交互

一、提交信息到網頁 1、模塊核心邏輯 “提交信息到網頁” 是網絡交互關鍵環節&#xff0c;借助 requests 庫的 post() 函數&#xff0c;能模擬瀏覽器向網頁發數據&#xff08;如表單、文件 &#xff09;&#xff0c;實現信息上傳&#xff0c;讓我們能與網頁背后的服務器 “溝通…

WPF學習(五)

文章目錄一、FileStream和StreamWriter理解1.1、具體關系解析1.2、類比理解1.3、總結1.4、示例代碼1.5、 WriteLine()和 Write&#xff08;&#xff09;的區別1.6、 StreamWriter.Close的作用二、一、FileStream和StreamWriter理解 在 C# 中&#xff0c;StreamWriter 和 FileS…

ctf.show-web習題-web2-最簡單的sql注入-flag獲取詳解、總結

解題思路打開靶場既然提示是最簡單的sql注入了&#xff0c;那么直接嘗試永真登錄1 or 11#這里閉合就是簡單的單引號可以看到沒登錄成功&#xff0c;但是有回顯&#xff1a;歡迎你&#xff0c;ctfshowsql注入最喜歡的就是回顯了&#xff01;這題的思路就是靠這個回顯&#xff0c…

upload-labs 靶場通關(1-20)

目錄 Pass-01(JS 繞過) Pass-02(文件類型驗證) Pass-03(黑名單驗證) Pass-04(黑名單驗證.htaccess) Pass-05(大小寫繞過) Pass-06(末尾空格) Pass-07(增加一個.) Pass-08(增加一個::$DATA) Pass-09&#xff08;代碼不嚴謹&#xff09; Pass-10&#xff08;PPHPHP&am…

[附源碼+數據庫+畢業論文]基于Spring+MyBatis+MySQL+Maven+vue實現的酒店預訂管理系統,推薦!

摘 要 使用舊方法對酒店預訂信息進行系統化管理已經不再讓人們信賴了&#xff0c;把現在的網絡信息技術運用在酒店預訂信息的管理上面可以解決許多信息管理上面的難題&#xff0c;比如處理數據時間很長&#xff0c;數據存在錯誤不能及時糾正等問題。 這次開發的酒店預訂管理系…

LSTM入門案例(時間序列預測)| pytorch實現(可復現)

需求 假如我有一個時間序列&#xff0c;例如是前113天的價格數據&#xff08;訓練集&#xff09;&#xff0c;然后我希望借此預測后30天的數據&#xff08;測試集&#xff09;&#xff0c;實際上這143天的價格數據都已經有了。這里為了簡單&#xff0c;每一天的數據只有一個價…

Axure RP 10 預覽顯示“無標題文檔”的空白問題探索【護航版】

1. 安裝情況 官網 Axure RP 10&#xff1a;Download Axure RP 10 - Axure &#xff08;PS&#xff1a;11都出了&#xff09; 版本&#xff1a;10.0.0.3924 激活碼&#xff1a;49bb9513c40444b9bcc3ce49a7a022f9 &#xff08;10/11都可以用&#xff0c;但只嘗試了10&#xff…

基于SpringBoot+Vue的汽車租賃系統(協同過濾算法、騰訊地圖API、支付寶沙盒支付、WebsSocket實時聊天、ECharts圖形化分析)

系統亮點&#xff1a;協同過濾算法、騰訊地圖API、支付寶沙盒支付、WebsSocket實時聊天、ECharts圖形化分析&#xff1b;01系統開發工具與環境搭建—前后端分離架構項目架構&#xff1a;B/S架構運行環境&#xff1a;win10/win11、jdk17前端&#xff1a;技術&#xff1a;框架Vue…

數據結構入門:像整理收納一樣簡單!

在我們生活中&#xff0c;經常會面對這樣的問題&#xff1a; “我要怎么整理我的衣柜&#xff1f;” “電腦里照片太多了&#xff0c;怎么歸類才方便查找&#xff1f;” 其實&#xff0c;程序員也有類似的煩惱。他們不整理衣柜&#xff0c;而是“整理數據”。而這門關于如何“收…

力扣每日一題--2025.7.15

&#x1f4da; 力扣每日一題–2025.7.15 3135. 有效單詞 &#xff08;簡單&#xff09; 大家好&#xff01;今天我們要來聊聊一道有趣的編程題——有效單詞 &#x1f4dd; 題目描述 題目分析 &#x1f4da; 題目要求我們判斷一個字符串是否為有效單詞。有效單詞需要滿足以下…

Mysql數據庫——增刪改查CRUD

文章目錄一、數據庫的基礎命令二、創建表三、增(create)四、查詢&#xff08;retrieve)五、條件查詢&#xff08;where&#xff09;六、修改&#xff08;update&#xff09;七、刪除&#xff08;delete&#xff09;一、數據庫的基礎命令 1.使用客戶端連接服務器 mysql -u root…

關于pytorch虛擬環境及具體bug問題修改

本篇博客包含對于虛擬環境概念的講解和代碼實現過程中相關bug的解決關于虛擬環境我的pytorch虛擬環境在D盤&#xff0c;相應python解釋器也在D盤&#xff08;一起&#xff09;&#xff0c;但是我的pycharm中的項目在C盤&#xff0c;使用的是pytorch的虛擬環境&#xff0c;這是為…

U盤量產工具與性能優化完全指南

本文還有配套的精品資源&#xff0c;點擊獲取 簡介&#xff1a;U盤量產工具是IT行業中的專業軟件&#xff0c;用于批量生產或修復U盤。安國和銀燦是兩個提供U盤量產工具的主控芯片制造商&#xff0c;提供初始化、格式化、分區管理、性能優化、故障修復、個性化定制、固件升級…

Golang http開發實戰:構建RESTful API保姆級教程

目錄 章節1:RESTful API的精髓與Go的Web開發哲學 RESTful API的設計原則 Go的http包核心組件 實戰:第一個RESTful API端點 章節2:設計優雅的RESTful路由 路由設計的注意事項 使用Gorilla Mux實現動態路由 章節3:請求與響應的藝術:解析與格式化 解析請求數據 統一…

UGUI 性能優化系列:第一篇——基礎優化與資源管理

UGUI 性能優化系列&#xff1a;第一篇——基礎優化與資源管理 UGUI 性能優化系列&#xff1a;第二篇——Canvas 與 UI 元素管理 在 Unity 游戲中&#xff0c;用戶界面&#xff08;UI&#xff09;是玩家與游戲交互的核心。然而&#xff0c;不當的 UGUI 使用常常成為游戲性能的…

多端協同的招聘系統源碼開發指南:小程序+APP一體化設計

當下&#xff0c;很多企業選擇搭建屬于自己的多端協同招聘平臺&#xff0c;尤其是中大型人力資源公司、連鎖品牌企業&#xff0c;以及同城服務平臺&#xff0c;更是將“小程序APP”一體化招聘系統視為提升效率、降低用工成本的利器。 今天&#xff0c;筆者將從源碼開發的角度&a…

Maven 配置文件核心配置:本地倉庫、鏡像與 JDK 版本

Maven 配置文件核心配置&#xff1a;本地倉庫、鏡像與 JDK 版本 在 Maven 項目開發中&#xff0c;合理配置 settings.xml 文件能顯著提升依賴管理效率。本文將聚焦本地倉庫、鏡像加速和 JDK 版本這三個核心配置&#xff0c;結合 IDEA 環境詳細講解配置方法與作用。 一、Maven 配…

【時時三省】(C語言基礎)通過指針引用字符串

山不在高&#xff0c;有仙則名。水不在深&#xff0c;有龍則靈。 ----CSDN 時時三省如在printf函數中輸出一個字符串。這些字符串都是以直接形式&#xff08;字面形式&#xff09;給出的&#xff0c;在一對雙撇號中包含若干個合法的字符。使用字符串的更加靈活方便的方法——通…