C 基礎 - 存儲類別、連接與內存管理

1、存儲類別

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; 
} 

 

 

2、分配內存 malloc ()與 free()

#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);

 

1. malloc()函數

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*) 進程強制類型轉換.

 

2. free()函數

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;
}

 

3. calloc() 函數

calloc 與 malloc的區別是calloc對申請分配的內存空間進行初始化爲0,而後返回分配內存空間的首地址。

long * newmem;
newmem = (long *) calloc(100, sizeof(long));

 

有兩個參數:

第一個參數是所需的存儲單元數量

第二個參數是存儲單元的大小,以字節爲單位

 

4. realloc()函數

realloc()函數用於修改一個原先已經分配的內存塊大小。

 

經典問題:new delete 與malloc free 的聯繫與區別?

1. 都是在堆(heap)上進行動態的內存操做。 自動分配的變量都在棧上

2. malloc 與 free 是C庫函數, new delete 是C++中運算符

 

問題:要求設置一絕對地址爲0x67a9的整型變量的值爲0xaa55。

int *ptr; 
ptr = (int *)0x67a9*ptr = 0xaa55;
相關文章
相關標籤/搜索