在C語(yǔ)言編程中使用變量的基礎(chǔ)教程
C語(yǔ)言在明面上將數(shù)的變量分為兩類,整型變量以及浮點(diǎn)數(shù),對(duì)應(yīng)著現(xiàn)實(shí)世界的整數(shù)和小數(shù)。
首先是整數(shù),使用了這么多的C語(yǔ)言之后,每當(dāng)在使用整數(shù)之時(shí)都會(huì)將其想象成二進(jìn)制的存在,而不是十進(jìn)制。原因在于,這是程序的本質(zhì)所在,稍有研究編譯器工作原理的都會(huì)發(fā)現(xiàn),在編譯器處理乘法乃至除法的時(shí)候,優(yōu)秀的編譯器總會(huì)想方設(shè)法的加快程序的速度,毫無疑問在所有運(yùn)算中移位運(yùn)算是最快速的"乘法"以及"除法":
1<<2 == 4 ,8>>2 == 2
而正常一個(gè)乘法相當(dāng)于十?dāng)?shù)次的加法運(yùn)算的時(shí)間消耗,移位則不用(除法的消耗更大,但是隨著CPU的進(jìn)步,這些差距正在逐漸縮小,就目前來看依舊是有著不小的差距但無論如何優(yōu)化,乘法時(shí)間都會(huì)大于加法)。正如前面所說,C語(yǔ)言設(shè)計(jì)之初便是給了程序員所有的權(quán)利,而程序員要做的就是掌控所有能掌控的,即便是數(shù)的計(jì)算亦是如此,比如在優(yōu)秀的編譯器看來:
2*7 ====> (2<<3) - 2 5*31 ====> (5<<5) - 5
毫無疑問經(jīng)過編譯器優(yōu)化后的代碼此前者要快許多。這就是為什么我們要將一個(gè)數(shù)看作二進(jìn)制,這不僅僅是表面,而是要在深層次的認(rèn)為它是二進(jìn)制,總體來說C語(yǔ)言的整型是非常簡(jiǎn)潔明了的總體分為 有符號(hào) 和 無符號(hào),很好理解只需要注意不要讓無符號(hào)數(shù)進(jìn)行負(fù)數(shù)的運(yùn)算,這里有一個(gè)原則,可以很好的規(guī)避這種無意之過,不把無符號(hào)類型變量和有符號(hào)類型變量放于同一運(yùn)算中,時(shí)刻記得保持式子的類型一致是設(shè)計(jì)時(shí)的保障。
浮點(diǎn)數(shù),由于實(shí)數(shù)域可以看作稠密的,故除了整數(shù)以外,還有無數(shù)的小數(shù),而小數(shù)在計(jì)算機(jī)中如何表示?一種無限的狀態(tài)是無法在計(jì)算機(jī)中被精確表示,所以有了浮點(diǎn)法,關(guān)于浮點(diǎn)法可以參考書籍《深入理解計(jì)算機(jī)系統(tǒng)》。
這里介紹的是在C語(yǔ)言中我們應(yīng)該如何正確使用浮點(diǎn)數(shù)?很多人(包括我)在初作之時(shí)總是想當(dāng)然的以為計(jì)算機(jī)是無所不能的,連人類都無法完全表達(dá)出來的小數(shù)計(jì)算機(jī)一定可以,實(shí)際上并非如此,在這里我可以說,計(jì)算機(jī)只是近似表達(dá),而最大的忌諱的便是將兩個(gè)浮點(diǎn)數(shù)進(jìn)行比較,此處介紹一種浮點(diǎn)數(shù)常用的比較方法,精確度法:
#define DISTANCE 0.00000001 ... float f_x_1 = 20.5; float f_x_2 = 19.5; if(f_x_1 - f_x_2 < DISTANCE) printf("They are Equal\n"); else printf("Different\n");
所以說,在很大程度上,當(dāng)你在程序中使用了浮點(diǎn)數(shù),又直接使用浮點(diǎn)數(shù)進(jìn)行比較,卻發(fā)現(xiàn)始終無法達(dá)到預(yù)期效果,那么你可以檢查一下,是否是這個(gè)原因,在這一點(diǎn)上,不得不說是C語(yǔ)言的一個(gè)缺憾。
指針變量,是一種比較特別的變量,以至于總是對(duì)它進(jìn)行特別對(duì)待。這里有幾個(gè)原則:
- 兩個(gè)不相關(guān)的指針進(jìn)行加減操作是無意義的
- 始終確保自己能夠找到分配的內(nèi)存
- 無論何時(shí)何地何種情況,都要記住,不使用未初始化的指針,不讓未使用的內(nèi)存持續(xù)存在。
指針在不同位的操作系統(tǒng)上的大小是不一樣的,但是在同一個(gè)操作系統(tǒng)下,無論什么類型的指針都是相同大小,這涉及到指針的尋址問題,(題外話:C語(yǔ)言的尋址實(shí)際上使用了匯編語(yǔ)言的間接尋址,有興趣的可以自行嘗試,方法之一,使用gcc編譯器的匯編選項(xiàng),產(chǎn)生匯編代碼,進(jìn)行一一比對(duì)),對(duì)于尋址一個(gè)籠統(tǒng)一些的說法便是
4Byte = 32bit 2^32 = 4G
所以32位的操作系統(tǒng)下C語(yǔ)言指針:
... size_t what = sizeof(void*); printf("%d", what); ...
輸出:
$root@mine: 4
對(duì)于大部分使用者來說,指針主要用來降低內(nèi)存消耗以及提高運(yùn)算效率的,這里設(shè)計(jì)許多學(xué)問,我也無法一一展示,比較有意思也常用的兩個(gè)東西便是遞增以及語(yǔ)法糖:++, ->
... int dupli_of_me[10] = {0};//也可以使用庫(kù)函數(shù)memset()進(jìn)行置0 int *point_to_me = dupli_of_me; int me = 100; while(point_to_me < (dupli_of_me + 10)) *point_to_me++ = me;
其中*point_to_me++ = me;在C語(yǔ)言應(yīng)用廣泛它相當(dāng)于是:
*point_to_me = me; point_to_me++;
的語(yǔ)法糖,對(duì)于++,在非必要的情況下,請(qǐng)使用前綴遞增,而非后綴遞增,原因是消耗問題,仔細(xì)想想這兩種遞增的區(qū)別在何處?
前綴遞增總是在原數(shù)上進(jìn)行遞增操作,然而后綴遞增呢?它首先拷貝一份原數(shù)放于別處,并且遞增這份拷貝,在原數(shù)進(jìn)行的操作完畢后,將這份拷貝再拷貝進(jìn)原數(shù)取代它,此中的操作涉及的更多,所以在非必要的情況下,請(qǐng)使用前綴遞增而不是后綴遞增(遞減也是同樣的道理)。
->則是在結(jié)構(gòu)體上使用的非常廣泛:
typedef struct data{ int test; struct data* next; }my_struct; ... my_struct temp; my_struct *ptemp = &temp; ptemp->test = 100; ptemp->next = NULL; if(temp.test == 100) printf("Correctly!\n"); else printf("That is impossible!\n"); ...
可以很清楚的看出其實(shí)ptemp->test便是(*ptemp).test的語(yǔ)法糖
變量限定
const 是最常用的變量限定符,它的意思是告訴編譯器,這個(gè)變量或者對(duì)象在初始化以后不能被改變,常用它來保護(hù)一些必要的返回值,參數(shù)以及常量的定義。
volatile 這個(gè)關(guān)鍵字常常被C語(yǔ)言教材所忽略,它很神秘。實(shí)際上確實(shí)如此,他的作用的確很神秘:一旦使用了,就是告訴編譯器,即使這個(gè)變量沒有被使用或修改其他內(nèi)存單元,它的值也可能發(fā)生變化。通俗的說就是,告訴編譯器,不要把你的那一套優(yōu)化策略用在我身上。
/* 此時(shí)我們將編譯器優(yōu)化等級(jí)提高到 -O2 */ int test_num = 100; //測(cè)試一個(gè)迭代加法 int nor_result = 0; volatile int vol_result = 0; /* 測(cè)試無volatile限定下,該程序的耗時(shí) */ for(int i = 0;i < 10000;++i) for(int j = 0;j < 10000;++j) nor_result += test_num;
接下來就是測(cè)試volatile限定下的代碼
for(int i = 0;i < 10000;++i) for(int j = 0;j < 10000;++j) vol_result += test_num;
在使用一些手段后,得到運(yùn)行時(shí)間,可以很清晰的看出差別,在我的機(jī)器上,i5-4CPU,得到的結(jié)果是后者比前者慢大概十五倍。 從某一些方向上證明了,volatile的一些作用,比如調(diào)試的時(shí)候,或者一些特殊用途。涉足不多,故不記錄。
變量說明
extern 用于將不同文件的,帶有外部鏈接性的變量引用到本文件中。所謂外部鏈接性就是可以被除本文件外的其他文件"看見"的變量,如全局變量,使用方法:
/* 以下為一個(gè)工程內(nèi)可見 */ /*file1.c*/ int glo_show;//對(duì)于該全局變量來說,它們?cè)诼暶鲿r(shí)無初始化,則默認(rèn)初始為0 int glo_print = 10;//聲明定義完成后,自動(dòng)分配內(nèi)存以存儲(chǔ)信息 ... /* file2.c */ extern glo_print; //僅僅是引用名字,并不會(huì)額外分配空間 //所以,只需要寫正確變量名字即可,后方的初始化無須完全 //因?yàn)樽兞康某跏蓟x只能有一次。 void print() { printf("The Globle Value is %d \n", glo_print); }
auto 可以姑且忽略,因?yàn)闆]有什么實(shí)際意義。
變量獲取
格式化輸入輸出在C語(yǔ)言的初學(xué)中使用的比較頻繁,但是到后期會(huì)發(fā)現(xiàn),由于I/O操作過于消耗資源,換句話來說就是會(huì)極大影響程序的執(zhí)行效率,會(huì)漸漸的在發(fā)行版程序中消除。
常見格式化輸入標(biāo)準(zhǔn)函數(shù): sacnf, fscanf, sscanf
對(duì)于常見的使用不贅述,有兩種比較不常見的格式:`%[]` 和 `%*`,
前者是用于限制讀取類型,常見于字符串的過濾(不是真正的過濾)
scanf("%d %[a-z]", &tmp, str); scanf("%d %[^i]", &tmp, str); scanf("%d %[^,]", &tmp, str);
假設(shè)輸入的是:22 hello,string to me!
讀取到的分別為:22 hello 和 22 hello,str 和 22 hello
后者則是忽略第一個(gè)輸入:
scanf("%*d %d", &tmp);
假設(shè)輸入的是:22 33
讀取到的則是:33
其中開頭的%*d忽略的輸入,必須和其類型匹配,例如輸入:string 33則會(huì)讀取失敗。
也可以將其解讀為文件寬度,例如在使用printf格式化輸出的時(shí)候:
char str[10] = "dir"; printf("%*s%s",4 ,"" , str); /* 輸出: dir */ 四個(gè)空白占位
但是實(shí)際上scanf并不太好用,所謂的好用指的是功能上以及設(shè)計(jì)上的缺陷,總是讓很多人摸不著頭腦的出了錯(cuò),往往很難調(diào)試。例如它會(huì)將每一行輸入的\n保留在輸入流里面,這個(gè)缺陷導(dǎo)致如果不明所以得人將其與其他的輸入函數(shù),例如fgets或者gets配合會(huì)出現(xiàn)差錯(cuò)。
上一篇:C語(yǔ)言實(shí)現(xiàn)選擇排序、直接插入排序、冒泡排序的示例
欄 目:C語(yǔ)言
下一篇:C語(yǔ)言完美實(shí)現(xiàn)動(dòng)態(tài)數(shù)組代碼分享
本文標(biāo)題:在C語(yǔ)言編程中使用變量的基礎(chǔ)教程
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2487.html
您可能感興趣的文章
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)數(shù)怎么表達(dá)
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎么打出三角函數(shù)的值
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解
- 01-10c++中inline的用法分析
- 01-10如何尋找數(shù)組中的第二大數(shù)
- 01-10C++大數(shù)模板(推薦)


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10delphi制作wav文件的方法
- 04-02jquery與jsp,用jquery
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子