C語言提供幾種存儲方法,來存放在內存中變量的值。數組
從硬件方面去看,被存儲的每個值都會佔用必定的物理內存,C語言把這樣的一塊內存叫對象(Object)。多線程
從軟件方面去看,程序須要一種方法訪問對象。聲明變量是一種方法。併發
一個變量具備不一樣的存儲類別,存儲類別是指具備不一樣的 存儲期(Storage duration)、做用域(scope)、 連接(linkage)。函數
有三個地方能夠用於存儲變量:普通內存、運行時堆棧、硬件寄存器。 oop
1. 做用域: 描述程序中可訪問標識符的區域。一個C變量做用域優化
* 塊做用域ui
* 函數做用域spa
* 函數原型做用域:指 int add(a, b); 中 a 與 b 的形參。 線程
* 文件做用域(全局變量):變量定義在函數的外面。翻譯
2. C變量有3種連接屬性:
* 外部連接(external): 能夠在多個文件程序中使用。
* 內部連接(static): 只能在一個翻譯單元中使用。
* 無連接: 具備塊做用域、函數做用域、函數原型做用域的變量都是無連接變量,表示變量的可見範圍屬於塊、函數私有。
int giants = 5; // 文件做用域,外部連接, 其餘文件可使用 static int dog = 4; // 文件做用域, 內部連接, 文件私有 // static 關鍵字 描述了文件做用域的 連接特性與存儲週期無關 int main(void) { ... static int max(int x, int y); ... }
3. 存儲期:做用域與連接描述了標識符的可見性,存儲期描述了經過這些標識符訪問的對象的生存期。
C對象有四種存儲期:
* 靜態存儲期: 程序執行期間一直存在,不管是內部連接仍是外部連接的文件做用域變量都是靜態存儲期。
* 線程存儲期: 用於併發程序設計,線程結束以前一直存在。
* 自動存儲期: 塊做用域變量具備自動存儲期,爲了讓塊做用域變量具備靜態存儲期,在變量前加 static 關鍵字
* 動態分配存儲期:
4. 五種存儲類別
存儲類別 | 存儲期 | 做用域 | 連接 | 聲明方式 |
自動(automatic) | 自動 | 塊 | 無 | 塊內 |
寄存器(register) | 自動 | 塊 | 無 | 塊內,關鍵字register |
靜態外部連接(static with external linkage) |
靜態 | 文件 | 外部 | 全部函數外 |
靜態內部連接(static with interanl linkage) |
靜態 | 文件 | 內部 | 全部函數外,關鍵字static |
靜態塊做用域(static with no linkage) |
靜態 | 塊 | 無 | 塊內,傅關鍵字 static |
例如以下
#include <stdio.h> int a; // 靜態外部連接 (全局變量,文件做用域,外部連接,其餘文件均可以使用) static int d; // 靜態內部連接 (文件做用域,內部連接,僅限本文件內函數使用) extern char str; // 字符str是定義在其餘文件中的全局變量
main() { int b; // 自動變量 (main函數結束後,消失) static int c; // 靜態塊做用域 (main函數結束後,消失) retister int e; // 寄存器變量,沒法獲取寄存器變量的地址 ...... } print_r() { int r; // 自動變量(Print_r函數調用時分配,結束後,消失) static int e; // 靜態塊做用域,該變量的地址在內存中不會改變 }
程序案例:
/* * 此程序的功能用來講明變量的5種存儲類別 * storage.c */ #include <stdio.h> void report_count(); // 函數原型 void accumulate(int k); // 函數原型 int count = 0; // 全局變量, 外部連接 int main(void) { int value; // 自動變量 register int i; // 寄存器變量 printf("Enter a positive integer (0 to quit): "); while (scanf("%d", &value) == 1 && value > 0) { ++count; // 引用全局變量 for (i = value; i >= 0; i--) accumulate(i); // 變量i是塊變量,調用accumulate函數 printf("Enter a positive integer (0 to quit): "); } report_count(); return 0; } void report_count() { printf("Loop executed %d times\n", count); }
問題:關鍵字static做用是什麼?
在C語言中,關鍵字 static 有三個明顯的做用:
首先:一旦聲明爲靜態變量,在編譯時刻開始永遠存在,不受做用域範圍約束
* 若是是局部靜態變量,則此靜態變量只能在局部做用域內使用,超出範圍不能使用,可是它確實還佔用內存,還存在.
* 在模塊內(但在函數體外),一個被聲明爲靜態的變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
* 在模塊內,一個被聲明爲靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用。
/* * 此程序的功能用來講明變量的5種存儲類別 * storage_accumulate.c */ #include <stdio.h> extern int count; // 引用全局變量 static int total = 0; // 全局靜態變量,本文件內有效 void accumulate(int k); // 函數原型 void accumulate(int k) // k是塊做用域 { static int subtotal = 0; // 塊靜態變量 if (k <= 0) { printf("loop cycle: %d\n", count); // 引用全局變量 printf("subtotal: %d; total: %d\n", subtotal, total); subtotal = 0; } else { subtotal += k; total += k; } }
編譯命令:
gcc -o storage storage.c storage_acculate.c
關鍵字const有什麼含意?
1) 只讀。
2)使用關鍵字const也許能產生更緊湊的代碼。
3)使編譯器很天然地保護那些不但願被改變的參數,防止其被無心的代碼修改。
Dan Saks在他的文章裏徹底歸納了const的全部用法,(譯者:Embedded Systems Programming)
下面的聲明都是什麼意思?
const int a; // 定義常整形數 a int const a; // 定義常整形數 a
const int *a; // a是一個指向常整形數的指針,值不可改,指針a可改 // const 在*以前,表示其值不能夠改
int * const a; // a是一個指向整型數的常指針, 指針指向的整形數能夠修改,指針不可改 // const 在*以後, 表示指針不可改
int const *a const; // a是一個指向常整型數的常指針, 指針與值都不可改
關鍵字volatile有什麼含意? 並給出三個不一樣的例子?
一個定義爲volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。
精確地說就是,優化器在用到這個變量時必須每次都當心地從新讀取這個變量的值,而不是使用保存在寄存器裏的備份。
下面是volatile變量的幾個例子:
* 並行設備的硬件寄存器(如:狀態寄存器)
* 一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
* 多線程應用中被幾個任務共享的變量
一個參數既能夠是const還能夠是volatile嗎?解釋爲何。
是的。一個例子是隻讀的狀態寄存器。它是volatile由於它可能被意想不到地改變。它是const由於程序不該該試圖去修改它。
一個指針能夠是volatile 嗎?解釋爲何。
是的。儘管這並不很常見。一個例子是當一箇中斷服務子程序修改一個指向一個buffer的指針時。
下面的函數有什麼錯誤:
int square(volatile int *ptr) { return *ptr * *ptr; }
這段代碼的目的是用來返回指針*ptr指向值的平方,可是,因爲*ptr指向一個volatile型參數,編譯器將產生相似下面的代碼:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
因爲*ptr的值可能被意想不到地該變,所以a和b多是不一樣的。結果,這段代碼可能返不是你所指望的平方值!
正確的代碼以下:
long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
#include <stdlib.h> void *malloc(size_t size); void free(void *ptr); void *calloc(size_t nmemb, size_t size); void *realloc(void *ptr, size_t size);
malloc自動分配內存,須要一個內存字節數做爲參數,返回動態分配內存塊的首字節地址。
若是malloc內存返回失敗,則會返回NULL。
例如用malloc建立一個數組: 爲30個double型的值請求內存空間,並設置ptd指向該位置。
double *ptd; ptd = (double *) malloc(30 * sizeof(double));
1). 定義double型指針變量ptd
2). 30 * sizeof(double) 計算出須要的字節數
3). malloc()函數請求內存空間
4). (double*) 進程強制類型轉換.
free() 的參數是malloc參數返回的地址,釋放malloc分配的內存。
所以,動態分配內存的週期從調用malloc到free爲止。
一個動態分配內存與釋放內存的例子
/* dyn_arr.c -- 動態分配一個數組 */ #include <stdio.h> #include <stdlib.h> /* for malloc(), free() */ int main(void) { double * ptd; /* 存儲動態分配內存的首地址 */ int max; /* 輸入數組的最大個數 */ int number; int i = 0; puts("What is the maximum number of type double entries?"); scanf("%d", &max); /* 輸入數組的最大個數 */ ptd = (double *) malloc(max * sizeof (double)); /* 分配內存 */ if (ptd == NULL) /* 檢查是否分配成功 */ { puts("Memory allocation failed. Goodbye."); exit(EXIT_FAILURE); } /* ptd now points to an array of max elements */ puts("Enter the values (q to quit):"); while (i < max && scanf("%lf", &ptd[i]) == 1) ++i; printf("Here are your %d entries:\n", number = i); for (i = 0; i < number; i++) { printf("%7.2f ", ptd[i]); if (i % 7 == 6) putchar('\n'); } if (i % 7 != 0) putchar('\n'); puts("Done."); free(ptd); return 0; }
calloc 與 malloc的區別是calloc對申請分配的內存空間進行初始化爲0,而後返回分配內存空間的首地址。
long * newmem; newmem = (long *) calloc(100, sizeof(long));
有兩個參數:
第一個參數是所需的存儲單元數量
第二個參數是存儲單元的大小,以字節爲單位
realloc()函數用於修改一個原先已經分配的內存塊大小。
經典問題:new delete 與malloc free 的聯繫與區別?
1. 都是在堆(heap)上進行動態的內存操做。 自動分配的變量都在棧上
2. malloc 與 free 是C庫函數, new delete 是C++中運算符
問題:要求設置一絕對地址爲0x67a9的整型變量的值爲0xaa55。
int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa55;