程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 基本存儲類別和動態內存分配,存儲類別動態內存

基本存儲類別和動態內存分配,存儲類別動態內存

編輯:關於C語言

基本存儲類別和動態內存分配,存儲類別動態內存


本文部分內容參考了C Primer Plus(sixth edition)一書

存儲類別和內存分布

  簡單介紹一下變量的存儲類別和它的內存分布,我們先通過一張表來了解一些基本術語:

術語 解釋 舉例 作用域  描述程序中可訪問的標識符的區域 塊作用域,函數作用域,函數原型作用域,文件作用域 鏈接 變量屬於哪一部分私有 外部鏈接(全局可見),內部鏈接(單一文件可見),無鏈接(塊內可見) 存儲期 描述通過標識符訪問對象的生命期 靜態存儲期,線程存儲期,自動存儲期,動態分配存儲期 翻譯單元 一個源文件包含多個頭文件,編譯時當作一個翻譯單元,作一個文件 頭文件包含在源代碼文件內

 

  〉〉塊指的是一對花括號括起來的代碼。C99之後,塊也可以是循環語句+循環體(單一語句)。具有塊作用域的變量只能在塊內可見。C99之前,變量只允許聲明在塊的開頭。無鏈接。

  〉〉函數作用域僅用於goto標簽,一個標簽首次出現在函數的內層塊中,它的作用域將延伸直整個函數。無鏈接。

  〉〉函數原型作用域:聲明函數原型時形參的作用域,僅限於原型。無鏈接。注意,如果在某一函數int fun的原型中聲明變長數組,應該這樣聲明int fun(int n, int m, int vla[n][m]);

  〉〉文件作用域:定義在函數外,從定義處到文件末尾均可見。具有內部和外部鏈接。

  〉〉外部鏈接:具有外部鏈接的變量在程序的所有文件中都可見。例如,main.c中定義了一個外部鏈接變量int var;在lock.c中就要用extern int x;來聲明使用。

  〉〉內部鏈接:具有該鏈接的變量只在該文件可見。例如:static int var; var使用時不必加上像外部鏈接變量的聲明。

  〉〉無鏈接:不具有文件作用域的變量無鏈接。

  〉〉靜態存儲期:生命(在內存中存在時間):程序開始到結束。

  〉〉線程存儲期:生命:從線程開始到結束。

  〉〉自動存儲期:生命以塊為參照,離開塊即消失。

  〉〉動態分配存儲期:由程序員管理。

  總結表如下:

存儲類別 存儲期 作用域 鏈接 聲明方式 自動 自動 塊 無 塊內 靜態外部鏈接 靜態 文件(整個程序) 外部 所有函數外,在另一文件使用前需要用extern再次聲明 靜態內部鏈接 靜態 文件(單一翻譯單元) 內部 所有函數外,用static聲明 靜態無鏈接 靜態 塊 無 塊內,用static聲明

 

  此外還有寄存器變量,由於這種變量實用性不高,不討論。

  在內存中,靜態數據(包括常量)占用一個區域,自動數據占用另一個區域,動態分配的數據占第三個區域(內存堆)。

動態內存分配函數malloc()和釋放函數free()

  在C99標准發布之前,我們不能使用變量作為聲明數組的元素個數。如果我們要創建一個自定義長度的數組,就不能使用原來定義普通靜態數組的方法。那麼該怎麼辦呢?這時候就要通過動態分配內存來聲明動態數組了。

  malloc()函數原型在stdlib.h文件中。它返回一個指向void類型的指針,這個指針指向分配內存的首地址。下面這段代碼分配一個具有10個int類型元素的動態數組,用指針ptr指向這個數組的首元素。

int *ptr, n = 10;
ptr = (int *)malloc( (size_t)n * sizeof(int) );

  malloc()的括號裡應填入所需要分配的內存大小,以字節為單位,類型是size_t。為了提供程序的可移植性,我們用了sizeof()運算符來獲取當前系統int類型的變量所占用的字節大小。建議大家使用強制類型轉換來使用指向void類型的指針。

  但是,如果分配內存失敗了,會怎麼樣?malloc()函數會返回一個指向NULL的指針,如果程序在內存分配失敗後繼續使用內存,就會導致程序異常終止。所以,在分配內存之後,需要檢查一下成功分配了沒有。

int *ptr, n = 10;
ptr = (int *)malloc( (size_t)n * sizeof(int) );
if(ptr == NULL)
    exit(EXIT_FAILURE);

或者:

int *ptr, n = 10;
if( !( ptr = (int *)malloc( (size_t)n * sizeof(int)) ) )
    exit(EXIT_FAILURE);

都是很好的方法。

  我們知道,malloc()分配的內存具有動態分配存儲期,由程序員控制變量的生命期。如果程序結束前沒有及時地釋放它,程序結束之後就仍然存在。更糟糕的是,如果程序運行中,指向該塊內存首地址的指針全部被銷毀,那麼這塊內存就永遠無法正確訪問,也就無法釋放這塊內存,這就會導致內存洩露。內存洩露問題很嚴重,會導致系統停止運行!因為沒有空余的內存空間,所以,在分配內存之後如果不需要再次使用,一定要釋放!警惕不小心更改指向動態分配內存空間的指針!釋放動態分配的內存,我們用free()函數。

free(ptr);

  這行代碼就釋放了之前用malloc()分配的內存空間。

動態數組排序實例

1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <conio.h> 4 5 int main(int argc, char * argv[]){ 6 int * ptr; 7 int key, size, i, j; 8 9 printf("請輸入動態數組大小:"); 10 while( !( scanf("%d",&size) ) || size < 0 ){ 11 printf("輸入錯誤,請重新輸入。\n:"); 12 while( getchar() != '\n' );//調整輸入緩沖區 13 } 14 15 if( !( ptr = (int *)malloc( (size_t)size * sizeof(int)) ) ){//分配內存 16 printf("內存請求失敗!"); 17 exit(EXIT_FAILURE); 18 } 19 20 printf("請依次輸入數組元素內容:\n"); 21 for(i = 0; i < size; i++) 22 scanf("%d",&ptr[i]); 23 while( getchar() != '\n' ); 24 25 for(i = 1; i < size; i++){//插入排序 26 j = i - 1; 27 key = ptr[i]; 28 while( j >= 0 && ptr[j] > key){ 29 ptr[j + 1] = ptr[j]; 30 j--; 31 } 32 ptr[j + 1] = key; 33 } 34 35 printf("各元素排序後輸出如下:"); 36 for(i = 0; i < size; i++) 37 printf("%d ",ptr[i]); 38 39 free(ptr); //釋放內存,重中之重 40 41 _getch(); 42 return 0; 43 } 動態數組排序實例

運行結果:

補充:realloc()函數

  原型:void *realloc(void * ptr, size_t size);

  簡單描述:把ptr指向的內存空間更改為size字節,size字節內的內存塊內容不變。該函數返回塊的位置。如果不能重新分配空間,則返回NULL。如果ptr為NULL,其行為與調用帶size參數的malloc()相同。如果size為0且ptr不是NULL,其行為與調用帶ptr參數的free相同。

  不推薦大量使用這個函數,因為當不能重新分配空間時,函數返回NULL,原來已分配的內存塊地址丟失,會造成內存洩露。盡管在很多時候這個函數非常方便。如果你需要使用這個函數,請先保存原來內存塊的地址!

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved