動態內存管理2+柔性數組

一、動態內存經典筆試題分析

分析錯誤并改正

題目1

void GetMemory(char *p)
{p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}
int main()
{Test();return 0;
}

錯誤的原因:

1.str傳給p的時候(值傳遞),p是str的臨時拷貝,有自己獨立的空間,當GetMemory函數內部申請了空間后,地址放在p中時,str依然是NULL。當GetMemory函數返回之后,strcpy拷貝的時候,形成了非法訪問內存
2.在GetMemory函數內部,動態申請了內存,但是沒有釋放,會內存泄露
3.添加對 malloc 返回值的檢查,避免分配失敗時操作空指針。

改正(采用傳址調用):

void GetMemory(char** p)
{*p = (char *)malloc(100);
}
void Test(void)
{char *str = NULL;GetMemory(&str);// 傳遞指針的地址if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

或者返回動態分配的指針:

char* GetMemory()
{char* p = (char *)malloc(100);return p;//return (char*)malloc(100);
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){strcpy(str, "hello world");printf(str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

題目2

返回棧空間地址的問題

char *GetMemory(void)
{char p[] = "hello world";// 局部數組,存儲在棧內存return p;// 函數結束后,棧內存被釋放,p的地址失效
}
void Test(void)
{char *str = NULL;str = GetMemory();//函數返回后,局部變量 p 的內存被系統回收,但 str 仍指向該地址,形成懸掛指針。printf(str);// 可能輸出亂碼或崩潰
}
int main()
{Test();return 0;
}

錯誤的原因:

GetMemory 函數內部定義了一個局部數組 char p[] = "hello world";,并返回其指針。
問題:局部數組存儲在棧內存中,函數結束后棧內存被釋放,此時返回的指針指向無效內存(懸掛指針)。后續通過 str 訪問會導致未定義行為(如輸出亂碼或程序崩潰)。
懸空指針是指向已經被釋放或無效內存區域的指針。這種指針雖然保留了之前的內存地址,但地址中的內容可能已被系統回收或重新分配,訪問它會導致 未定義行為(如程序崩潰、數據錯誤、輸出亂碼等)。

改正:
1.返回字符串常量的指針:

char *GetMemory(void)
{char* p = "hello world";return p;// 字符串常量存儲在只讀區,生命周期為整個程序//return "hello world";
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}

2.使用動態內存分配:

char *GetMemory(void)
{char* p = (char*)malloc(strlen("hello world")+1);// +1 用于容納 '\0'if(p != NULL){strcpy(p,"hello world");}return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();if(str != NULL){printf("%s\n",str);}free(str);str = NULL;
}
int main()
{Test();return 0;
}

3.使用靜態變量:

char *GetMemory(void)
{static char p[] = "hello world";// 靜態變量生命周期為整個程序return p;
}
void Test(void)
{char *str = NULL;str = GetMemory();printf(str);
}
int main()
{Test();return 0;
}

題目3

void GetMemory(char **p, int num)
{*p = (char *)malloc(num);
}
void Test(void)
{char *str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}
int main()
{Test();return 0;
}

錯誤的原因:malloc的返回值沒有判斷并且存在內存泄露(沒有free)

改正:

void GetMemory(char **p, int num)
{*p = (char *)malloc(num);
}
void Test(void)
{char *str = NULL;GetMemory(&str, 100);if(str != NULL){strcpy(str, "hello");printf("%s\n",str);}free(str);str = NULL:
}
int main()
{Test();return 0;
}

題目4

void Test(void)
{char *str = (char *) malloc(100);//未對malloc返回值進行判斷strcpy(str, "hello");free(str);if(str != NULL)// str 未被置空,條件仍為真{strcpy(str, "world");// 操作已釋放的內存(未定義行為)printf(str);// 可能崩潰或輸出亂碼}
}
int main()
{Test();return 0;
}

錯誤的原因:未對malloc返回值進行判斷,str 未被置空,是一個懸空指針

改正:

void Test(void)
{char *str = (char *) malloc(100);if(str != NULL){strcpy(str, "hello");printf("%s\n", str);}free(str);str = NNULL;// 置空指針,避免誤用// 后續操作需確保指針有效if(str != NULL){// 此處代碼永遠不會執行strcpy(str, "world");printf(str);}
}
int main()
{Test();return 0;
}

二、柔性數組

C99中,結構中的最后?個元素允許是未知大小的數組,這就叫做『柔性數組』成員

struct st_type
{int i;int arr[0];//柔性數組成員
};

有些編譯器會報錯?法編譯可以改成:

struct st_type
{int i;int arr[];//柔性數組成員
};

2.1?柔性數組的特點

1.結構中的柔性數組成員前?必須至少?個其他成員。

2.sizeof返回的這種結構大小不包括柔性數組的內存。

3.包含柔性數組成員的結構用malloc()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。

struct st_type
{//1.結構中的柔性數組成員前?必須?少?個其他成員。int i;int arr[];//柔性數組成員
}type_a;
int main()
{//2.sizeof返回的這種結構??不包括柔性數組的內存。printf("%d\n", sizeof(type_a));//輸出的是4return 0;
}
//3.包含柔性數組成員的結構?malloc()函數進?內存的動態分配,并且分配的內存應該?于結構的??,以適應柔性數組的預期??。
struct S
{int i;char arr[];
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S) + 10*sizeof(char));if(ps == NULL){return 1;}ps->i = 100;printf("%d\n", ps->i);int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}for(i = 0;i < 10; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q}printf("\n");//增容struct S* ptr = (struct S*)realloc(ps,sizeof(struct S) + 20*sizeof(char));if(ptr != NULL){ps = ptr;}else{perror("realloc");return 1;}for(i = 10;i < 20; i++){ps->arr[i] = 'A';}for(i = 0;i < 20; i++){printf("%c ",ps->arr[i]);//Q Q Q Q Q Q Q Q Q Q A A A A A A A A A A}free(ps);ps = NULL;return 0;
}

柔性數組成員?malloc()函數進?內存的動態分配后獲得空間,對結構體進行計算大小,還是不會算進去,只算除了柔性數組外的大小

printf("%d\n", sizeof(struct S));//4

2.2?柔性數組的優勢

1.方便內存釋放

2.這樣有利于訪問速度

代碼一:

struct S
{int i;char arr[];
};
int main()
{int i = 0;struct S* p = (struct S*)malloc(sizeof(struct S) + 100 * sizeof(char));//業務處理 p->i = 100;for (i = 0; i < 100; i++){p->arr[i] = i;}free(p);p = NULL;return 0;
}

1.malloc一次 ? ? ?2.free一次 ? ? ?3.空間連續

代碼二:

struct S
{int i;char* arr;
};
int main()
{struct S* ps = (struct S*)malloc(sizeof(struct S));if(ps == NULL){perror("malloc");return 1;} ps->i = 100;ps->arr = (char*)malloc(sizeof(char) * 10);if(ps->arr == NULL){perror("malloc->arr");return 1;}//使用int i = 0;for(i = 0;i < 10; i++){ps->arr[i] = 'Q';}//釋放free(ps->arr);ps->arr = NULL;free(ps);ps = NULL;return 0;
}

1.malloc兩次 ? ? ?2.free兩次 ? ? ?3.內存空間不連續(內存碎片多,浪費空間)

三、內存區域劃分

C/C++程序內存分配的幾個區域:

1. 棧區(stack):在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時 這些存儲單元?動被釋放。棧內存分配運算內置于處理器的指令集中,效率很?,但是分配的內 存容量有限。棧區主要存放運?函數?分配的局部變量、函數參數、返回數據、返回地址等。

2. 堆區(heap):?般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。分配? 式類似于鏈表。

3. 數據段(靜態區):(static)存放全局變量、靜態數據。程序結束后由系統釋放。

4. 代碼段:存放函數體(類成員函數和全局函數)的?進制代碼。

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

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

相關文章

AI寫PPT可以用嗎?我測試了3款AI寫PPT工具,分享感受

上周五臨下班&#xff0c;領導突然讓我周末趕出一份季度營銷報告 PPT&#xff0c;還要求周一晨會展示。看著空蕩蕩的 PPT 頁面&#xff0c;我滿心都是絕望 —— 周末不僅泡湯&#xff0c;搞不好還得熬夜到凌晨。好在同部門的前輩給我推薦了幾款 AI 寫 PPT 工具&#xff0c;沒想…

PrimeVul論文解讀-如何構建高質量漏洞標簽與數據集

目錄 1. 引入2. 現有漏洞識別方案的不足2.1 數據集中label不準2.2 數據重復2.3 測評標準不夠好 3. 現有漏洞識別數據集分析3.1 關于現有數據集中label的準確率分析3.2 關于現有數據集中數據泄露&#xff08; Data Leakage&#xff09;情況分析 4. 漏洞識別測評5. PrimeVul數據集…

關于數據湖和數據倉的一些概念

一、前言 隨著各行業數字化發展的深化,數據資產和數據價值已越來越被深入企業重要發展的戰略重心,海量數據已成為多數企業生產實際面臨的重要問題,無論存儲容量還是成本,可靠性都成為考驗企業數據治理的考驗。本文來看下海量數據存儲的數據湖和數據倉,數據倉庫和數據湖,…

linux-----------------庫制作與原理(下)

1.ELF文件 要理解編譯鏈鏈接的細節&#xff0c;我們不得不了解?下ELF?件。其實有以下四種?件其實都是ELF?件&#xff1a; ? 可重定位?件&#xff08;Relocatable File &#xff09; &#xff1a;即 xxx.o ?件。包含適合于與其他?標?件鏈接來創 建可執??件或者共享…

python-爬蟲基礎

爬蟲本質&#xff1a;通過編寫程序來獲取到互聯網上的資源。 我們的程序本質上就是模擬瀏覽器 一個簡單的小爬蟲&#xff1a; 只需要三步&#xff1a; from urllib.request import urlopen #url是網址&#xff0c;request意思是請求 這里跑出來的中文是這樣的注意看&#…

單元化架構

目錄 ????????編輯 單元化 邏輯單元 單元化 多地多機房部署&#xff0c;是互聯網系統的必然發展方向&#xff0c;一個系統要走到這一步&#xff0c;也就必然要解決上面提到的問題&#xff1a;流量調配、數據拆分、延時等。業界有很多技術方案可以用來解決這些問題&…

【免殺】C2免殺技術(五)動態API

一、什么是動態API 在C2免殺領域中&#xff0c;“動態API” 主要指的是繞過靜態檢測的一種技術手段&#xff0c;其本質是運行時動態解析和調用Windows API函數&#xff0c;而不是在程序編譯階段就明確引用這些API。這種方式可以有效躲避靜態分析工具和殺軟的簽名識別。 為什么…

Python爬蟲實戰:研究JavaScript壓縮方法實現逆向解密

一、引言 在數字化信息爆炸的時代,網絡數據已成為驅動各行業發展的核心資產。Python 憑借其豐富的庫生態和簡潔的語法,成為網絡爬蟲開發的首選語言。然而,隨著互聯網安全防護機制的不斷升級,網站普遍采用 JavaScript 壓縮與混淆技術保護其核心邏輯和數據傳輸,這使得傳統爬…

HTTP 請求走私(HTTP Request Smuggling)

HTTP 請求走私&#xff08;HTTP Request Smuggling&#xff09;是一種通過利用前端代理&#xff08;如負載均衡器、CDN&#xff09;和后端服務器在 解析 HTTP 請求時存在不一致性 的漏洞&#xff0c;從而實現 注入惡意請求 的攻擊技術。 一、基本原理 HTTP 請求走私主要依賴兩…

【Google機器學習實踐指南(線性回歸篇)

&#x1f50d; Google機器學習實踐指南&#xff08;線性回歸篇&#xff09; Google機器學習實戰(3)-單變量線性回歸核心解析&#xff0c;掌握房價預測模型 一、建模流程全景圖 ▲ 四大核心步驟&#xff1a; 數據可視化→特征工程→模型訓練→預測推理 二、房價預測實戰 1. …

python打卡day16

NumPy 數組基礎 因為前天說了shap&#xff0c;這里涉及到數據形狀尺寸問題&#xff0c;所以需要在這一節說清楚&#xff0c;后續的神經網絡我們將要和他天天打交道。 知識點&#xff1a; numpy數組的創建&#xff1a;簡單創建、隨機創建、遍歷、運算numpy數組的索引&#xff1a…

ubuntu 20.04 更改國內鏡像源-阿里源 確保可用

鏡像源是跟linux版本一一對應的,查詢自己系統的版本號&#xff1a; 命令&#xff1a;lsb_release -a macw:~$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.6 LTS Release: 20.04 Codename: focal macw:~$…

基于OpenCV的SIFT特征和FLANN匹配器的指紋認證

文章目錄 引言一、概述二、代碼解析1. 圖像顯示函數2. 核心認證函數2.1 創建SIFT特征提取器2.2 檢測關鍵點和計算描述符&#xff08;源圖像&#xff09;2.3 檢測關鍵點和計算描述符&#xff08;模板圖像&#xff09;2.4 創建FLANN匹配器2.5 使用K近鄰匹配 3. 匹配點篩選4. 認證…

四品種交易策略

策略概述 策略思路: 交易品種:同時交易四個品種,每個品種使用總資金的10%。 合約選擇:使用連續合約(data0)發出交易信號,實際交易 主力合約(data1)和下一個主力合約(data2)。 資金管理:總資金用A_CurrentEquity表示,交易手數據此計算。 止損執行:盤中達到止損…

MySQL事務的一些奇奇怪怪知識

Gorm事務有error卻不返回會發生什么 Gorm包是大家比較高頻使用。正常的用法是&#xff0c;如果有失敗返回error&#xff0c;整體rollback&#xff0c;如果不返回error則commit。下面是Transaction的源碼&#xff1a; // Transaction start a transaction as a block, return …

時序數據庫、實時數據庫與實時數倉:如何為實時數據場景選擇最佳解決方案?

隨著物聯網、金融交易、在線游戲等場景對實時數據處理需求的增長&#xff0c;市場上涌現出多種專門針對實時數據處理的數據庫解決方案。然而&#xff0c;面對時序數據庫、實時數據庫和實時數據倉庫這三種看似相似的技術&#xff0c;許多技術決策者常常感到困惑&#xff1a;它們…

Spring3+Vue3項目中的知識點——JWT

全稱&#xff1a;JOSN Web Token 定義了一種簡潔的、自包含的格式&#xff0c;用于通信雙方以json數據格式的安全傳輸信息 組成&#xff1a; 第一部分&#xff1a;Header&#xff08;頭&#xff09;&#xff0c;記錄令牌類型、簽名算法等。 第二部分&#xff1a;Payload&am…

微服務架構詳解

微服務架構詳解:從概念到實踐(附代碼案例) 目錄 微服務架構詳解:從概念到實踐(附代碼案例) 一、微服務架構概述 1.1 什么是微服務? 1.2 微服務的核心思想 二、微服務架構的優勢與挑戰 2.1 優勢 2.2 挑戰 三、微服務架構的核心組件 3.1 服務注冊與發現 示例代…

linux下編寫shell腳本一鍵編譯源碼

0 前言 進行linux應用層編程時&#xff0c;經常會使用重復的命令對源碼進行編譯&#xff0c;然后把編譯生成的可執行文件拷貝到工作目錄&#xff0c;操作非常繁瑣且容易出錯。本文編寫一個簡單的shell腳本一鍵編譯源碼。 1 linux下編寫shell腳本一鍵編譯源碼 shell腳本如下&…

學習!FastAPI

目錄 FastAPI簡介快速開始安裝FastApiFastAPI CLI自動化文檔 Reqeust路徑參數Enum 類用于路徑參數路徑參數和數值校驗 查詢參數查詢參數和字符串校驗 請求體多個請求體參數嵌入單個請求體參數 CookieHeader表單文件直接使用請求 ResponseResponse Model多個關聯模型 響應狀態碼…