c語言輸出11258循環,c/c++內存機制(一)(轉)

一:C語言中的內存機制

在C語言中,內存主要分為如下5個存儲區:

(1)棧(Stack):位于函數內的局部變量(包括函數實參),由編譯器負責分配釋放,函數結束,棧變量失效。

(2)堆(Heap):由程序員用malloc/calloc/realloc分配,free釋放。如果程序員忘記free了,則會造成內存泄露,程序結束時該片內存會由OS回收。

(3)全局區/靜態區(Global Static Area):?全局變量和靜態變量存放區,程序一經編譯好,該區域便存在。并且在C語言中初始化的全局變量和靜態變量和未初始化的放在相鄰的兩個區域(在C++中,由于全局變量和靜態變量編譯器會給這些變量自動初始化賦值,所以沒有區分了)。由于全局變量一直占據內存空間且不易維護,推薦少用。程序結束時釋放。

(4)C風格字符串常量存儲區:?專門存放字符串常量的地方,程序結束時釋放。

(5)程序代碼區:存放程序二進制代碼的區域。

二:C++中的內存機制

在C++語言中,與C類似,不過也有所不同,內存主要分為如下5個存儲區:

(1)棧(Stack):位于函數內的局部變量(包括函數實參),由編譯器負責分配釋放,函數結束,棧變量失效。

(2)堆(Heap):這里與C不同的是,該堆是由new申請的內存,由delete或delete[]負責釋放

(3)自由存儲區(Free Storage):由程序員用malloc/calloc/realloc分配,free釋放。如果程序員忘記free了,則會造成內存泄露,程序結束時該片內存會由OS回收。

(4)全局區/靜態區(Global Static Area): 全局變量和靜態變量存放區,程序一經編譯好,該區域便存在。在C++中,由于全局變量和靜態變量編譯器會給這些變量自動初始化賦值,所以沒有區分了初始化變量和未初始化變量了。由于全局變量一直占據內存空間且不易維護,推薦少用。程序結束時釋放。

(5)常量存儲區: 這是一塊比較特殊的存儲區,專門存儲不能修改的常量(如果采用非正常手段更改當然也是可以的了)。

三:堆和棧的區別

3.1 棧(Stack)

具體的講,現代計算機(馮諾依曼串行執行機制),都直接在代碼低層支持棧的數據結構。這體現在有專門的寄存器指向棧所在的地址(SS,堆棧段寄存器,存放堆棧段地址);有專門的機器指令完成數據入棧出棧的操作(匯編中有PUSH和POP指令)。

這種機制的特點是效率高,但支持數據的數據有限,一般是整數、指針、浮點數等系統直接支持的數據類型,并不直接支持其他的數據結構(可以自定義棧結構支持多種數據類型)。因為棧的這種特點,對棧的使用在程序中是非常頻繁的 。對子程序的調用就是直接利用棧完成的。機器的call指令里隱含了把返回地址入棧,然后跳轉至子程序地址的操作,而子程序的ret指令則隱含從堆棧中彈出返回地址并跳轉之的操作。

C/C++中的函數自動變量就是直接使用棧的例子,這也就是為什么當函數返回時,該函數的自動變量自動失效的原因,因而要避免返回棧內存和棧引用,以免內存泄露。

3.2 堆(Heap)

和棧不同的是,堆得數據結構并不是由系統(無論是機器硬件系統還是操作系統)支持的,而是由函數庫提供的。基本的malloc/calloc/realloc/free函數維護了一套內部的堆數據結構(在C++中則增加了new/delete維護)。

當程序用這些函數去獲得新的內存空間時,這套函數首先試圖從內部堆中尋找可用的內存空間(常見內存分配算法有:首次適應算法、循環首次適應算法、最佳適應算法和最差適應算法等。os的基本內容!!)。如果沒有可用的內存空間,則試圖利用系統調用來動態增加程序數據段的內存大小,新分配得到的空間首先被組織進內部堆中去,然后再以適當的形式返回給調用者。當程序釋放分配的內存空間時,這片內存空間被返回到內部堆結構中,可能會被適當的處理(比如空閑空間合并成更大的空閑空間),以更適合下一次內存分配申請。 這套復雜的分配機制實際上相當于一個內存分配的緩沖池(Cache),使用這套機制有如下幾個原因:

(1)系統調用可能不支持任意大小的內存分配。有些系統的系統調用只支持固定大小及其倍數的內存請求(按頁分配);這樣的話對于大量的小內存分配來說會造成浪費。

(2)系統調用申請內存可能是代價昂貴的。 系統調用可能涉及到用戶態和核心態的轉換。

(3)沒有管理的內存分配在大量復雜內存的分配釋放操作下很容易造成內存碎片。

3.3 棧和堆的對比

從以上介紹中,它們有如下區別:

(1)棧是系統提供的功能,特點是快速高效,缺點是由限制,數據不靈活;

堆是函數庫提供的功能,特點是靈活方便,數據適應面廣,但是效率有一定降低。

(2)棧是系統數據結構,對于進程/線程是唯一的;

堆是函數庫內部數據結構,不一定唯一,不同堆分配的內存無法互相操作。

(3)棧空間分靜態分配和動態分配,一般由編譯器完成靜態分配,自動釋放,棧的動態分配是不被鼓勵的;

堆得分配總是動態的,雖然程序結束時所有的數據空間都會被釋放回系統,但是精確的申請內存/釋放內存匹配是良好程序的基本要素。

(4)碎片問題

對于堆來講,頻繁的new/delete等操作勢必會造成內存空間的不連續,從而造成大量的碎片,使程序的效率降低;對于棧來講,則不會存在這個問題,因為棧是后進先出(LIFO)的隊列。

(5)生長方向

堆的生長方向是向上的,也就是向這內存地址增加的方向;對于棧來講,生長方向卻是向下的,是向著內存地址減少的方向增長。

(6)分配方式

堆都是動態分配的,沒有靜態分配的堆;

棧有兩種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,比如局部變量的分配。動態分配則由alloca函數進行分配,但是棧的動態分配和堆不同,它的動態分配是由編譯器進行釋放,無需我們手工實現。

(7)分配效率

棧是機器系統提供的數據結構,計算機在底層提供支持,分配有專門的堆棧段寄存器,入棧出棧有專門的機器指令,這些都決定了棧的高效率執行。

堆是由C/C++函數庫提供的,機制比較復雜,有不同的分配算法,易產生內存碎片,需要對內存進行各種管理,效率比棧要低很多。

四:具體實例分析

例子(一)

看下面的一小段C程序,仔細體會各種內存分配機制。

ef5f243b23e0b8ab1dcdb3ab50e771f6.gif

int a = 0; //全局初始化區,a的值為0

char *p1; //全局未初始化區(C++中則初始化為NULL) intmain() { int b; //b分配在棧上,整型 char s[] = "abc"; //s分配在棧上,char *類型;"abc\0"分配在棧上,運行時賦值,函數結束銷毀 char *p2; //p2分配在棧上,未初始化 char *p3 = "123456"; //p3指向"123456"分配在字符串常量存儲區的地址,編譯時確定 static int c = 0; //c在全局(靜態)初始化區,可以多次跨函數調用而保持原值 p1 = (char *)malloc(10); //p1在全局未初始化區,指向分配得來得10字節的堆區地址 p2 = (char *)malloc(20); //p2指向分配得來得20字節的堆區地址 strcpy(p1, "123456"); //"123456"放在字符串常量存儲區,編譯器可能會將它與p3所指向的"123456"優化成一塊 return 0; }

4dcc3ce362d35c305ed21d3f2be6a970.gif

例子(二)

看下面的一小段代碼,體會堆與棧的區別:

cf1c60e16d0d9c2eec6664a397127b43.gif

intfoo()

{

//其余代碼

int *p = new int[5]; //其余代碼 return 0; }

95c691ccf12e7c39860f86275d62d8e7.gif

其中的語句int *p = new int[5];就包含了堆與棧。其中new關鍵字分配了一塊堆內存,而指針p本身所占得內存為棧內存(一般4個字節表示地址)。這句話的意思是在棧內存中存放了一個指向一塊堆內存的指針p。在程序中先確定在堆中分配內存的大小,然后調用new關鍵字分配內存,最后返回這塊內存首址,放入棧中。匯編代碼為:

51a0e738b0397882de2a74f9bcd5c8de.gif

intfoo()

{

008C1520 push ebp

008C1521 mov ebp,esp

008C1523 sub esp,0D8h

008C1529 push ebx

008C152A push esi

008C152B push edi

008C152C lea edi,[ebp-0D8h]

008C1532 mov ecx,36h

008C1537 mov eax,0CCCCCCCCh

008C153C rep stos dword ptr es:[edi]

int *p = new int[5]; 008C153E push 14h 008C1540 call operator new[] (8C1258h) 008C1545 add esp,4008C1548 mov dword ptr [ebp-0D4h],eax 008C154E mov eax,dword ptr [ebp-0D4h] 008C1554 mov dword ptr [p],eax return 0; 008C1557 xor eax,eax } 008C1559 pop edi 008C155A pop esi 008C155B pop ebx 008C155C add esp,0D8h 008C1562 cmp ebp,esp 008C1564 call @ILT+395(__RTC_CheckEsp) (8C1190h) 008C1569 mov esp,ebp 008C156B pop ebp 008C156C ret

cd1f834856372abff1ef4ef909d82251.gif

如果需要釋放內存,這里我們需要使用delete[] p,告訴編譯器,我要刪除的是一個數組。

例子(三)

看下面的一小段代碼,試著找出其中的錯誤:

7aacfc212cca4bee7b9c86375d7c7c87.gif

#include

using namespacestd;

intmain() { char a[] = "Hello"; //分配在棧上 a[0] = 'X'; cout << a <

51b4f9c0c72513ba40aa323fbbca588f.gif

發現問題了嗎?是的,字符數組a的容量是6個字符,其內容為"hello\0"。a的內容時可以改變的,比如a[0]='X',因為其是在棧上分配的,也就是在運行時確定的內容。但是指針p指向的字符串"world"分配在字符串常量存儲區,內容為"world\0",常量字符串的內容時不可以修改的。從語法上來說,編譯器并不覺得語句p[0]='X'有什么問題,但是在運行時則會出現"access violation"非法內存訪問的問題。

以下幾個函數的變化要看清楚了:

ca83d8940d824ffc74491c06bb1c93a9.png

7f0c945651cacfa32551df9ae82b7ce2.gif

char *GetString1(void)

{

char p[] = "hello,world"; //結果:h。由于數組指針指向第一元素的地址,所以調用之后是h returnp; } char *GetString2(void) { char *p = "hello,world"; //結果:hello,world。由于p指向“hello,world”字符串常量區域地址 returnp; } char *GetString3(void) { char *p = (char *)malloc(20); //指向p所分配的堆上的內存空間。 returnp; } char *GetString4(void) { char *p = new char[20]; //指向p所分配的內存空間,p本身在棧上的,p所指向的空間是堆上的。 returnp; }

fb71f6e0da35763773d2f04f3e881091.gif

附錄:內存管理注意事項

d36d0c9116342906ec7eda99b02e082e.png

【規則1】用malloc或new申請內存之后,應該立即檢查指針值是否為NULL,防止使用指針值為NULL的內存,可以在函數入口處斷言檢測。

【規則2】不要忘記為數組或動態內存賦初值(比如calloc比malloc就要好),指針初始化為NULL(c++中為0)。

【規則3】避免數組或指針下標越界,特別

d0053015efdd2bb166c31199cf43a80a.png當心發生“多1”或者"少1"

2221329ea698d6451219f86a3eb7208c.png的操作。

【規則4】動態內存的申請和釋放必須配對,防止內存泄露,具體為malloc/calloc/realloc和free配對,new和delete以及delete[]配對。

【規則5】用free或者delete釋放內存后,應立即將指針設置為NULL(C++中為0),防止產生“野指針”、"懸垂指針"。

【規則6】遇到不懂得問題及時debug,一般的蟲子

792158a84d8c3cfacf8770d17ccb03c8.pngdebug一下就灰飛煙滅了,一切bug都是浮云而已

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

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

相關文章

【神經網絡八股擴展】:數據增強

課程來源&#xff1a;人工智能實踐:Tensorflow筆記2 文章目錄前言TensorFlow2數據增強函數數據增強網絡八股代碼&#xff1a;總結前言 本講目標:數據增強&#xff0c;增大數據量 關于我們為何要使用數據增強以及常用的幾種數據增強的手法&#xff0c;可以看看下面的文章&#…

C++:從C繼承的標準庫

C從C繼承了的標準庫 &#xff0c; 這就意味著 C 中 可以使用的標準庫函數 在C 中都可以使用 &#xff0c; 但是需要注意的是 &#xff0c; 這些標準庫函數在C中不再以 <xxx.h> 命名 &#xff0c; 而是變成了 <cxxx> 。 例如 &#xff1a; 在C中操作字符串的…

分享WCF聊天程序--WCFChat

無意中在一個國外的站點下到了一個利用WCF實現聊天的程序&#xff0c;作者是&#xff1a;Nikola Paljetak。研究了一下&#xff0c;自己做了測試和部分修改&#xff0c;感覺還不錯&#xff0c;分享給大家。先來看下運行效果&#xff1a;開啟服務&#xff1a;客戶端程序&#xf…

c# uri.host_C#| 具有示例的Uri.Equality()運算符

c# uri.hostUri.Equality()運算符 (Uri.Equality() Operator) Uri.Equality() Operator is overloaded which is used to compare two Uri objects. It returns true if two Uri objects contain the same Uri otherwise it returns false. Uri.Equality()運算符已重載&#xf…

第六章至第九章的單元測試

1,?助劑與纖維作用力大于纖維分子之間的作用力,則該助劑最好用作() 纖維增塑膨化劑。 2,助劑擴散速率快,優先占領纖維上的染座,但助劑與纖維之間作用力小于染料與纖維之間作用力,該助劑可以作為() 勻染劑。 3,助劑占領纖維上的染座,但助劑與纖維之間作用力大于染…

【神經網絡擴展】:斷點續訓和參數提取

課程來源&#xff1a;人工智能實踐:Tensorflow筆記2 文章目錄前言斷點續訓主要步驟參數提取主要步驟總結前言 本講目標:斷點續訓&#xff0c;存取最優模型&#xff1b;保存可訓練參數至文本 斷點續訓主要步驟 讀取模型&#xff1a; 先定義出存放模型的路徑和文件名&#xff0…

開發DBA(APPLICATION DBA)的重要性

開發DBA是干什么的&#xff1f; 1. 審核開發人員寫的SQL&#xff0c;并且糾正存在性能問題的SQL ---非常重要 2. 編寫復雜業務邏輯SQL&#xff0c;因為復雜業務邏輯SQL開發人員寫出的SQL基本上都是有性能問題的&#xff0c;與其讓開發人員寫&#xff0c;不如DBA自己寫。---非常…

javascript和var之間的區別?

You can define your variables in JavaScript using two keywords - the let keyword and the var keyword. The var keyword is the oldest way of defining and declaring variables in JavaScript whereas the let is fairly new and was introduced by ES15. 您可以使用兩…

小米手環6NFC安裝太空人表盤

以前看我室友峰哥、班長都有手環&#xff0c;一直想買個手環&#xff0c;不舍得&#xff0c;然后今年除夕的時候降價&#xff0c;一狠心&#xff0c;入手了&#xff0c;配上除夕的打年獸活動還有看春晚京東敲鼓領的紅包和這幾年攢下來的京東豆豆&#xff0c;原價279的小米手環6…

計算機二級c語言題庫縮印,計算機二級C語言上機題庫(可縮印做考試小抄資料)...

小抄,答案,形成性考核冊,形成性考核冊答案,參考答案,小抄資料,考試資料,考試筆記第一套1.程序填空程序通過定義學生結構體數組&#xff0c;存儲了若干個學生的學號、姓名和三門課的成績。函數fun 的功能是將存放學生數據的結構體數組&#xff0c;按照姓名的字典序(從小到大排序…

為什么兩層3*3卷積核效果比1層5*5卷積核效果要好?

目錄1、感受野2、2層3 * 3卷積與1層5 * 5卷積3、2層3 * 3卷積與1層5 * 5卷積的計算量比較4、2層3 * 3卷積與1層5 * 5卷積的非線性比較5、2層3 * 3卷積與1層5 * 5卷積的參數量比較1、感受野 感受野&#xff1a;卷積神經網絡各輸出特征像素點&#xff0c;在原始圖片映射區域大小。…

算法正確性和復雜度分析

算法正確性——循環不變式 算法復雜度的計算 方法一 代換法 —局部代換 這里直接對n變量進行代換 —替換成對數或者指數的情形 n 2^m —整體代換 這里直接對遞推項進行代換 —替換成內部遞推下標的形式 T(2^n) S(n) 方法二 遞歸樹法 —用實例說明 —分析每一層的內容 —除了…

第十五章 Python和Web

第十五章 Python和Web 本章討論Python Web編程的一些方面。 三個重要的主題&#xff1a;屏幕抓取、CGI和mod_python。 屏幕抓取 屏幕抓取是通過程序下載網頁并從中提取信息的過程。 下載數據并對其進行分析。 從Python Job Board&#xff08;http://python.org/jobs&#x…

array_chunk_PHP array_chunk()函數與示例

array_chunkPHP array_chunk()函數 (PHP array_chunk() Function) array_chunk() function is an array function, it is used to split a given array in number of array (chunks of arrays). array_chunk()函數是一個數組函數&#xff0c;用于將給定數組拆分為多個數組(數組…

raise

raise - Change a windows position in the stacking order button .b -text "Hi there!"pack [frame .f -background blue]pack [label .f.l1 -text "This is above"]pack .b -in .fpack [label .f.l2 -text "This is below"]raise .b轉載于:ht…

c語言輸出最大素數,for語句計算輸出10000以內最大素數怎么搞最簡單??各位大神們...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓#include #include int* pt NULL; // primes_tableint pt_size 0; // primes_table 數量大小int init_primes_table(void){FILE* pFile;pFile fopen("primes_table.bin", "rb");if (pFile NULL) {fputs(&q…

【數據結構基礎筆記】【圖】

代碼參考《妙趣橫生的算法.C語言實現》 文章目錄前言1、圖的概念2、圖的存儲形式1、鄰接矩陣&#xff1a;2、鄰接表3、代碼定義鄰接表3、圖的創建4、深度優先搜索DFS5、廣度優先搜索BFS6、實例分析前言 本章總結&#xff1a;圖的概念、圖的存儲形式、鄰接表定義、圖的創建、圖…

第十六章 測試基礎

第十六章 測試基礎 在編譯型語言中&#xff0c;需要不斷重復編輯、編譯、運行的循環。 在Python中&#xff0c;不存在編譯階段&#xff0c;只有編輯和運行階段。測試就是運行程序。 先測試再編碼 極限編程先鋒引入了“測試一點點&#xff0c;再編寫一點點代碼”的理念。 換而…

如何蹭網

引言蹭網&#xff0c;在普通人的眼里&#xff0c;是一種很高深的技術活&#xff0c;總覺得肯定很難&#xff0c;肯定很難搞。還沒開始學&#xff0c;就已經敗給了自己的心里&#xff0c;其實&#xff0c;蹭網太過于簡單。我可以毫不夸張的說&#xff0c;只要你會windows的基本操…

android對象緩存,Android簡單實現 緩存數據

前言1、每一種要緩存的數據都是有對應的versionCode&#xff0c;通過versionCode請求網絡獲取是否需要更新2、提前將要緩存的數據放入assets文件夾中&#xff0c;打包上線。緩存設計代碼實現/*** Created by huangbo on 2017/6/19.** 主要是緩存的工具類** 緩存設計&#xff1a…