C語言 變數的儲存類

更新於 發佈於 閱讀時間約 12 分鐘
● auto(自動)
● register(暫存器)
● static(靜態)
● extern(外部)
定義
作用範圍(scope)、儲存時期(life time)連結(linkage)的不同作為區別。

●作用範圍:分為區域變數(只能作用於其函數內)全域變數(作用於不同文件中)
接著我們來看看以下兩種程式碼哪個是可執行的。
#include "stdio.h"

void text(){
  printf("%d\n",a);
}

int main(void){
  int a = 0;
  text();
}
--------------------------------結果---------------------------------
’a’ was not declared in this scope
沒錯,這段程式碼是無法執行的,它會跑出一條’a’ was not declared in this scope的訊息,也就是a變數不存在於該區塊中。為什麼呢?我們可以發現,a的定義是在main這個主函數裡面,它是無法作用在其他函數內的,相反的我們在也可以發現,a也只會作用在main裡面,這就是所謂的區域變數。
#include "stdio.h"
int a = 1;
int main(void){
  printf("%d\n",a);
}
--------------------------------結果---------------------------------
1
而這段程式碼中,a是定義在任何函數外,就結果來說,我們可以正常看到它輸出了1。因此a的定義為全域變數,也就是在它定義開始一直到文件的最後一行,任何函式都可以使用a這個變數。
那如果,同時使用呢?
#include "stdio.h"

int a = 1;

int main(void){
  int a = 2;
  printf("%d\n",a);
}
--------------------------------結果---------------------------------
2
結果很明顯,輸出的會是2。由這點可知,局部變數在該函數中,作用的優先會大於全局變數,因此main函式中,a的變數會對應到函數內部,定義為2的a。

●存儲時期(life time):分為靜態存儲(直到整個程式結束前,該變數的值會持續保留,不受二次定義賦值影響)以及自動存儲(一旦該函式結束後,其變數就會消失)兩種。
一樣來看看這兩種的差別吧!
#include "stdio.h"

void text(){
  int a = 0;
  a++;
  printf("%d\n",a);
}

int main(void){
  text();
  text();
  text();
}
--------------------------------結果---------------------------------
1
1
1
結果輸出了不同行的三個1,這是為什麼呢?當main函數跑第一個text()的時候,text內部會創建一個a,並賦值0給a。接著第六行會讓a的值再加上1,也就是0+1=1。最後輸出a的值,並結束這個函數,同時刪掉a這個變數,注意喔!當函數結束後,裡面所有的變數都是會刪除的。因此,我們可以理解到,main使用text函式的三次中,a都會初始定義並賦值為0,所以這也是為什麼輸出的結果都是相同的,而這就是所謂的自動存儲
#include "stdio.h"

void text(){
  static int a = 0;
  a++;
  printf("%d\n",a);
}

int main(void){
  text();
  text();
  text();
}
--------------------------------結果---------------------------------
1
2
3
而這一次的執行可以得到不同行的1、2、3,為什麼加上static之後,就會這樣呢?當main在跑text第一次的時候,流程都跟前面講左方程式碼時一樣,但在最後輸出a的值之後,結束函數的同時並不會刪除掉a的值。並且在第二次跑text函數時,第五行不會重新賦值0給a,a變數仍舊保持著上一次結果的值,也就是1。因此在這次的text中,它會再次加1並輸出。最後我們可以理解到,函數在結束後,不會刪除掉有static描述的變數,並保持其值,而這就是所謂的靜態存儲

●連結(linkage):分為空連結(只能於函數內使用,區域變數)內部連結(可於定義並賦值位置一直到文件最後一行都可使用,類似全域變數)以及外部連結(可跨文件做使用)
讓我們測驗看看外部連結吧~
(text.c)
int b = 10;
(main.c)
#include "stdio.h"
int main(void){
  extern int b;
  printf("%d",b);
}
--------------------------------結果---------------------------------
10
當我在text.c檔案中定義一個變數b,只要在另外一個文件main.c當中,在定義類型前面再加一個extern修飾,就能跨文件使用變數了。在main.c執行後,即可成功輸出10。而這種方法,就是所謂的外部連結。
但要特別注意的是,外部連結必須是在同個資料夾,否則無法透過外部連結使用喔。

儲存類

●auto(自動)

auto擁有自動存儲區域變數空連結的特性,簡單來說就是auto只能修飾在函數內的變數,只能在函數內使用,且函數執行結束後就會消失。事實上,任何在函數內定義的變數,只要沒有特別修飾,直接定義類型、名稱及值後,就會自動修飾為auto。簡單來說就是auto在修飾上面,其實沒有特別用處。
#include "stdio.h"
int main(void){
  auto int b = 0;
  printf("%d",b);
}
--------------------------------結果---------------------------------
0
另外auto還有一個作用是,自動定義變數的類型,如下。
#include "stdio.h"

int main(void){
  auto b = 0;
  printf("%d",b);
}
--------------------------------結果---------------------------------
0
如果使用auto當定義類型時,它會自動幫b搜尋它適合的類型,也就是int。但如果沒有特別指定值的時候,變數則會默認為int。

●register(暫存器)

register擁有自動存儲區域變數空連結的特性,一樣是只能修飾在函數內的變數,它的特點在於,經過register修飾的變數,會存放在暫存器當中,而不是平常的記憶體內。因此它的執行速度會大幅提升,如下圖採用CR Ferreira的時間計算程式碼,計算有無register修飾的時間差。
#include "stdio.h"
#include "sys/time.h"
int main(void){
  double sum = 0;
  double add = 1;
  struct timeval begin, end;
  gettimeofday(&begin, 0);
  register int a,i;//暫存器變數
  for (i=0; i<1000000000; i++){//主要執行內容
    a++;
  }
  gettimeofday(&end, 0);
  long seconds = end.tv_sec - begin.tv_sec;
  long microseconds = end.tv_usec - begin.tv_usec;
  double elapsed = seconds + microseconds*1e-6;
  printf("Time measured: %.3f seconds.\n", elapsed);
  return 0;
}
--------------------------------結果---------------------------------
Time measured: 0.681 seconds.
-------------------------------分隔線--------------------------------
#include "stdio.h"
#include "sys/time.h"

int main(void){
  double sum = 0;
  double add = 1;
  struct timeval begin, end;

  gettimeofday(&begin, 0);
  int a,i;//一般變數

  for (i=0; i<1000000000; i++){//主要執行內容
  a++;
  }

  gettimeofday(&end, 0);
  long seconds = end.tv_sec - begin.tv_sec;
  long microseconds = end.tv_usec - begin.tv_usec;
  double elapsed = seconds + microseconds*1e-6;
  printf("Time measured: %.3f seconds.\n", elapsed);
  return 0;
}
--------------------------------結果---------------------------------
Time measured: 2.215 seconds.
可以清楚看出,有register修飾的文件,執行速度大幅領先無修飾的文件。

●static(靜態)

static擁有靜態區域變數、靜態全域變數兩種方式。前者只能在函數內修飾,並擁有靜態存儲、區域變數、空連結的特性後者只能在函數外修飾,擁有靜態存儲、全域變數、內部連結的特性
#include "stdio.h"
static int i;//靜態全域變數
void text(){
  static int j;//靜態區域變數
}
靜態區域變數的特性,在前面有說過,能夠在整個程式執行結束前,一直保有其值不會消失。這時候你可能會想,全域變數本來就是到程式執行結束才會消失,那還有必要寫static來修飾嗎?
確實,是如此沒錯,但全域變數跟靜態全域變數的最大差別在於連結性。一般沒做修飾的全域變數是可以跨文件利用extern做連結的,但經過static修飾的變數,則是無法跨文件取得,也就是內部連結,僅供該文件使用。
(text.c)
static int i = 5;
(main.c)
#include "stdio.h"
extern int i;
int main(void){
  printf("%d",i);
}
--------------------------------結果---------------------------------
當在main.c執行後,就無法取得i這個變數,也因此無法取得i的值,這就是static的內部連結性質。

● extern(外部)

一般在函數外未特別修飾的變數,擁有靜態存儲、全域變數、外部連結的特性,而extern則可以引用這些變數。相關實作在前方有示範過了,這邊就不多加掩飾。
參考內容
即將進入廣告,捲動後可繼續閱讀
為什麼會看到廣告
avatar-img
12會員
18內容數
這裡是來自 高科大 資管系二年級的學生,希望能在學習的過程中,也分享這些知識給大家。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇內容,將會講解什麼是腳本函式,以及與腳本函式相關的知識。包括腳本的簡介、使用函式(或全域變數)的注意事項、定義全域變數、定義函式、什麼是宣告、局部變數的應用。
Thumbnail
這篇內容,將會講解什麼是變數範圍,以及與變數範圍相關的知識。包括變數範圍的簡介、實體變數、全域變數、局部變數、常數。
“所有人寫的程式會變成指令 每一道指令是由CPU執行 而CPU所能理解的指令類型有限”
就是指變數可以被訪問和使用的範圍,來說一下var、let和const的作用域差異。 var :function example() { console.log(x); // 輸出: undefined 因為變量提升造成的 var x = 5; } 函數作用域或全域作用域 可以重複宣告
前言: 雖然前面有些定義還沒有完整的解釋,但還是後面再說吧, 誰想一直看理論啊(摔本子)
※ 認識變數: 變數(variable)是在程式裡面把東西存起來的概念。 基於不讓電腦每次都需要重新運算,把需要花時間運算的東西先存起來,之後再直接拿出來使用,所以我們需要變數。 變數儲存的位置在電腦裡的記憶體。變數就像是一個箱子,記憶體就像是一個倉庫。箱子的名稱就是變數名稱,箱子的內容物就是
Thumbnail
宣告變數 變數是程式中用來儲存和表示數據的標識符號​,並將變數存放在某個記憶體位子 可以用ID的方法查找變數存在哪個記憶體,此方法有利於以後查找問題用。 在大多數程式語言中,變數需要事先聲明(宣告)並賦值。 而Python是一種動態類型語言,不需要顯式宣告變數類型,而是在賦值時自動進行推斷。
Thumbnail
在Python中,有三種變數作用域:全域、區域和封閉。 區域作用域(Local Scope): 在函式內部定義的變數具有區域作用域,它們只能在該函式內部訪問。 例如: def my_function(): local_variable = 10
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇內容,將會講解什麼是腳本函式,以及與腳本函式相關的知識。包括腳本的簡介、使用函式(或全域變數)的注意事項、定義全域變數、定義函式、什麼是宣告、局部變數的應用。
Thumbnail
這篇內容,將會講解什麼是變數範圍,以及與變數範圍相關的知識。包括變數範圍的簡介、實體變數、全域變數、局部變數、常數。
“所有人寫的程式會變成指令 每一道指令是由CPU執行 而CPU所能理解的指令類型有限”
就是指變數可以被訪問和使用的範圍,來說一下var、let和const的作用域差異。 var :function example() { console.log(x); // 輸出: undefined 因為變量提升造成的 var x = 5; } 函數作用域或全域作用域 可以重複宣告
前言: 雖然前面有些定義還沒有完整的解釋,但還是後面再說吧, 誰想一直看理論啊(摔本子)
※ 認識變數: 變數(variable)是在程式裡面把東西存起來的概念。 基於不讓電腦每次都需要重新運算,把需要花時間運算的東西先存起來,之後再直接拿出來使用,所以我們需要變數。 變數儲存的位置在電腦裡的記憶體。變數就像是一個箱子,記憶體就像是一個倉庫。箱子的名稱就是變數名稱,箱子的內容物就是
Thumbnail
宣告變數 變數是程式中用來儲存和表示數據的標識符號​,並將變數存放在某個記憶體位子 可以用ID的方法查找變數存在哪個記憶體,此方法有利於以後查找問題用。 在大多數程式語言中,變數需要事先聲明(宣告)並賦值。 而Python是一種動態類型語言,不需要顯式宣告變數類型,而是在賦值時自動進行推斷。
Thumbnail
在Python中,有三種變數作用域:全域、區域和封閉。 區域作用域(Local Scope): 在函式內部定義的變數具有區域作用域,它們只能在該函式內部訪問。 例如: def my_function(): local_variable = 10