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

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