嵌入式學習日志(十)

10? ?學習指針

1? 指針核心定義與本質

1.1 指針與指針變量

? ? ? ? 1、指針即地址,指針變量是存放地址的變量,其大小與操作系統位數相關:64 位系統中占 8 字節,32 位系統中占 4 字節。

? ? ? ? 2、指針的核心功能是通過地址間接訪問目標變量,實現靈活的內存操作。

1.2 指針類型的關鍵作用

? ? ? ? 1、承載地址信息與內存解析規則,基類型必須與指向數據類型嚴格一致。

? ? ? ? 2、決定內存訪問步長:char*偏移 1 字節,int*偏移 4 字節,double*偏移 8 字節。

1.3 核心要求

????????指針變量本身及指向的內存空間必須確定,嚴禁使用野指針(未初始化)或懸垂指針(指向已釋放空間)。

2? 指針的基本操作規則

2.1 地址獲取與傳遞

????????1、通過&(取地址運算符)可獲取變量在內存空間中的首地址,只有左值(可被賦值的變量)能進行&操作,常量、表達式和寄存器變量(register修飾)不能。

????????2、例如int Num = 0; &Num可得到Num在內存中 4 字節空間的首地址,其類型為int *(由基類型int升級而來)。

????????3、在函數傳參中,使用&傳遞變量地址,能實現在被調函數中修改主調函數的變量值,突破值傳遞 “單向傳遞” 的限制(典型應用:函數返回多個值)。

2.2 指針訪問與解引用

????????1、通過*運算符可獲得指針指向的空間或對應空間中的值,*連接的內容必須為指針類型(否則編譯報錯)。

? ? ? ? 2、若直接使用*對應的表達式,其值為指針指向空間中的值,類型為指針類型降級后的基類型,如int *p; *p的類型為int

? ? ? ? 3、變量有兩種訪問形式:直接訪問(通過變量名,如Num = 5)和間接訪問(通過指針,如*p = 5)。

3? 字符串函數與指針應用(基于指針實現的庫函數)

????????字符串操作函數的底層實現依賴指針對字符數組的遍歷與修改,核心是通過char*指針訪問內存中的字符序列(以'\0'結尾)。

函數功能核心實現邏輯關鍵點
strlen計算字符串長度指針遍歷至'\0',返回指針差值const char*保證只讀,不包含'\0'
strcpy復制字符串逐個字符復制(含'\0'需保證目標空間足夠,禁止地址重疊
strcat拼接字符串先移動指針至目標末尾,再復制源字符串目標需足夠大,源和目標均需'\0'結尾
strcmp比較字符串按 ASCII 值逐個比較,遇不同字符返回差值非長度比較,一旦不同立即返回

3.1?strlen:計算字符串長度

? ? ? ? 1、功能:返回字符串中'\0'前的字符個數(不包含'\0')。

? ? ? ? 2、底層實現:

size_t strlen(const char *str) 
{const char *p = str; // 用指針遍歷字符串while (*p != '\0') { // 遍歷至結束符p++;}return p - str; // 指針差值即長度(字符個數)
}

? ? ? ? 3、關鍵點:const char*保證不修改原字符串;通過指針自增遍歷,通過指針相減計算長度。

3.2?strcpy:字符串復制

? ? ? ? 1、功能:將源字符串(src)復制到目標空間(dest),包括'\0',返回目標地址。

? ? ? ? 2、底層實現:

char *strcpy(char *dest, const char *src) 
{char *p = dest; // 保存目標首地址(用于返回)while ((*p++ = *src++) != '\0'); // 逐個復制字符,包括'\0'return dest;
}

????????3、注意事項

????????????????需保證dest空間足夠大,否則會導致緩沖區溢出。

????????????????源字符串必須以'\0'結尾,否則會復制隨機數據。

????????????????禁止源地址與目標地址重疊(如strcpy(a, a+1)會導致未定義行為)。

3.3 strcat:字符串拼接

? ? ? ? 1、功能:將源字符串(src)追加到目標字符串(dest)的末尾(覆蓋dest原有的'\0'),返回目標地址。

? ? ? ? 2、底層實現:

char *strcat(char *dest, const char *src) 
{char *p = dest;// 1. 移動指針到dest的末尾('\0'位置)while (*p != '\0') {p++;}// 2. 復制src到dest末尾,同strcpy邏輯while ((*p++ = *src++) != '\0');return dest;
}

? ? ? ? 3、注意事項:

????????dest必須有足夠空間容納拼接后的字符串。

????????destsrc都必須以'\0'結尾。

3.4?strcmp:字符串比較

? ? ? ? 1、功能:按 ASCII 值比較兩個字符串,返回差值(0表示相等,正數表示str1大于str2,負數表示str1小于str2)。

? ? ? ? 2、底層實現:

int strcmp(const char *str1, const char *str2) 
{// 遍歷字符,直到遇到不同字符或'\0'while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2) {str1++;str2++;}// 返回對應字符的ASCII差值return (unsigned char)*str1 - (unsigned char)*str2;
}

? ? ? ? 3、關鍵點:

????????????????比較的是字符的 ASCII 值,而非字符串長度。

????????????????一旦遇到不同字符立即返回差值,不繼續比較后續字符。

4? 指針偏移與內存訪問

? ? ? ? 1、指針偏移大小由基類型大小決定:char *偏移 1 字節,int *偏移 4 字節,double *偏移 8 字節,結構體指針偏移整個結構體大小。

? ? ? ? 2、兩個同類型指針相減的結果為地址間相差的數據類型元素個數(非字節數),例如int *p1 = a; int *p2 = a+3; p2-p1結果為 3(表示相差 3 個int元素)。

? ? ? ? 3、指針不能與非指針類型運算,也不能跨類型相減(編譯報錯)。

5? 野指針與空指針

? ? ? ? 1、野指針成因:未經初始化的指針(如int *p;)、指向已釋放空間的指針(如free(p);后未置空)、越界訪問的指針(如數組越界)。

? ? ? ? 2、空指針:指向內存地址0x0的指針,用NULL(本質為(void*)0)表示,該空間為系統保留的只讀區域,對空指針解引用(*p = 10)會導致程序崩潰。

? ? ? ? 3、預防野指針:未使用的指針初始化為NULL;釋放內存后立即置空(free(p); p = NULL;);避免指針越界訪問。

6? 指針賦值與修改

? ? ? ? 1、對指針變量本身賦值(如p = q;):改變指針的指向,使其指向新的地址。

? ? ? ? 2、對指針解引用賦值(如*p = *q;):改變指針指向空間的值,指針指向不變。

? ? ? ? 3、示例:int a=1, b=2; int *p=&a, *q=&b; p=q;p指向b*p結果為 2;*p=*q;a的值變為 2,p仍指向a

7? 動態內存分配與管理

7.1 核心函數

函數功能特點
malloc(size_t size)申請size字節的連續內存未初始化,內容為隨機值;返回void*,需強轉;失敗返回NULL
calloc(size_t n, size_t size)申請nsize字節的連續內存自動初始化為 0;效率略低于malloc
realloc(void *ptr, size_t size)調整已分配內存的大小可能在原地址擴展或重新分配;失敗返回NULL,原內存不變
free(void *ptr)釋放動態分配的內存僅釋放ptr指向的空間,不改變指針值;不能重復釋放或釋放非動態內存

7.2 內存管理規則

? ? ? ? 1、動態內存必須手動釋放,否則導致內存泄漏(程序運行中內存占用持續增長)。

? ? ? ? 2、釋放后指針需置空(p = NULL;),避免成為野指針。

? ? ? ? 3、申請內存后必須檢查返回值是否為NULL(防止內存不足導致崩潰):

int *p = (int*)malloc(10*sizeof(int));
if (p == NULL) { /* 內存申請失敗處理 */ }

? ? ? ? 4、避免 “內存碎片”:頻繁申請和釋放小塊內存會導致內存碎片,可通過內存池優化。

8? 指向函數的指針與指針函數

類型定義特點
指針函數返回指針的函數(類型* 函數名(參數)不可返回局部變量地址,可返回動態內存、全局變量地址
函數指針指向函數的指針(返回類型 (*指針名)(參數列表)需匹配函數返回類型、參數類型和個數,用于回調函數(如qsort比較器)

8.1 指針函數

? ? ? ? 1、定義:返回值為指針的函數,格式為類型 *函數名(參數列表)

? ? ? ? 2、注意:不能返回局部變量的地址(局部變量在函數結束后釋放,返回后成為野指針),可返回動態分配內存、全局變量或靜態變量的地址。

? ? ? ? 3、示例:

int *createArray(int n) 
{int *arr = (int*)malloc(n*sizeof(int));return arr; // 返回動態內存地址,需外部釋放
}

8.2 函數指針

? ? ? ? 1、定義:指向函數的指針,格式為返回類型 (*指針名)(參數類型列表),需嚴格匹配函數的返回類型、參數類型和個數。

? ? ? ? 2、函數名本質是函數入口地址,可直接賦值給函數指針(無需&)。

? ? ? ? 3、應用:實現回調函數(如排序函數qsort的比較器)、函數接口封裝,降低模塊耦合性。

? ? ? ? 4、示例:

int add(int a, int b) 
{return a + b; 
}
int (*funcPtr)(int, int) = add; // 函數指針指向add
int result = funcPtr(3, 4); // 調用函數,結果為7

9? 指針與數組的關系

9.1數組名的特殊性

? ? ? ? 1、數組名是指向首元素的指針常量(不可修改指向),如int a[5]; a等價于&a[0],類型為int *

? ? ? ? 2、例外情況:

????sizeof(數組名):獲得數組總字節數(如int a[5]; sizeof(a) = 20)。

????&數組名:類型為指向整個數組的指針(如int (*)[5]),偏移量為整個數組大小。

? ? ? ? ? ? ? ?數組名作為sizeof參數或取地址時,不退化為首元素指針。

9.2 數組作為函數參數

? ? ? ? 1、三種傳遞形式等價:int fun(int a[5]);int fun(int a[]);int fun(int *a);,函數內均按指針處理,丟失數組長度信息。

? ? ? ? 2、必須顯式傳遞數組長度:int fun(int *a, int len),避免越界訪問。

9.3 字符數組與字符串

? ? ? ? 1、字符串本質是'\0'結尾的字符數組,傳參時可直接傳遞數組名(即char *指針)。

? ? ? ? 2、遍歷字符串:while (*p != '\0') { printf("%c", *p++); }

? ? ? ? 3、字符串常量存儲在只讀區,不能通過指針修改(如char *p = "hello"; *p = 'H';會崩潰)。

10? const 指針

10.1 三種形式及特性

定義含義指針值是否可改指向空間是否可改必須初始化
const int *p;?或?int const *p;const 修飾*p
int *const p;const 修飾p
const int *const p;?或?int const *const p;const 修飾p*p

10.2 應用場景

? ? 1、const int *p:保護指向的數據不被修改(如函數參數傳遞只讀數據)。

? ? 2、int *const p:確保指針始終指向同一變量(如硬件寄存器地址)。

? ? 3、const int *const p:既固定指針指向,又保護數據(如常量配置參數)。

11? 指針數組與數組指針

11.1 概念區分

類型本質定義形式內存占用(64 位)示例
指針數組數組,元素為指針int *a[5];5×8=40 字節char *strs[] = {"apple", "banana"};
數組指針指針,指向數組int (*a)[5];8 字節int (*p)[3] = &arr;arrint[3]數組)

11.2數組指針與二維數組

? ? ? ? 1、二維數組名是指向第一行的數組指針,類型為int (*)[列數],如int a[2][3];a的類型為int (*)[3]

? ? ? ? 2、訪問元素的方式:a[i][j]*(a[i] + j)*(*(a + i) + j)(*(a + i))[j]

? ? ? ? 3、遍歷二維數組:

int a[2][3] = {{1,2,3}, {4,5,6}};
int (*p)[3] = a; // p指向第一行
for (int i=0; i<2; i++) 
{for (int j=0; j<3; j++) {printf("%d ", *(*(p+i) + j));}
}

12? 二級指針

12.1 定義與本質

? ? ? ? 1、二級指針是指向一級指針變量的指針,格式為類型 **p,64 位系統中占 8 字節,用于存儲一級指針的地址。

? ? ? ? 2、示例:int a=10; int *p=&a; int **q=&p;q是二級指針,*q等價于p**q等價于a

12.2 使用場景

? ? ? ? 1、函數內部修改外部指針的指向:

void allocMemory(int **p, int size) 
{*p = (int*)malloc(size); // 修改外部指針p的指向
}
int *arr;
allocMemory(&arr, 10*sizeof(int)); // 傳遞一級指針的地址

? ? ? ? 2、指針數組傳參:指針數組的數組名是二級指針,如char *strs[] = {"a", "b"};傳參時類型為char **

? ? ? ? 3、動態二維數組:int **arr = (int**)malloc(3*sizeof(int*));用于創建行長度可變的二維數組。

13? 指針作為函數參數

13.1 傳遞方式對比

傳遞方式特點適用場景
值傳遞形參是實參的副本,修改形參不影響實參函數僅使用參數值,不修改原變量
地址傳遞(一級指針)形參指向實參地址,可修改實參的值函數需修改原變量的值(如交換兩個變量)
二級指針傳遞形參指向一級指針的地址,可修改一級指針的指向函數需為外部指針分配內存或改變其指向

13.2典型應用:交換兩個變量

void swap(int *a, int *b) 
{int temp = *a;*a = *b;*b = temp;
}
int x=1, y=2;
swap(&x, &y); // 調用后x=2, y=1

14? 學習總結

1、野指針操作:對未初始化、已釋放或越界的指針解引用,導致程序崩潰或數據損壞。
解決:初始化指針為NULL,釋放后立即置空,避免越界。

2、類型不匹配:用char*指針訪問int變量(如char *p = (char*)&a;)可能導致數據截斷或解析錯誤。
解決:嚴格保證指針類型與指向數據類型一致。

3、內存泄漏:動態分配的內存未釋放,長期運行導致系統內存耗盡。
解決:遵循 “誰申請誰釋放” 原則,使用智能指針(C++)或內存池管理。

4、重復釋放內存:對同一指針多次調用free,導致內存管理混亂。

????????解決:釋放后立即置空,釋放前檢查是否為NULL

5、字符串函數使用錯誤

? ? ? ? (1)使用strcpy時目標空間不足,導致緩沖區溢出(如char dest[3]; strcpy(dest, "hello");)。
解決:使用strncpy限制復制長度,或確保目標空間足夠。

? ? ? ? (2)對非'\0'結尾的字符序列使用strlen,導致遍歷越界(如未初始化的字符數組)。
解決:確保字符串以'\0'結尾,或手動指定長度。

6、const 指針違規操作:對const int *p嘗試修改指向空間的值(*p = 5),編譯報錯。
解決:明確const修飾的對象,避免違規修改。

7、混淆指針數組與數組指針:錯誤定義(如int (*a)[5]寫成int *a[5])導致內存訪問錯誤。
解決:記住優先級:[]高于*,數組指針需加括號。

8、返回局部變量地址:函數返回棧上變量的地址(如int *func() { int a=1; return &a; }),返回后地址失效。
解決:返回全局變量、靜態變量或動態分配內存的地址。

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

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

相關文章

Anaconda創建環境報錯:CondaHTTPEFTOT: HTTP 403 FORBIDDEN for url

一、快速解決方案這類報錯的原因通常是由于 conda 無法訪問鏡像源或權限被服務器拒絕&#xff0c;以下是常見原因和對應的解決方案&#xff1a;檢查鏡像源拼寫是否正確conda config --show channels清華源鏡像示例如果不正確&#xff0c;先清除舊配置del %USERPROFILE%\.condar…

亞馬遜地址關聯暴雷:新算法下的賬號安全保衛戰

2025年Q3&#xff0c;上千個店鋪因共享稅代地址、海外倉信息重疊等問題被批量凍結&#xff0c;為行業敲響了“精細化合規”的警鐘。事件復盤&#xff1a;地址成為關聯風控的“致命開關”稅代機構違規引發“多米諾效應”事件的導火索指向稅代機構“saqibil”&#xff0c;其為降低…

在本地環境中運行 ‘dom-distiller‘ GitHub 庫的完整指南

在本地環境中運行 ‘dom-distiller’ GitHub 庫的完整指南 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家&#xff0c;覺得好請收藏。點擊跳轉到網站。 1. 項目概述 ‘dom-distiller’ 是一個用于將網頁…

11. isaacsim4.2教程-Transform 樹與Odometry

1. 前言學習目標在本示例中&#xff0c;你將學習如何&#xff1a;使用 TF 發布器將相機作為 TF 樹的一部分發布在 TF 上發布機械臂&#xff0f;可動結構&#xff08;articulation&#xff09;的樹狀結構發布里程計&#xff08;Odometry&#xff09;消息開始之前前置條件已完成 …

安寶特新聞丨安寶特與Logivations正式建立合作伙伴關系,共筑物流新未來

近日&#xff0c;安寶特與物流創新企業Logivations簽署合作協議&#xff0c;雙方將深度融合技術專長&#xff0c;共同為客戶提供高效、精準的智能物流解決方案&#xff0c;助力企業實現從人工巡檢到智能管控的跨越式升級。 關于Logivations Logivations是一家深耕物流與供應鏈…

第三階段—8天Python從入門到精通【itheima】-139節(pysqark實戰-前言介紹)

目錄 139節——pysqark實戰-前言介紹 1.學習目標 2.spark是什么 3.如下是詳細介紹 PySpark 的兩種使用方式&#xff0c;并提供具體的代碼示例【大數據應用開發比賽的代碼熟悉如潮水一般沖刷我的記憶】&#xff1a; 一、本地模式&#xff08;作為 Python 第三方庫使用&#…

redis數據庫的四種取得 shell方法

Redis作為高性能內存數據庫&#xff0c;若配置不當&#xff08;特別是未授權訪問&#xff09;&#xff0c;將面臨極高安全風險。攻擊者可利用漏洞實現遠程代碼執行&#xff08;GetShell&#xff09;&#xff0c;嚴重威脅數據安全與服務器控制權。本文深入剖析此類漏洞的核心原理…

墨者:SQL過濾字符后手工繞過漏洞測試(萬能口令)

1. 墨者學院&#xff1a;SQL過濾字符后手工繞過漏洞測試(萬能口令)&#x1f680; 2. 漏洞背景分析&#x1f50d; 近期發現某登錄系統存在SQL注入漏洞&#xff0c;攻擊者可通過構造特殊用戶名admin,a,a)#繞過身份驗證。本文將深入解析其工作原理&#xff0c;并演示完整滲透測試流…

Kafka 順序消費實現與優化策略

在 Apache Kafka 中&#xff0c;實現順序消費需要從 Kafka 的架構和特性入手&#xff0c;因為 Kafka 本身是分布式的消息系統&#xff0c;默認情況下并不完全保證全局消息的順序消費&#xff0c;但可以通過特定配置和設計來實現局部或完全的順序消費。以下是實現 Kafka 順序消費…

CSP-J 2022_第三題邏輯表達式

題目 邏輯表達式是計算機科學中的重要概念和工具&#xff0c;包含邏輯值、邏輯運算、邏輯運算優先級等內容。 在一個邏輯表達式中&#xff0c;元素的值只有兩種可能&#xff1a;0&#xff08;表示假&#xff09;和 1&#xff08;表示真&#xff09;。元素之間有多種可能的邏輯運…

從釋永信事件看“積善“與“積惡“的人生辯證法

博客目錄起心動念皆是因&#xff0c;當下所受皆是果。"起心動念皆是因&#xff0c;當下所受皆是果。"這句古老的智慧箴言&#xff0c;在少林寺方丈釋永信涉嫌違法被調查的事件中得到了令人唏噓的印證。一位本應六根清凈、持戒修行的佛門領袖&#xff0c;卻深陷貪腐丑…

圖片格式轉換

文章目錄 背景目標實現下載 背景 格式碎片化問題 行業標準差異&#xff1a;不同領域常用格式各異&#xff08;如設計界用PSD/TIFF&#xff0c;網頁用JPG/PNG/WEBP&#xff0c;系統圖標用ICO/ICNS&#xff09;。 設備兼容性&#xff1a;老舊設備可能不支持WEBP&#xff0c;專業…

Flutter實現Android原生相機拍照

方法1&#xff1a;使用Flutter的camera插件&#xff08;完整實現&#xff09; 1. 完整依賴與權限配置 # pubspec.yaml dependencies:flutter:sdk: fluttercamera: ^0.10.52path_provider: ^2.0.15 # 用于獲取存儲路徑path: ^1.8.3 # 用于路徑操作permission_handler:…

記錄幾個SystemVerilog的語法——隨機

1. 隨機穩定性(random stability)隨機穩定性是指每個線程(thread)或對象(object)的random number generator(RNG)是私有的&#xff0c;一個線程返回的隨機值序列與其他線程或對象的RNG是無關的。隨機穩定性適用于以下情況&#xff1a;系統隨機方法調用&#xff1a;$urandom()和…

初識 docker [下] 項目部署

項目部署Dockerfile構建鏡像DockerCompose基本語法基礎命令項目部署 前面我們一直在使用別人準備好的鏡像&#xff0c;那如果我要部署一個Java項目&#xff0c;把它打包為一個鏡像該怎么做呢&#xff1f; …省略一萬字 站在巨人的肩膀上更適合我們普通人,所以直接介紹兩種簡單…

Android15廣播ANR的源碼流程分析

Android15的廣播ANR源碼流程跟了下實際代碼的流程&#xff0c;大概如下哈&#xff1a;App.sendBroadcast() // 應用發起廣播→ AMS.broadcastIntentWithFeature() // 通過Binder IPC進入system_server進程→ AMS.broadcastIntentLocked() // 權限校驗廣播分類&#xff08;前…

密碼學中的概率論與統計學:從頻率分析到現代密碼攻擊

在密碼學的攻防博弈中&#xff0c;概率論與統計學始終是破解密碼的“利器”。從古典密碼時期通過字母頻率推測凱撒密碼的密鑰&#xff0c;到現代利用線性偏差破解DES的線性密碼分析&#xff0c;再到側信道攻擊中通過功耗數據的統計特性還原密鑰&#xff0c;統計思維貫穿了密碼分…

力扣刷題977——有序數組的平方

977. 有序數組的平方 題目&#xff1a; 給你一個按 非遞減順序 排序的整數數組 nums&#xff0c;返回 每個數字的平方 組成的新數組&#xff0c;要求也按 非遞減順序 排序。示例 1&#xff1a; 輸入&#xff1a;nums [-4,-1,0,3,10] 輸出&#xff1a;[0,1,9,16,100] 解釋&…

應用加速游戲盾的安全作用

在數字娛樂產業蓬勃發展的今天&#xff0c;游戲已從單純的娛樂工具演變為連接全球數十億用戶的社交平臺與文化載體。然而&#xff0c;伴隨游戲市場的指數級增長&#xff0c;網絡攻擊的頻率與復雜性也呈爆發式上升。從DDoS攻擊導致服務器癱瘓&#xff0c;到外掛程序破壞公平競技…

linux安裝zsh,oh-my-zsh,配置zsh主題及插件的方法

這是一份非常詳細的指南&#xff0c;帶你一步步在 Linux 系統中安裝 Zsh、配置主題和安裝插件。 Zsh&#xff08;Z Shell&#xff09;是一個功能強大的 Shell&#xff0c;相比于大多數 Linux 發行版默認的 Bash&#xff0c;它提供了更強的自定義能力、更智能的自動補全、更漂亮…