日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C/C++学习之路_七: 内存管理

發(fā)布時(shí)間:2024/4/11 c/c++ 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++学习之路_七: 内存管理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

C/C++學(xué)習(xí)之路_七: 內(nèi)存管理


目錄

  • 作用域
  • 內(nèi)存布局
  • 內(nèi)存分區(qū)代碼分析

  • 1. 作用域

  • C語言變量的作用域分為:
  • 代碼塊作用域(代碼塊是{}之間的一段代碼)
  • 函數(shù)作用域
  • 文件作用域
  • 1. 局部變量

  • 局部變量也叫auto自動(dòng)變量(auto可寫可不寫),一般情況下代碼塊{}內(nèi)部定義的變量都是自動(dòng)變量,它有如下特點(diǎn):
  • 在一個(gè)函數(shù)內(nèi)定義,只在函數(shù)范圍內(nèi)有效
  • 在復(fù)合語句中定義,只在復(fù)合語句中有效
  • 隨著函數(shù)調(diào)用的結(jié)束或復(fù)合語句的結(jié)束局部變量的聲明聲明周期也結(jié)束
  • 如果沒有賦初值,內(nèi)容為隨機(jī)
  • void test() {//auto寫不寫是一樣的,auto只能出現(xiàn)在{}內(nèi)部auto int b = 10; }int main(void) {//b = 100; //err, 在main作用域中沒有bif (1) {//在復(fù)合語句中定義,只在復(fù)合語句中有效int a = 10;printf("a = %d\n", a);}//a = 10; //err離開if()的復(fù)合語句,a已經(jīng)不存在return 0; }

    2. 靜態(tài)局部變量

  • static局部變量的作用域也是在定義的函數(shù)內(nèi)有效
  • static局部變量的生命周期和程序運(yùn)行周期一樣,執(zhí)行main函數(shù)之前就已經(jīng)開辟空間,程序結(jié)束之后才釋放空間,同時(shí)staitc局部變量的值只初始化一次,但可以賦值多次
  • static局部變量若未賦以初值,則由系統(tǒng)自動(dòng)賦值:數(shù)值型變量自動(dòng)賦初值0,字符型變量賦空字符
  • void fun1() {int i = 0;i++;printf("i = %d\n", i); }void fun2() {//靜態(tài)局部變量,沒有賦值,系統(tǒng)賦值為0,而且只會(huì)初始化一次static int a;a++;printf("a = %d\n", a); }int main(void) {fun1();fun1();fun2();fun2();return 0; }

    3. 全局變量

  • 在函數(shù)外定義,可被本文件及其它文件中的函數(shù)所共用,若其它文件中的函數(shù)調(diào)用此變量,須用extern聲明
  • 全局變量的生命周期和程序運(yùn)行周期一樣,執(zhí)行main函數(shù)之前就已經(jīng)開辟空間,程序結(jié)束之后才釋放空間
  • 不同文件的全局變量不可重名
  • 4. 靜態(tài)全局變量

  • 在函數(shù)外定義,作用范圍被限制在所定義的文件中
  • 不同文件靜態(tài)全局變量可以重名,但作用域不沖突
  • static全局變量的生命周期和程序運(yùn)行周期一樣,同時(shí)staitc全局變量的值只初始化一次,執(zhí)行main函數(shù)之前就已經(jīng)開辟空間,程序結(jié)束之后才釋放空間
  • 5. extern全局變量聲明

  • extern int a; //聲明一個(gè)變量,這個(gè)全局變量在別的文件中已經(jīng)定義了,這里只是聲明,而不是定義。
  • 6. 全局函數(shù)和靜態(tài)函數(shù)

  • 在C語言中函數(shù)默認(rèn)都是全局的,使用關(guān)鍵字static可以將函數(shù)聲明為靜態(tài),函數(shù)定義為static就意味著這個(gè)函數(shù)只能在定義這個(gè)函數(shù)的文件中使用,在其他文件中不能調(diào)用,即使在其他文件中聲明這個(gè)函數(shù)都沒用。
  • 對(duì)于不同文件中的staitc函數(shù)名字可以相同。
  • 注意:
  • 允許在不同的函數(shù)中使用相同的變量名,它們代表不同的對(duì)象,分配不同的單元,互不干擾。
  • 同一源文件中,允許全局變量和局部變量同名,在局部變量的作用域內(nèi),全局變量不起作用。
  • 所有的函數(shù)默認(rèn)都是全局的,意味著所有的函數(shù)都不能重名,但如果是staitc函數(shù),那么作用域是文件級(jí)的,所以不同的文件static函數(shù)名是可以相同的。
  • 7. 總結(jié)

    類型作用域生命周期
    auto變量一對(duì){}內(nèi)當(dāng)前函數(shù)
    static局部變量一對(duì){}內(nèi)整個(gè)程序運(yùn)行期
    extern變量整個(gè)程序整個(gè)程序運(yùn)行期
    static全局變量當(dāng)前文件整個(gè)程序運(yùn)行期
    extern函數(shù)整個(gè)程序整個(gè)程序運(yùn)行期
    static函數(shù)當(dāng)前文件整個(gè)程序運(yùn)行期
    register變量一對(duì){}內(nèi)當(dāng)前函數(shù)
    全局變量整個(gè)程序整個(gè)程序運(yùn)行期

    2. 內(nèi)存布局

    1. 內(nèi)存分區(qū)

  • 程序沒有加載到內(nèi)存前,可執(zhí)行程序內(nèi)部已經(jīng)分好3段信息,分別為代碼區(qū)(text)、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)3 個(gè)部分,有些人直接把data和bss合起來叫做靜態(tài)區(qū)或全局區(qū)。
  • 1. 代碼區(qū)

  • 文本段也稱代碼段,進(jìn)程啟動(dòng)時(shí)會(huì)將程序的代碼加載到物理內(nèi)存中,文本段映射到這片物理內(nèi)存,這片內(nèi)存是不可以在運(yùn)行期間修改的。
  • 通常代碼區(qū)是可共享的(即另外的執(zhí)行程序可以調(diào)用它),使其可共享的目的是對(duì)于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可。
  • 代碼區(qū)通常是只讀的,使其只讀的原因是防止程序意外地修改了它的指令。
  • 另外,代碼區(qū)還規(guī)劃了局部變量的相關(guān)信息。
  • 2. 已初始化數(shù)據(jù)段(data段)

  • 已初始化數(shù)據(jù)段包含了在程序中明確被初始化的全局變量、已經(jīng)初始化的靜態(tài)變量(包括全局靜態(tài)變量和局部靜態(tài)變量)和常量數(shù)據(jù)(如字符串常量)。
  • 在程序真正運(yùn)行前就已經(jīng)確定的數(shù)據(jù),所以可以提前加載到內(nèi)存保存好,生存周期為整個(gè)程序運(yùn)行過程。
  • 需注意的是,數(shù)據(jù)段不是只讀的,在運(yùn)行時(shí)變量值是可以變動(dòng)的。
  • 數(shù)據(jù)段還可以更細(xì)的分為初始化只讀區(qū)以及初始化可讀寫區(qū)。舉例:
  • 全局字符串 char s[] = “hello world”,全局變量 int debug=1,靜態(tài)變量 static int i = 10 存儲(chǔ)在初始化可讀寫區(qū);
  • 另一種情況下,const char* string = “hello world”,字符串“hello world”存儲(chǔ)在初始化只讀區(qū),string指針則存在初始化可讀寫區(qū)。
  • 3. 未初始化數(shù)據(jù)區(qū)(又叫 bss 區(qū))

  • 未初始化數(shù)據(jù)區(qū)BSS存儲(chǔ)未初始化的全局變量和靜態(tài)變量,這些變量的值是在程序真正運(yùn)行起來并賦值后才能確定的,所以程序加載的時(shí)候,只需要記錄它的內(nèi)存地址和所需大小。。
  • 程序在加載到內(nèi)存前,代碼區(qū)和全局區(qū)(data和bss)的大小就是固定的,程序運(yùn)行期間不能改變。
  • 然后,運(yùn)行可執(zhí)行程序,系統(tǒng)把程序加載到內(nèi)存,除了根據(jù)可執(zhí)行程序的信息分出代碼區(qū)(text)、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)之外,還額外增加了棧區(qū)、堆區(qū)。
  • 舉例:變量 static int i; 全局變量 int j; 包含在BBS段中
  • 4. 堆(heap)

  • 堆用于運(yùn)行時(shí)內(nèi)存分配,在內(nèi)存中位于BSS區(qū)和棧區(qū)之間。
  • 堆用于存儲(chǔ)那些生存期與函數(shù)調(diào)用無關(guān)的數(shù)據(jù)。比如系統(tǒng)調(diào)用 malloc 申請(qǐng)的內(nèi)存就分配在堆上,這些申請(qǐng)的內(nèi)存在不需要時(shí)需要手動(dòng)釋放,否則會(huì)出現(xiàn)內(nèi)存泄漏。堆在程序結(jié)束時(shí)由操作系統(tǒng)回收。
  • 堆中內(nèi)容是匿名的,只能通過指針間接訪問。
  • 堆空間被進(jìn)程內(nèi)所有的共享庫(kù)及動(dòng)態(tài)加載模塊所共享。
  • 5. 棧(stack)

    棧是一個(gè)由內(nèi)核動(dòng)態(tài)調(diào)整的內(nèi)存段落,由棧幀(Stack Frames)組成,進(jìn)程每調(diào)用一個(gè)函數(shù),都將為該函數(shù)分配一個(gè)棧幀,棧幀中保存了該函數(shù)的局部變量、參數(shù)值和返回值,棧幀會(huì)在函數(shù)返回時(shí)清理掉。
    在程序運(yùn)行過程中實(shí)時(shí)的加載和釋放,因此,局部變量的生存周期為申請(qǐng)到釋放該段棧空間。
    4. 棧存儲(chǔ)著自動(dòng)變量以及每次函數(shù)調(diào)用時(shí)保存的信息,每當(dāng)函數(shù)被調(diào)用時(shí),返回地址以及調(diào)用者的上下文環(huán)境例如一些機(jī)器寄存器都存儲(chǔ)在棧中,新的被調(diào)用函數(shù)此時(shí)會(huì)在棧上重新分配自動(dòng)或者臨時(shí)變量,這就是遞歸函數(shù)的工作原理。
    5. 每當(dāng)函數(shù)遞歸調(diào)用自己時(shí),都會(huì)使用新的棧幀,因此當(dāng)前函數(shù)實(shí)體內(nèi)的棧幀內(nèi)的變量不會(huì)影響另外一個(gè)函數(shù)實(shí)體內(nèi)的變量。
    棧主要有三個(gè)用途:
    為函數(shù)內(nèi)部聲明的非靜態(tài)局部變量(局部變量)提供存儲(chǔ)空間。
    記錄函數(shù)調(diào)用過程相關(guān)的維護(hù)性信息,稱為棧幀或過程活動(dòng)記錄
    臨時(shí)存儲(chǔ)區(qū),用于臨時(shí)存放長(zhǎng)算術(shù)表達(dá)式部分計(jì)算結(jié)果或alloca()函數(shù)分配的棧內(nèi)內(nèi)存。

    2. 存儲(chǔ)類型總結(jié)

    類型作用域生命周期存儲(chǔ)位置
    auto變量一對(duì){}內(nèi)當(dāng)前函數(shù)棧區(qū)
    static局部變量一對(duì){}內(nèi)整個(gè)程序運(yùn)行期初始化在data段,未初始化在BSS段
    extern變量整個(gè)程序整個(gè)程序運(yùn)行期初始化在data段,未初始化在BSS段
    static全局變量當(dāng)前文件整個(gè)程序運(yùn)行期初始化在data段,未初始化在BSS段
    extern函數(shù)整個(gè)程序整個(gè)程序運(yùn)行期代碼區(qū)
    static函數(shù)當(dāng)前文件整個(gè)程序運(yùn)行期代碼區(qū)
    register變量一對(duì){}內(nèi)當(dāng)前函數(shù)運(yùn)行時(shí)存儲(chǔ)在CPU寄存器
    字符串常量當(dāng)前文件整個(gè)程序運(yùn)行期data段
    int e; static int f; int g = 10; static int h = 10;int main() {int a;int b = 10;static int c;static int d = 10;char *i = "test";char *k = NULL;printf("&a\t %p\t //局部未初始化變量\n", &a);printf("&b\t %p\t //局部初始化變量\n", &b);printf("&c\t %p\t //靜態(tài)局部未初始化變量\n", &c);printf("&d\t %p\t //靜態(tài)局部初始化變量\n", &d);printf("&e\t %p\t //全局未初始化變量\n", &e);printf("&f\t %p\t //全局靜態(tài)未初始化變量\n", &f);printf("&g\t %p\t //全局初始化變量\n", &g);printf("&h\t %p\t //全局靜態(tài)初始化變量\n", &h);printf("i\t %p\t //只讀數(shù)據(jù)(文字常量區(qū))\n", i);k = (char *) malloc(10);printf("k\t %p\t //動(dòng)態(tài)分配的內(nèi)存\n", k);return 0; }

    3. 內(nèi)存操作函數(shù)

    1. memset()

    #include <string.h> void *memset(void *s, int c, size_t n); 功能:將s的內(nèi)存區(qū)域的前n個(gè)字節(jié)以參數(shù)c填入 參數(shù):s:需要操作內(nèi)存s的首地址c:填充的字符,c雖然參數(shù)為int,但必須是unsigned char , 范圍為0~255n:指定需要設(shè)置的大小 返回值:s的首地址int a[10];memset(a, 0, sizeof(a));memset(a, 97, sizeof(a));int i = 0;for (i = 0; i < 10; i++){printf("%c\n", a[i]);}

    2. memcpy()

    #include <string.h> void *memcpy(void *dest, const void *src, size_t n); 功能:拷貝src所指的內(nèi)存內(nèi)容的前n個(gè)字節(jié)到dest所指的內(nèi)存地址上。 參數(shù):dest:目的內(nèi)存首地址src:源內(nèi)存首地址,注意:dest和src所指的內(nèi)存空間不可重疊,可能會(huì)導(dǎo)致程序報(bào)錯(cuò)n:需要拷貝的字節(jié)數(shù) 返回值:dest的首地址int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int b[10];memcpy(b, a, sizeof(a));int i = 0;for (i = 0; i < 10; i++){printf("%d, ", b[i]);}printf("\n");//memcpy(&a[3], a, 5 * sizeof(int)); //err, 內(nèi)存重疊

    3. memmove()

  • memmove()功能用法和memcpy()一樣,區(qū)別在于:dest和src所指的內(nèi)存空間重疊時(shí),memmove()仍然能處理,不過執(zhí)行效率比memcpy()低些。
  • 4. memcmp()

    #include <string.h> int memcmp(const void *s1, const void *s2, size_t n); 功能:比較s1和s2所指向內(nèi)存區(qū)域的前n個(gè)字節(jié) 參數(shù):s1:內(nèi)存首地址1s2:內(nèi)存首地址2n:需比較的前n個(gè)字節(jié) 返回值:相等:=0大于:>0小于:<0int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int flag = memcmp(a, b, sizeof(a));printf("flag = %d\n", flag);

    4. 堆區(qū)內(nèi)存分配和釋放

    1. malloc()

    #include <stdlib.h> void *malloc(size_t size); 功能:在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)(堆區(qū))中分配一塊長(zhǎng)度為size字節(jié)的連續(xù)區(qū)域,用來存放類型說明符指定的類型。分配的內(nèi)存空間內(nèi)容不確定,一般使用memset初始化。 參數(shù):size:需要分配內(nèi)存大小(單位:字節(jié)) 返回值: 成功:分配空間的起始地址 失敗:NULL#include <stdlib.h> #include <stdio.h> #include <string.h>int main(){int count, *array, n;printf("請(qǐng)輸入要申請(qǐng)數(shù)組的個(gè)數(shù):\n");scanf("%d", &n);array = (int *) malloc(n * sizeof(int));if (array == NULL) {printf("申請(qǐng)空間失敗!\n");return -1;}//將申請(qǐng)到空間清0memset(array, 0, sizeof(int) * n);for (count = 0; count < n; count++) /*給數(shù)組賦值*/array[count] = count;for (count = 0; count < n; count++) /*打印數(shù)組元素*/printf("%2d", array[count]);free(array);return 0; }

    2. free()

    #include <stdlib.h> void free(void *ptr); 功能:釋放ptr所指向的一塊內(nèi)存空間,ptr是一個(gè)任意類型的指針變量,指向被釋放區(qū)域的首地址。對(duì)同一內(nèi)存空間多次釋放會(huì)出錯(cuò)。 參數(shù): ptr:需要釋放空間的首地址,被釋放區(qū)應(yīng)是由malloc函數(shù)所分配的區(qū)域。 返回值:無

    3. 內(nèi)存分區(qū)代碼分析

    1. 返回棧區(qū)地址

    #include <stdio.h> int *fun() {int a = 10;return &a;//函數(shù)調(diào)用完畢,a釋放 }int main(int argc, char *argv[]) {int *p = NULL;p = fun();*p = 100; //操作野指針指向的內(nèi)存,errreturn 0; }

    2. 返回data區(qū)地址

    #include <stdio.h>int *fun() {static int a = 10;return &a; //函數(shù)調(diào)用完畢,a不釋放 }int main(int argc, char *argv[]) {int *p = NULL;p = fun();*p = 100; //okprintf("*p = %d\n", *p);return 0; }

    3. 值傳遞1

    #include <stdio.h> #include <stdlib.h>void fun(int *tmp) {tmp = (int *) malloc(sizeof(int));*tmp = 100; }int main(int argc, char *argv[]) {int *p = NULL;fun(p); //值傳遞,形參修改不會(huì)影響實(shí)參printf("*p = %d\n", *p);//err,操作空指針指向的內(nèi)存return 0; }

    4. 值傳遞2

    #include <stdio.h> #include <stdlib.h>void fun(int *tmp) {*tmp = 100; }int main(int argc, char *argv[]) {int *p = NULL;p = (int *) malloc(sizeof(int));fun(p); //值傳遞printf("*p = %d\n", *p); //ok,*p為100return 0; }

    5. 返回堆區(qū)地址

    #include <stdio.h> #include <stdlib.h>int *fun() {int *tmp = NULL;tmp = (int *) malloc(sizeof(int));*tmp = 100;return tmp;//返回堆區(qū)地址,函數(shù)調(diào)用完畢,不釋放 }int main(int argc, char *argv[]) {int *p = NULL;p = fun();printf("*p = %d\n", *p);//ok//堆區(qū)空間,使用完畢,手動(dòng)釋放if (p != NULL) {free(p);p = NULL;}return 0; } 超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生

    總結(jié)

    以上是生活随笔為你收集整理的C/C++学习之路_七: 内存管理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。