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