(作者:徐誠 http://blog.csdn.net/shizhebsys 保留版權)
C 語言程序中用于運算的數據可以分為常量與變量兩種基本類型。常量是直接在代碼中所出現的數據,運算過程中不能修改常量值。變量是 C 語言程序在內存中為數據動態劃分出的定長存儲空間,運算過程中可以修改變量值。為了讓讀者能夠更深入的了解常量與變量的本質,在介紹常量與變量前,我們首先需要認識計算機內部數據存儲機制。
3.1.1 內部存儲器、寄存器和數據存儲形式
在計算機的電路中,用于存放運算數據的設備有內部存儲器(內存)和寄存器。內存是主要的存儲設備,對應的電路模塊稱之為內存條。寄存器的容量非常小,但是處于 CPU 的內部,因此訪問速度非常快。
數據以二進制形式存儲在內存或寄存器中,最小的存儲單位為位( bit ),每次可操作的最小存儲單位為字節( byte )。例如十進制整數 87 對應的二進制數為 01010111 ,至少需要 1 字節的存儲空間,在內存中的存儲形式如圖 3.1 所示。
圖 3.1 1 字節存儲空間模擬圖
內存地址用以表示字節單元在內存中的位置,計算機可通過內存地址訪問相應的內存單元。由于計算機的結構差異非常大,每次運行程序時內存的狀況也不相同,因此存放數據的位置也不一樣。在 C 語言程序中,變量是程序運行時動態劃分的內存單元,其本質是某一內存單元在程序中的映射。這樣,設計程序時不用考慮數據具體存放的位置,只用變量的名稱就可以訪問相應的內存地址。變量的聲明形式為:
[modifier] type name [= value];
其中, modifier 是修飾符, name 是變量的名稱, value 是在聲明時為變量賦予的初始值。聲明語句結束后,必須使用分號結束一行。
例如為了保存十進制整數 87 聲明了一個字符型變量,變量名為 a 。該變量規定的長度為 1 字節,程序運行時,操作系統會為變量 a 劃分出 1 字節的存儲空間,通過名稱 a 可對相應的存儲空間進行數據的讀或寫操作。如下列源代碼所示:
#include <stdio.h> // 包含基本輸入輸出頭文件
int main() // 主函數
{
char a; // 聲明字符型變量 a
a = 87; // 向變量 a 寫入數據
printf("%d", a); // 輸出變量 a 的數值到終端
return 0; // 退出程序
}
代碼運行時,“ char a ”表達式進行聲明操作,要求操作系統為字符型變量 a 分配內存。假設分配的內存地址為 0x30 ,名稱 a 就可以代表以該地址開始,長度為 1 字節的存儲空間。操作系統會將這一段內存空間保護起來,不會將同樣的空間分配給其他程序或變量。“ a = 87 ”表達式對 a 進行賦值操作,實際上就是把數據寫入到相應的內存單元中。程序結束后,操作系統會進行內存回收,分配給變量 a 的內存空間被標為空閑,其他程序可以獲得該空間。
不同數據類型的變量差別在于對應存儲空間的長度,該長度還會因為計算機硬件結構和編譯器類型的差別而不同。例如整型變量的長度對應于凌動處理器和 GCC 編譯器的長度為 4 字節,存放負整數 -87 的整型變量在內存中的存儲形式如圖 3.2 所示。
圖 3.2 4 字節存儲空間模擬圖
整型變量的長度為 4 個字節,但是只需要第 1 個字節的地址就能訪問到整個空間。因為變量類型已經為變量定義了存儲空間長度的信息,第 1 個字節的地址稱為首地址,只用通過偏移量就能得到其他字節的地址。
為了保存正負符號,存儲空間中的第 1 位是符號位。正數對應 0 ,負數對應 1 。被符號占用 1 位存儲空間后,字符型變量可儲存的最小值為 -2 7 ,最大值為 2 7 -1 ,即 -128 ~ 127 ;整型變量可存儲的最小值為 -2 31 ,最大值為 2 31 -1 。 0 被作為正數保存,因此正數最大值的數值比負數最大值的數值要少 1 。
存儲到寄存器的原理與存儲到內存非常相似,在變量聲明表達式前加入 register 標識符可將變量聲明為寄存器變量。如下例所示:
register int a; // 聲明寄存器整型變量 a
上述語句聲明了寄存器整型變量 a ,其長度同樣是 4 字節,并且有自己的地址。但是由于寄存器的資源非常有限,通常只將需要高頻率訪問的變量聲明為寄存器變量。操作系統和編譯器考慮到程序性能優化的問題,并不一定會將用戶聲明的寄存器變量保存在寄存器中,而是轉換為普通變量。
一切在代碼中直接出現的數據都是常量,例如“ a = 87 ”表達式中的數值 87 即常量。常量在內存中的存儲位置不被程序設計者關心,程序中也無法直接得到常量的地址,因此常量是不可修改的。由此我們可以用內存地址是否能被程序得到來區別常量與變量,這也是常量與變量的本質性區別。
3.1.2 數據類型
認識了數據存儲形式后,數據類型就比較容易理解。本小節所討論的數據類型指的是 C 語言中原始的數據類型,實際上數據類型直接的差別在于存儲空間長度。另外,還將涉及是否保存正負數符號,以及是否使用浮點方式來保存小數和指數。有正負符號的數稱為有符號數,沒有正負符號的數稱為無符號數。有符號數可以存儲負數,無符號數只能存儲正數。不使用浮點形式的數稱之為整數,使用浮點形式的數稱之為浮點數。除此以外還有一種空值類型,它不能保存任何數據,存儲空間長度為 0 。
C 語言的所有類型都是從 5 種最原始的類型發展而來的,見表 3.1 所示。
表 3.1 ANSI C 標準基本類型的字長與范圍表
類型 |
說明符 |
長度 |
值域 |
字符型 |
char |
1 字節 |
-128 ~ 127 |
整型 |
int |
4 字節 |
- 2147483648 ~ 2147483647 |
單精度浮點型 |
float |
4 字節 |
約精確到 6 位數 |
雙精度浮點型 |
double |
8 字節 |
約精確到 12 位數 |
空值型 |
void |
0 字節 |
無值 |
其中字符型和整型有無符號數和有符號數的差別,區別在于說明符前加入了 signed 和 unsigned 修飾符,見表 3.2 所示。
表 3.2 無符號與有符號類型的字長與范圍表
類型 |
說明符 |
長度 |
值域 |
無符號字符型 |
unsigned char |
1 字節 |
0 ~ 255 |
有符號字符型 |
signed char |
1 字節 |
-128 ~ 127 |
無符號整型 |
unsigned int |
4 字節 |
0 ~ 4294967295 |
有符號整型 |
signed int |
4 字節 |
- 2147483648 ~ 2147483647 |
由于字符型和整型默認為有符號數,所以通常在聲明時可以省略 signed 修飾符。另外,整型數據可以使用 short 和 long 修飾符來定義為短整型和長整型,見表 3.3 所示。
表 3.3 短整型和長整型的字長與范圍表
類型 |
說明符 |
長度 |
值域 |
短整型 |
short int |
2 字節 |
-32768 ~ 32767 |
整型 |
long int |
4 字節 |
- 2147483648 ~ 2147483647 |
長整型 |
long long int |
8 字節 |
-9.223372e+18 ~ 9.223372e+18 |
在使用短整型和長整型時,可省略 int 說明符。因此,聲明短整型可使用 short 修飾符作為說明符,聲明長整型可使用 long long 作為說明符,而 long 和 int 說明符是等價的。 unsigned 、 signed 修飾符與 short 、 long 說明符可以同時使用,如下例所示:
unsigned long long a; // 聲明無符號長整型變量 a
該行代碼聲明了無符號長整型變量 a ,其值域范圍為 0 ~ 2 64 -1 。在不使用科學計數法的條件下,變量 a 可以用來保存 C 語言中最大的正整數 2 64 -1 。
3.1.3 常量的形式
在 C 語言中,常量出現的形式共有 4 種,分別是直接常量、符號常量、枚舉常量和常量變量。其中,前 3 種是嚴格意義上的常量,而常量變量是一種特殊的常量。
1 .直接常量
所有在 C 語言源代碼中直接出現的數值、字符和字符串都是直接常量。如下列源代碼所示:
float pi = 3.141593; // 聲明單精度浮點型變量 pi 并賦值
char c = 'a'; // 聲明字符型變量 c 并賦值
printf("a cup of coffee"); // 在終端上輸出一行字符串
代碼中定義了 2 個變量,并使用直接常量為其賦值。其中,數值 3.141593 在類型上屬于浮點型常量,“ a ”屬于字符型常量。最后一行使用 printf() 函數輸出了字符串“ a cup of coffee ”,此處使用的是字符串常量。
注意:字符型常量必須使用單引號包圍,如 'a' 。字符串常量必須使用雙引號包圍,如 "a cup of coffee" 。如果使用雙引號包圍一個字符,如 "a" ,那么編譯器會認為這是一個字符串,并在其后自動加上字符串結束符。如果用單引號包圍一個字符串,如 'a cup of coffee' ,編譯器將認為這是語法錯誤,并拋出錯誤提示信息。
2 .符號常量
使用“ #define ”定義的常量稱之為符號常量。符號常量定義的形式為:
#define NAME value
其中, NAME 是符號常量的名稱, value 必須是一個直接常量。通常,符號常量聲明在源代碼的最上方,并且用大寫字母作為其名稱。如下例所示:
#include <stdio.h> // 包含基本輸入輸出頭文件
#define PI 3.141593 // 定義符號常量 PI
#define C 'a' // 定義符號常量 C
#define S "a cup of coffee" // 定義符號常量 S
int main() // 主函數
{
printf("%f/n", PI); // 輸出符號常量所代表的數值
printf("%c/n", C);
printf("%s/n", S);
return 0; // 主函數結束
}
代碼中定義了 3 個符號常量,在主函數中,這些符號常量所代表的數值被 printf() 函數輸出。我們可以簡單的認為,符號常量所做的僅僅是在源代碼中進行的字符串替換。編譯器編譯時,所以常量 PI 都會被替換為 3.141593 。
使用符號常量有三點好處,其一是易于記憶,例如我們可以為某個常量定義一個較容易理解的名稱,如 PI 。其二是表達簡潔,在上例的代碼中,僅僅用一個字母 S 就能代理整個字符串“ a cup of coffee ”。其三是數值容易修改,如果某個直接常量需要多次使用,一旦該常量的值必須被調整時,往往需要修改多處代碼;而使用符號常量代替了源代碼中所有直接常量后,修改時只用在符號常量定義部分修改。因此我們建議讀者盡量在代碼中使用符號常量。
注意:符號常量定義的行尾不需要使用分號“ ; ”結束該語句,否則會造成語法錯誤。
3 .枚舉常量
使用 enum 定義的常量稱之為枚舉常量,它是一種聚合類型。枚舉常量定義的形式為:
enum name {CON1 [= INT], CON2 [= INT], …};
其中, nume 為枚舉類型名稱, CON1 、 CON2 為枚舉成員名稱。枚舉成員的數值在定義后不可改變,并且能作為常量使用,被稱為枚舉常量。枚舉成員可用整型常量賦值,第 1 個枚舉成員默認值為 0 ,其后枚舉成員的默認值為前一個枚舉成員值加 1 的結果。如下例所示:
enum week {MON = 1, TUE, WED, THU, FRI, SAT, SUN}; // 定義枚舉類型和成員,將 MON 的值設置為 1
printf("%d", SAT); // 輸出成員 SAT 的值
代碼中定義了枚舉類型 week ,其中有 7 個成員,第 1 個成員 MON 的值設置為 1 。然后, printf() 函數輸出了成員 SAT 的值。根據枚舉類型的默認值規則可知, SAT 的值為 6 。
4 .常量變量
使用 const 修飾符聲明的變量稱之為常量變量。從本質上來說,常量變量依然屬于變量的一種,但是程序運行過程中不能修改其值。如下例所示:
const int id = 15; // 聲明常量變量 id 并賦值
代碼中聲明了常量變量 id ,并且為其賦值。聲明語句以后,任何賦值或修改常量變量數值的語句,都將造成編譯錯誤。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
