重溫C語言(6)之存儲類別

原文:https://mp.weixin.qq.com/s/aDziV-VJ-5Wr6eitt7FaoA

對象

被存儲的每一個值都佔用一定的物理內存,這樣的內存稱爲對象。

int a = 73;

a 是一個標誌符。指定了硬件內存中的對象的方式,並提供了存儲在對象中的值。

int * p = &a;

p 是一個標誌符,指定了儲存地址的對象。但是 *p 不是標誌符,是個表達式, 它指向了一個對象。

作用域

作用域描述程序中可訪問標識符的區域。
一個C變量的作用域可以是塊作用域、函數作用域、函數原型作用域或文件作用域。塊是用一對花括號括起來的代碼區域。

函數原型作用域: int a(int a, double b) 小括號擴起來的就是函數原型作用域。
文件作用域即聲明的變量在所在文件從定義處到文件末尾都可用,這個變量也稱爲全局變量。

int a = 10; // 本文件和同程序的其他文件都可以使用 a變量
static b = 22;  // 只有本文件可以訪問 b
int main(){
}

如果文件要使用其他文件中的外部變量,則必須用 extern 聲明該變量。

extern char Name;

存儲期

靜態存儲期

存儲在靜態內存中存儲地址不變但值可以變,在程序的執行期間一直存在,關鍵字 static 聲明的變量變量具有靜態存儲期。

線程存儲期

自動變量

默認情況下,聲明在塊或函數頭中的任何變量都屬於自動存儲類別。可以使用 auto 關鍵字來顯示的定義它。
變量具有自動存儲期意味着,程序在進入該變量聲明所在的塊時變量存在,程序在退出該塊時變量消失。原來該變量佔用的內存位置現在可做他用。
自動變量不會初始化,除非顯式初始化它。

int a; // a 變量的值是之前佔用分配給 a 的空間中的任意值(如果有的話),它不會是0
int b = 10;

寄存器變量

變量通常儲存在計算機內存中。如果幸運的話,寄存器變量儲存在CPU的寄存器中,或者概括地說,儲存在最快的可用內存中。
由於寄存器變量儲存在寄存器而非內存中,所以無法獲取寄存器變量的地址。
register 聲明寄存器變量,編譯器必須根據寄存器或最快可用內存的數量衡量你的請求,或者直接忽略你的請求,這個時候寄存器變量會成爲普通變量,不管怎樣都不能使用地址運算符。

register int a;

存儲類別說明符

  1. auto說明符表明變量是自動存儲期,只能用於塊作用域的變量聲明中;
  2. register說明符也只用於塊作用域的變量,它把變量歸爲寄存器存儲類別,請求最快速度訪問該變量。同時,還保護了該變量的地址不被獲取;
  3. 用static說明符創建的對象具有靜態存儲期,載入程序時創建對象,當程序結束時對象消失;
  4. extern說明符表明聲明的變量定義在別處;

注意:外部函數可以被其他文件的函數訪問,但是靜態函數只能用於其定義所在的文件。

doubleg amma(double);/*該函數默認爲外部函數*/
static double beta(int, int);
extern double delta(double, int);

在同一個程序中,其他文件中的函數可以調用gamma()和delta(),但是不能調用beta(),因爲以static存儲類別說明符創建的函數屬於特定模塊私有。

隨機數

rand()函數生成隨機數字。這個函數取值範圍是0RNAD_MAX之間,RAND_MAX被定義在stdlib.h中,其值通常是INT_MAX。

下面實現一個自己的隨機數程序:
myrand.c 文件:

static unsigned long int next = 1;

unsigned int myrand(void) {
    // 生成僞隨機數的魔法公示
    next = next * 1103515245 + 12345;
    return (unsigned int)(next / 65536) % 32768;

}

main.c 文件:

#include <stdio.h>
#include "myrand.c"
extern unsigned int myrand(void);
int main() {
    for (int i = 0; i < 10; ++i) {
        printf("%d\n", myrand());
    }
}

分配內存

malloc()

malloc() 函數可以在程序運行時分配更多的內存,該函數接受一個參數:所需的內存字節數。malloc()函數會找到合適的空閒內存塊,這樣的內存是匿名的,它會返回動態分配內存塊的首字節地址。

如果malloc()分配內存失敗,將返回空指針。

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

上面代碼爲30個double類型的值請求內存空間,並設置ptd指向該位置。注意,指針ptd被聲明爲指向一個double類型,而不是指向內含30個double類型值的塊。
動態分配的內存數量只會增加,除非用free()進行釋放。
free() 函數的參數是指向malloc() 分配的內存的指針,用來釋放這塊內存。

calloc()

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

calloc()malloc()類似, 返回指向void的指針。calloc()函數接受兩個無符號整數作爲參數(ANSI規定是size_t類型)。第1個參數是所需的存儲單元數量,第2個參數是存儲單元的大小(以字節爲單位)。它會把分配的塊所有位都設爲0.

也需要用 free() 函數進行釋放。

存儲類別和動態內存分配有何聯繫?

靜態存儲類別所用的內存數量在編譯時確定,該類別的變量在程序開始執行時被創建,在程序結束時被銷燬。

自動存儲類別的變量在程序進入變量定義所在塊時存在,在程序離開塊時消失。

動態分配的內存在調用malloc()或相關函數時存在,在調用free()後釋放。使用動態內存通常比使用棧內存慢。

ANSIC類型限定符

const

聲明對象的值可以初始化但是不能被修改。

volatile

volatile限定符告知計算機,代理(而不是變量所在的程序)可以改變該變量的值。通常,它被用於硬件地址以及在其他程序或同時運行的線程中共享數據。例如,一個地址上可能儲存着當前的時鐘時間,無論程序做什麼,地址上的值都隨時間的變化而改變。

restrict

restrict關鍵字允許編譯器優化某部分代碼以更好地支持計算。它只能用於指針,表明該指針是訪問數據對象的唯一且初始的方式。

_Atomic

併發程序設計把程序執行分成可以同時執行的多個線程,。當一個線程對一個原子類型的對象執行原子操作時,其他線程不能訪問該對象。_Atomic 用來聲明原子類型的變量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章