網(wǎng)站開(kāi)發(fā)數(shù)據(jù)庫(kù)課程設(shè)計(jì)專注于網(wǎng)站營(yíng)銷服務(wù)
編程基礎(chǔ) - 變量與常量
返回序言及專欄目錄
文章目錄
- 編程基礎(chǔ) - 變量與常量
- 前言
- 一、變量是什么?
- 二、為什么要有變量
- 三、局部變量和全局變量
- 四、常量
- 五、只讀變量
- 小結(jié)
前言
變量是編程最重要知識(shí)點(diǎn)之一,從根本上講,編程就是對(duì)數(shù)據(jù)的操作,讓數(shù)據(jù)按我們?cè)O(shè)定的邏輯進(jìn)行運(yùn)算。變量是用于存儲(chǔ)數(shù)據(jù)的內(nèi)存地址的人性化表示方式。在shell中,數(shù)據(jù)類型比較簡(jiǎn)單,它屬于弱數(shù)據(jù)類型的編程語(yǔ)言,簡(jiǎn)而言之它自動(dòng)幫你處理了有關(guān)數(shù)據(jù)類型的工作。
一、變量是什么?
顧名思義,變量就是可以變化的量。很多書(shū)上這么寫(xiě)的,筆者也學(xué)習(xí)一下,但這是一句沒(méi)什么意義的話,純粹是為了讓人更迷糊的說(shuō)法。
變量是內(nèi)存地址的別名,別名就是外號(hào)的文雅說(shuō)法。這是某本知名C/C++語(yǔ)言著作上的說(shuō)法(具體是哪本書(shū)我忘記了),當(dāng)然外號(hào)這句是筆者加的,從這個(gè)角度比較容易解釋什么是變量。
都知道計(jì)算機(jī)系統(tǒng)中內(nèi)存是必不可少的,數(shù)據(jù)是暫時(shí)存儲(chǔ)在內(nèi)存當(dāng)中的。所以當(dāng)你用PS修圖的時(shí)候,如果沒(méi)有保存就突然停電,來(lái)電以后你就要找出原圖重新開(kāi)始修1。這就是因?yàn)閮?nèi)存中的數(shù)據(jù)沒(méi)有被保存到我們的硬盤(pán)上。
既然數(shù)據(jù)是先存儲(chǔ)在內(nèi)存中的,不管內(nèi)存有多大,把內(nèi)存細(xì)分成字節(jié)來(lái)管理(1G內(nèi)存約有1千兆個(gè)字節(jié),兆表示10的6次方)。這個(gè)數(shù)量是極其龐大的,大到我們光用二進(jìn)制寫(xiě)一個(gè)內(nèi)存地址就要寫(xiě)很久,程序在退休前肯定寫(xiě)不完,還得找個(gè)繼承人接著寫(xiě)!更關(guān)鍵的是你無(wú)法記住這么長(zhǎng)的一串?dāng)?shù)字表示的地址。所以我們要有一個(gè)好記的簡(jiǎn)短的名字來(lái)給我們所使用到的內(nèi)存地址命名。
當(dāng)然實(shí)際情況肯定是比這復(fù)雜多了,內(nèi)存實(shí)際是由系統(tǒng)管理的,系統(tǒng)分配給你哪塊就用哪塊。變量只是給這部分內(nèi)存起了一個(gè)名字。希望這么解釋變量能讓讀者有個(gè)概念,變量不是什么稀奇的玩意,就是一個(gè)內(nèi)存地址的名字。
那為什么那么多書(shū)都說(shuō)變量是可以變化的量呢?那些作者比筆者我傻嗎?肯定不是的,看下一節(jié)的演示你就明白,為什么說(shuō)它是可以變化的量了。
二、為什么要有變量
對(duì)啊,我不用變量不就行了?要這種抽象的概念來(lái)做什么呢?假設(shè)我們要求計(jì)算機(jī)計(jì)算1+2等于多少,不用變量你輸入echo $((1+2))
,計(jì)算機(jī)也會(huì)告訴你是3,沒(méi)毛病。那我們要學(xué)習(xí)高斯同學(xué)的1加到100呢?你把1到100都寫(xiě)一遍嗎?顯然沒(méi)有這個(gè)必要,加數(shù)是有規(guī)律的,每次加1,那么我們可以在變量被使用后,每次讓它加上1,就是下一次計(jì)算的加數(shù)了。計(jì)算出的和也是一個(gè)道理,先讓和是1+2的結(jié)果,下一次計(jì)算,我們讓和加上3,以此類推,就輕松的得出結(jié)果了。當(dāng)然這個(gè)高斯同學(xué)小學(xué)時(shí)的例子太簡(jiǎn)單,我們來(lái)個(gè)復(fù)雜點(diǎn)的著名例子:
有數(shù)列1,1,2,3,5,8,13 … 我們想知道第37個(gè)數(shù)是什么,之所以要37個(gè)是怕讀者太聰明,口算得出結(jié)果~ 顯然它的規(guī)律是:每個(gè)數(shù)都是前兩個(gè)數(shù)的和。我們可以編寫(xiě)程序來(lái)計(jì)算:
#!/bin/sh
a=1
b=1
i=3
while [ $i -lt 38 ]dotmp=$aa=$bb=$tmp+$bi=$i+1done
echo $b
我就不信有口算這么厲害的人,沒(méi)算出來(lái)吧~ 我們看看具體邏輯細(xì)節(jié):
- while是一個(gè)循環(huán),只要當(dāng)變量
i
小于38,它就一直在do
和done
之間循環(huán)。 - 每次循環(huán)程序會(huì)把變量
a
的值賦值給變量tmp
(用于記錄變量a的值,下行a的值會(huì)改變)。 - 再把變量
b
的值給變量a,把變量tmp
和b
的值加起來(lái)給b。 - 用變量
i
加上1表示計(jì)算了一次,直到變量i
等于37,while循環(huán)條件不滿足了,程序跳出循環(huán)。echo
顯示結(jié)果
這個(gè)程序的4個(gè)變量在運(yùn)行時(shí)不停的改變自身存儲(chǔ)的值。正是因?yàn)樽兞康闹翟诔绦虻倪\(yùn)算過(guò)程中不停的變化,我們才叫它變量!變量的存在才讓程序變得簡(jiǎn)潔、靈活。
三、局部變量和全局變量
通常,我們將變量分為局部變量和全局變量。在函數(shù)內(nèi)部被定義的變量,我們叫它局部變量,它只在函數(shù)內(nèi)部作用。當(dāng)函數(shù)被調(diào)用結(jié)束時(shí),這個(gè)變量也就銷毀了。相對(duì)的在函數(shù)外部定義的變量,我們稱之為全局變量,這種變量在整個(gè)程序運(yùn)行期間起作用。
這里引出了函數(shù)和調(diào)用的概念,函數(shù)我們可以認(rèn)為是一個(gè)功能模塊,它是為了實(shí)現(xiàn)某一個(gè)功能而定義的代碼集合。局部變量只在定義它的函數(shù)內(nèi)部有效,這個(gè)函數(shù)就是它的作用域。
如果局部變量和全局變量重名,全局變量會(huì)在同名局部變量的函數(shù)內(nèi)被屏蔽。因?yàn)槿趾途植渴莻€(gè)相對(duì)的概念,相對(duì)于整個(gè)系統(tǒng)來(lái)說(shuō),每個(gè)程序內(nèi)定義的變量都是局部變量。
這么分是有意義的:主要是為了節(jié)省內(nèi)存空間的占用,局部變量用完就銷毀的特性避免內(nèi)存被無(wú)意義的數(shù)據(jù)填滿。同時(shí)也能讓程序員不用關(guān)心程序的其它部分定義了什么變量,不然多個(gè)程序員協(xié)作,大家就天天為了變量命名權(quán)打架了。
通常程序員會(huì)把全局變量寫(xiě)在程序的開(kāi)始部分,這個(gè)開(kāi)始并不一定是程序代碼的最上部,而是指程序執(zhí)行的入口部分,很多時(shí)候這個(gè)入口都寫(xiě)在程序的最下面部分。當(dāng)然寫(xiě)在代碼最前面也是一種好習(xí)慣。這么做是為了閱讀程序方便,實(shí)際上只要變量在被使用前定義就行。
大多數(shù)編程語(yǔ)言規(guī)定,變量名只能用大小寫(xiě)字母和數(shù)字、下劃線_ 組成,且數(shù)字不能位于變量名開(kāi)頭。
我們用幾個(gè)例子來(lái)看一下:
age=23 # 很好的變量名,簡(jiǎn)單又有意義
age_1=24 # 較好的變量名,還可以命名多個(gè)類似的
_age_=23 # 通常程序員喜歡用下劃線在前的變量表示在內(nèi)部調(diào)用的
_tmp=23 # 也有程序員喜歡用這種變量名表示臨時(shí)的
years_old=23 # 有很多程序員喜歡這樣命名,也常用于函數(shù)命名
yearsOld=23 # 這叫駝峰命名,也是很常用的,也是通常用于函數(shù)名
AGE=23 # 符合規(guī)則,但通常不用全大寫(xiě)定義變量
abc=23 # 符合規(guī)則,但沒(méi)有意義,不建議使用
1a=23 # 錯(cuò)誤的命名方式
四、常量
既然有變量,相應(yīng)的就有常量。變量是可以變化的量,常量就是通常不變的量。比如Linux系統(tǒng)本身就定義了一些常量:
echo $HOME # 家目錄,和~的作用一樣
/root
echo $PWD
/tmp/home/root
echo $PATH
/koolshare/bin:/koolshare/scripts:/opt/bin:/opt/sbin:/bin:/usr/bin:/sbin:/usr/sbin:/home/admin:/mmc/sbin:/mmc/bin:/mmc/usr/sbin:/mmc/usr/bin:/opt/sbin:/opt/bin:/opt/usr/sbin:/opt/usr/bin
echo $SHELL
/bin/sh
echo $IFS# 這里有一個(gè)空格
echo $USER
admin
echo $UID
0 # 路由器上這個(gè)命令可能沒(méi)有顯示
echo $HOSTNAME
RT-AC68U-F2A7
以下是一些常用的系統(tǒng)已定義常量:
- HOME 家目錄,和
~
的作用一樣 - PWD 當(dāng)前路徑
- PATH 環(huán)境常量,可執(zhí)行程序的搜索路徑
- SHELL shell所在路徑
- IFS 分隔符,默認(rèn)為空格,可以用于把字符串按IFS分割成數(shù)組
- USER 當(dāng)前用戶名
- UID 當(dāng)前用戶的ID,相應(yīng)的就有GID,沒(méi)加入組就沒(méi)有
- HOSTNME 主機(jī)名
我們?cè)诔绦蛑幸部梢宰远x常量,它和變量沒(méi)有本質(zhì)的區(qū)別。命名規(guī)則也是一樣,只是通常用全大寫(xiě)來(lái)命名。這只是一個(gè)概念,表示這是個(gè)常量,我們不是不能修改它的值,只是不想修改它。包括系統(tǒng)定義的常量,我們也是可以修改的,最常見(jiàn)之一就是IFS:
admin@RT-AC68U-F2A7:/tmp# touch test.txt
admin@RT-AC68U-F2A7:/tmp# echo "this is a test!" > test.txt
admin@RT-AC68U-F2A7:/tmp# cat test.txt
this is a test!
admin@RT-AC68U-F2A7:/tmp# for i in $(cat test.txt)
> do
> echo $i
> done
this
is
a
test!
上述代碼中touch
是一個(gè)新建空白文件命令,如果要建立的文件已經(jīng)存在,命令會(huì)改變這個(gè)文件的最后訪問(wèn)時(shí)間記錄,并不會(huì)改變文件內(nèi)容。>
重定向符,用于將標(biāo)準(zhǔn)輸出(stdout)重定向到文件中。如果目標(biāo)文件不存在則創(chuàng)建新文件;若已經(jīng)存在同名文件,會(huì)被覆蓋。最上面的touch
是可以不寫(xiě)的,只是作者的習(xí)慣。
我們把這個(gè)字符串改一改再來(lái)測(cè)試:
admin@RT-AC68U-F2A7:/tmp# echo "this,is,a,test!" > test.txt
admin@RT-AC68U-F2A7:/tmp# cat test.txt
this,is,a,test!
admin@RT-AC68U-F2A7:/tmp# for i in $(cat test.txt)
> do
> echo $i
> done
this,is,a,test!
上面這個(gè)例子,很好的說(shuō)明了IFS的作用,默認(rèn)以空格來(lái)分割字符串,當(dāng)我們的字符串改成以逗號(hào)分隔時(shí),因?yàn)樽址袥](méi)有空格,所以不進(jìn)行分割了。下面我們就修改這個(gè)IFS常量:
admin@RT-AC68U-F2A7:/tmp# cat test.txt
this,is,a,test!
admin@RT-AC68U-F2A7:/tmp# IFS_OLD=$IFS
admin@RT-AC68U-F2A7:/tmp# IFS=","
admin@RT-AC68U-F2A7:/tmp# for i in $(cat test.txt)
> do
> echo $i
> done
this
is
a
test!
admin@RT-AC68U-F2A7:/tmp# IFS=$IFS_OLD
這個(gè)例子說(shuō)明了,常量是可以修改的,即使它是系統(tǒng)定義的常量。通常我們修改了IFS后要第一時(shí)間給改回去。所以程序第一步就是將原IFS值先保存在IFS_OLD中,最后又改回去了。
五、只讀變量
如果我們實(shí)在不想一個(gè)變量或常量被無(wú)意中修改,在c/c++中有靜態(tài)變量的概念,shell中也有只讀變量可以做到。你可以定義一個(gè)通常不能改變的變量:
admin@RT-AC68U-F2A7:/tmp/home/root# readonly age=23
admin@RT-AC68U-F2A7:/tmp/home/root# echo $age
23
admin@RT-AC68U-F2A7:/tmp/home/root# age=24
-sh: age: is read only
admin@RT-AC68U-F2A7:/tmp/home/root# unset age
admin@RT-AC68U-F2A7:/tmp/home/root# echo $age
23
admin@RT-AC68U-F2A7:/tmp/home/root# unset -v age
admin@RT-AC68U-F2A7:/tmp/home/root# unset age
admin@RT-AC68U-F2A7:/tmp/home/root# echo $age
23
unset
用于刪除一個(gè)變量。這樣定義的變量是只讀的,一般情況不能修改,在shell中還設(shè)計(jì)成很難修改,刪除都不能。當(dāng)然它只在內(nèi)存中,你重啟系統(tǒng)就沒(méi)了。有人說(shuō)服務(wù)器讓你隨便重啟嗎?對(duì)的,所以少用這個(gè)定義變量的方法。還有人說(shuō)用unset -v 變量名
可以解除只讀屬性,至少在路由的這個(gè)2.6版內(nèi)核中是不可行的,這真是夠頑強(qiáng)的。筆者也沒(méi)在多個(gè)發(fā)行版Linux中測(cè)試過(guò)這個(gè),估計(jì)在ubuntu20.4中是不可行的,筆者查過(guò)這個(gè)命令的幫助文件:
sal@sal-laptop:~$ uname -a
Linux sal-laptop 5.4.0-169-generic #187-Ubuntu SMP Thu Nov 23 14:52:28 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
sal@sal-laptop:~$ unset --help
unset: unset [-f] [-v] [-n] [名稱 ...]取消設(shè)定 shell 變量和函數(shù)的值和屬性。對(duì)每一個(gè) NAME 名稱,刪除對(duì)應(yīng)的變量或函數(shù)。選項(xiàng):-f 將每個(gè) NAME 視為函數(shù)-v 將每個(gè) NAME 視為變量-n 將每個(gè) NAME 視為名稱引用,只取消其本身而非其指向的變量不帶選項(xiàng)時(shí),unset 首先嘗試取消設(shè)定一個(gè)變量,如果失敗,再嘗試取消設(shè)定一個(gè)函數(shù)。某些變量不可以被取消設(shè)定;參見(jiàn) `readonly'。退出狀態(tài):返回成功,除非使用了無(wú)效的選項(xiàng)或者 NAME 名稱為只讀。
從這個(gè)幫助文件可以看出,函數(shù)也是用unset
來(lái)刪除的,所以函數(shù)和變量是有共通之處的。從本質(zhì)上講,變量和函數(shù)都是用戶在內(nèi)存中存儲(chǔ)的一串0和1。
小結(jié)
本章介紹了變量和常量的概念,需要了解變量和常量的命名規(guī)則以及通常用法,這個(gè)規(guī)則在絕大多數(shù)的編程語(yǔ)言中是通用的。
返回專欄目錄 <<<
某些軟件設(shè)計(jì)了隨時(shí)保存臨時(shí)文件的功能,在我們使用軟件的過(guò)程中定時(shí)或檢測(cè)到活動(dòng)就保存到硬盤(pán)上的一個(gè)文件中。當(dāng)停電后重啟電腦,可能可以找到一個(gè)臨時(shí)文件來(lái)恢復(fù)大部分?jǐn)?shù)據(jù)。 ??