1. 程式人生 > >C儲存類、連結和記憶體管理--動態分配記憶體及型別限定詞

C儲存類、連結和記憶體管理--動態分配記憶體及型別限定詞

文章目錄

儲存類說明符

C中儲存類說明符共有5個,為auto register static extern typeddef,最後一個關鍵字typedef

與記憶體儲存無關。
規定:不可以在一個宣告中使用一個以上儲存類說明符
儲存類說明符用來確定變數的儲存型別。

儲存類和函式

函式的儲存類有兩種:

  • 外部
  • 靜態

在一個檔案中定義的函式預設是外部的,也就是說其他檔案可以呼叫它,只有使用static關鍵字修飾的函式才是函式定義所在檔案所私有的函式,通常用來解決不同檔案函式之間的命名衝突。

double a();//預設宣告,函式a是外部的
extern int b();//此處顯式宣告b函式是在其他檔案中定義的,可以省略。主要是為了讓程式更清晰,除非函式宣告使用了關鍵字`static`,否則預設其為`extern`的
static
int c();//c函式只能在本檔案中呼叫

動態分配記憶體

mallocfree函式原型存在於stdlib.h中。

malloc函式

在C中,一些資料的記憶體是由系統自動分配的,也允許程式主動要求分配。

int foo = 0;//系統自動分配記憶體空間用來儲存一個int
char string[] = "I love you.";//系統自動為陣列string分配正好裝下字串的記憶體空間
int bar[10];//要求分配10個用來儲存int的記憶體空間

還可以手動分配記憶體。
extern void * malloc(unsigned int num_bytes)
函式malloc

函式接受一個引數,該餐宿用於指定需要分配的記憶體位元組數。malloc找到可用記憶體中一個區塊,並返回該區塊記憶體第一個位元組的地址。它的void *返回值是一個通用指標,可以轉換為其他指標型別。C中不要求強制轉換,但C++中要求強制轉換。如果malloc找不到符合要求的可用記憶體,它會返回空指標。例子:

double *p;
p = (double *)malloc(30 * sizeof(double));

例子中,分配了一塊記憶體用於儲存30個double型別資料,並將首位元組地址賦值給了指標p

free函式

void free(void *p)
對應每個malloc函式呼叫,應該有對應的free呼叫來進行記憶體釋放。它的引數是之前malloc函式分配記憶體塊第一個位元組的地址。也就是說分配的記憶體可用時間是從malloc執行結束開始到free釋放記憶體為止。
例子:

#include <stdio.h>
#include <stdlib.h>

/*
 * test.c 編譯後產生可執行檔案test.exe或test.out
 */


int main() {

    double *p;
    int max;
    int number;
    int i = 0;

    puts("What's the number of \"double\" entries?");
    while (scanf("%d",&max) != 1){
//        setbuf(stdin,NULL);
        scanf("%*s");
        puts("Please input a integer:");
    }

    printf("max = %d\n",max);

    p = (double *) malloc(max * sizeof(double));
    if (p == NULL){
        puts("Memory allocation has failed. Try to Restart this program.");
        exit(EXIT_FAILURE);
    }

    puts("Enter the values(q to quit): ");
    while (i < max && scanf("%lf",&p[i]) == 1)
        ++i;
    printf("Here are the number of entries: %d\n",number = i);
    for (int j = 0; j < number; ++j) {
        printf("%7.2f ",p[j]);
        if (j % 7 == 6)
            putchar('\n');
    }
    if (i % 7 !=0)
        putchar('\n');
    printf("i = %d\n",i);
    puts("Done.");
    free(p);
    return 0;
}

calloc函式

malloc類似,不同的是calloc可以指定要分配單元數目以及每個單元所需要的位元組數。calloc會預設將記憶體塊中的各個位置0。
calloc分配的記憶體同樣需要用free函式來釋放。

動態分配記憶體的缺點

動態分配記憶體給了程式一定的自由,但是若是忘記釋放記憶體,那麼就會造成資源的浪費(記憶體洩漏)。而且相對於自動變數棧式管理,動態分配記憶體不是緊湊的連續分配,而是在記憶體中找合適的區塊,會造成記憶體碎片,拖慢速度。

C型別限定關鍵字

constant定義全域性常量

constant定義常量之前已經做了筆記,看這裡。這裡我們來看它與全域性常量的關係。
constant定義全域性常量有兩種方式:
第一種方法:

//file1.c
const double PI = 3.14;

//file2.c
extern const double PI;

第二種:

//constant.h中定義常量,需要`static`關鍵字來修飾
static const double PI = 3.14;

//file1.c只需include 標頭檔案即可
#include "constant.h"

//file2.c
#include "cobnstant.h"

第二種方法中,file1.cfile2.c檔案都包含了constant.h標頭檔案,那麼這兩個檔案都會定義宣告一個本檔案私有的靜態內部連結變數PI,其實是對constant.hPI值的拷貝。為什麼必須要使用static關鍵字呢?因為如果不使用的話同一個靜態外部連結變數就要在兩個檔案中定義宣告兩次,而我們知道外部變數只允許定義宣告一次,其餘的都應該是引用宣告,定義兩次會造成識別符號衝突,還不如直接加個static修飾,為每個檔案分別拷貝一個PI值給他們用。

volatile關鍵字

volatile告訴編譯器某個變數除了能被程式本身修改之外,還可以被超出程式之外的其他部分改變。假定,有一個變數的值記錄的是時間,那麼不管程式有沒有在執行,執行的如何,這個變數的值肯定是要隨著時間變化而變化的,那麼這個變數就應該加volatile修飾來提醒編譯器。再來個例子:

int x = 10;
int val1 = x;
int val2 = x;

編譯器注意到x變數被使用了兩次而沒有進行別的操作,那麼他可以將x的值臨時儲存在暫存器中,那麼當val2val1進行賦值操作時就會變快。但是,如果x的值可能被除了程式之外的部分改變,那麼就應該這樣:volatile int x = 10;來告訴編譯器這個變數可能會如此,那麼編譯器就不會做出將x值存於暫存器這樣的優化。一個變數既可以是constant,也可以是volatile的,因為不能被程式改變的量為常量,但可能被硬體改變,那麼就是volatile的。這與Java中的volatile關鍵字可不一樣。

restrict關鍵字

restrict關鍵字只能用來修飾指標,表示某個資料物件的唯一訪問方式就是該指標,方便編譯器優化。用法:
int * restrict foo = (int *) malloc(10 * sizeof(int))。這裡表明foo這個指標是陣列的唯一訪問方式。

int array[4] = {};
int *p = array;

這裡就不能給prestrict限定詞,因為array這個陣列可以通過arrayp兩種方式進行訪問。