C語言基本結構(下)
Every program is a part of some other program and rarely fits.1
碼字不易,對你有幫助 點贊/轉發/關注 支持一下作者
思維導圖
寫在前面
如果只是寫個人學習總結的博客很容易,簡單寫一些感悟然後貼上代碼走人就可以了,甚至不用校審。但是我命名本系列爲【C語言必知必會】幫助你從入門到精通 C語言,那勢必要“事無鉅細”一些:既要考慮到沒有基礎的初學者,又不能止於基礎。所以本教程適合各類人羣學習,只要你看就一定會有幫助。
本教程是本人純手打併排版,校審由我與我的搭檔湯圓君一起完成的。你們看這一篇文章我要寫好幾個小時。如果文章對你有幫助,請不要“白嫖”。支持一下作者,作者會發更多幹貨好文。
》 此符號表示該內容以後的章節會講解,此章節內不要求理解。
簡單的程序結構
下面是一個簡單的程序,身高是給出的,體重是在程序中得到的,我們輸出的是體重與身高/體重的值。
這裏我們更注重的是程序的結構而非程序本身。
示例
1. 類型
每一個變量都有類型(type)。類型用來描述變量的數據的種類,也稱數據類型。
數值型變量的類型決定了變量所能存儲的最大值與最小值,以及是否允許小數點後出現數字。
示例中只有一種數據類型:int
int(integer):即整型,表示整數。
數據類型還有很多,目前除了 int 以外,我們只再使用另一種:
float(floating-point): 浮點型,可以表示小數
注意:雖然 float 型可以帶小數,但是進行算術運算時,float 型要比 int 型慢,而且 float 通常只是一個值的近似值。(比如在一個float 型變量中存儲 0.1, 但其實可能這個變量的值爲 0.09999987,這是舍入造成的誤差)
題外話:我當時學的時候,就沒有人告訴我這些知識,你們如果現在是初學,我都感覺到羨慕,你們要少走多少彎路啊!
2. 關鍵字
int 與float 都是C語言的關鍵字(keyword),關鍵字是語言定義的單詞,不能用做其他用途。比如不能用作命名函數名與變量名。
關鍵字:斜體代表C99新增關鍵字
auto | enum | unsigned | break | extern |
---|---|---|---|---|
return | void | case | float | short |
volatile | char | for | signed | while |
const | goto | sizeof | continue | if |
static | default | struct | do | int |
switch | double | long | typedef | else |
register | union | |||
restrict | inline | _Bool | _Complex | _Imaginary |
如果關鍵字使用不當(關鍵字作爲變量名),編譯器會將其視爲語法錯誤。
保留標識符(reserved identifier):下劃線開頭的標識符和標準庫函數名(如:printf())
C語言已經指定了這些標識符的用途或保留了它們的使用權,如果你使用它們作爲變量名,即使沒有語法錯誤,也不能隨便使用。
3. 聲明
聲明(declaration):在使用變量(variable)之前,必須對其進行聲明(爲編譯器所作的描述)。
聲明的方式爲:數據類型 + 變量名(程序員自己決定變量名,命名規則後面會講)
示例中的 int weight
完成了兩件事情。第一,函數中有個變量名爲 weight。第二,int 表明這個變量是整型。
編譯器用這些信息爲變量 weight 在內存中分配空間。
C99 前,如果有聲明,聲明一定要在語句之前。(就像示例那樣,函數體中第一塊是聲明,第二塊纔是語句。)
C99 和 C11 遵循 C++ 的慣例,可以把聲明放在任何位置。即可以使用時再聲明變量。以後C程序中這種做法可能會很流行。但是目前不建議這樣。
就書寫格式而言,我建議將聲明全部放在函數體頭部,聲明與語句之間空出一行。
4. 命名
weight,height 都是標識符,也就是一個變量,函數或其他實體的名稱。因此,聲明將特定標識符與計算機內存的特定位置聯繫起來,同時也就確定了存儲在某位置的信息類型或數據類型。
給變量命名時要使用有意義的變量名或標識符。如果變量名無法清楚的表達自身的用途,可以在註釋中進一步說明,這是一種良好的編程習慣與編程技巧。
C99 與 C11 允許使用更長的標識符,但是編譯器只識別前 63個字符。對於外部標識符,只允許 31 個字符。事實上,你可以使用更長的字符,但是編譯器可能忽略超出的部分。(比如有兩個標識符都是 64 個字符,但只有最後一個字符不同。編譯器可能會視其爲同一個名字,也可能不會。標準並未定義在這種情況下會發生什麼。)
命名規則:可以用小寫字母,大寫字母,數字和下劃線(_)來命名。名稱的第一個字符必須是字符或下劃線,不能是數字
操作系統和C庫經常使用一個下劃線或兩個下劃線開始的標識符(如:_kcab),因此最好避免在自己的程序中使用這種名稱。(避免與操作系統和c庫的標識符重複)
C語言的名稱區分大小寫。即:star,Star,STAR 是不同的。
聲明變量的理由:
- 把所有變量放在一處,方便讀者查找和理解程序的用途。
- 聲明變量可以促使你在編寫程序之前做好計劃(比如你的程序要用什麼變量,你可以提前規劃)。
- 聲明變量有助於發現程序中的小錯誤,如拼寫錯誤。
- 不提前聲明變量,C程序編譯將無法通過
5. 賦值
賦值(assignment):變量通過賦值的方式獲得值。
示例中,weight = 160;
是一個 賦值表達式語句。意思是“把值 160 賦給 變量 weight”。
在執行 int weight;
時,編譯器在計算機內存中爲變量 weight 預留的空間,然後在執行這行代碼時,把值存儲在之前預留的位置。可以給 weight 賦不同的值,這就是 weight 之所以被稱爲變量的原因。
注意:
-
該行表達式將值從右側賦到左側。
-
該語句以分號結尾。
-
=
在計算機中不是相等的意思,而是賦值。我們在讀weight = 160;
時,我們應該這麼讀:“將 160 賦給 weight” -
==
表示相等
6. printf() 函數
printf(“我的體重是:%d斤\n,身高與體重的比爲:%d”, weight, height / weight);
這是我們示例中的 printf 函數,我們來看兩個不那麼複雜的:
main(void);
printf("Hi");
首先,printf() 的 圓括號是不是很像 main() ?這表示 printf 是一個函數名,它也是一個函數。圓括號內的內容是從 main() 函數傳遞給 printf() 函數的信息。該信息被稱爲參數,更確切的說,是實際參數(actual argument),簡稱實參。
既然是函數,它其實也是像我們看到的 main函數一樣,也有函數頭和函數體。
printf() 函數是一個庫函數,庫函數我們上一節講函數種類時說過,這是一種不需要程序員去寫的,只需要引用頭文件 stdio.h
就可以直接使用的。但是我們應該知道這一點,詳細情況我們後面會說講。
當程序運行到 printf() 函數這一行時,控制權被轉給了printf()函數。函數執行結束後,控制權被返回至主調函數(calling function),該例中是 main() 。
printf() 函數的作用是向我們的顯示器輸出內容。
此例中,printf() 函數的括號內 分爲兩部分,一部分在雙引號內,另一部分在雙引號外,它們中間以逗號隔開。雙引號外有兩個參數 weight 和 height / weight ,他們分別是變量和表達式(含有常量,變量和運算符的式子),也是指定要被打印的參數(打印到你的屏幕上)。
我們發現,最終我們屏幕上看到的是引號內的內容。我們可以來看一下輸出的內容:
我的體重是:160斤
身高與體重的比爲:1
我們發現:首先引號內的 %d
和\n
並沒有被輸出,%d
的位置被替換成了一個整數。爲什麼會這樣呢?
\n
代表一個換行符(newline character)。對於 printf 函數來說,它的意思是:“在下一行的最左邊開始新的一行”。也就是說換行符和在鍵盤上按下 Enter按鍵相同。既然如此,爲何不在鍵入 printf() 參數時直接使用 Enter鍵呢?因爲編輯器可能認爲這是直接的命令,而不是存儲在源代碼中的指令。換句話說,如果直接按下 Enter鍵,編輯器會退出當前行並開始新的一行。但是,換行符會影響程序輸出的(顯示)格式。
換行符是一個轉義序列(escape sequence)。轉義序列用於難以表示或無法輸入的字符。如,\t
代表 Tab鍵,即製表符。\b
代表 Backspace鍵,即退格鍵。我們在後面會繼續討論。
這樣就解釋了爲何一行的printf() 函數會輸出兩行。
以下這部分不能理解可以只看結論,能理解更好。
在解釋 %d 之前我們先來看一下,weight 和 height / weight 所代表的值。
weight 是被賦值爲 160 的,所以 weight 的值就是 160
C語言中,/
表示除法, *
表示乘法。
那麼 height / weight 的值是多少呢?我們現在不知道這個表達式的值是多少,但是我們知道這個它肯定代表 180 / 160
而最終輸出的值是 1 ,這和我們想的不一樣,我們知道結果應該是個小數,那麼這是爲什麼呢?
我想可能的原因有兩個:
- %d 將小數轉換爲整數
- 180 / 160 本身在C語言中的值就是整數
我們來測試一下:
int main(void) {
int a = 3;
int b = 2;
float c = 1.1f;//f 表示1.1是浮點數
printf("%d\n", c);//%d 用來輸出整型
printf("%f\n", a / b);//%f 用來輸出浮點型
return 0;
}
輸出:
-1717986918
0.000000
輸出並不是我們想要的內容,我們來看一下編譯器的警告:
編譯器警告:
“printf”: 格式字符串“%d”需要類型“int”的參數,但可變參數 1 擁有了類型“double”
“printf”: 格式字符串“%f”需要類型“double”的參數,但可變參數 1 擁有了類型“int”
可以不去理解報錯的內容。輸出與報錯至少說明了一點:
%d 在我的編譯器上無法輸出浮點型;整型 / 整型 不是浮點型。
那就說明了原因2是對的,即:180 / 160 的值就是 1
爲什麼 180 / 160 == 1
(180 / 160 的值是 1)呢?
因爲 weight 和 height 都整數,它們相除結果取整數(向下取整)。
如何輸出 float 類型的浮點數?
printf("%f", 2.0f);
%d
是一個佔位符,其作用是指明 num 值的位置。d 代表 以十進制的格式。
還有一點要注意的是,在示例中,第二個輸出的整數的參數(height / weight )是一個表達式,我們也可以在程序中添加一個新的變量,然後用這個變量代替上面的表達式作爲 printf() 的參數。如:
int main(void)
{
int height = 180;
int weight, scale;//scale:比例
weight = 160;
scale = height / weight;
printf(“我的體重是:%d斤\n,身高與體重的比爲:%d”, weight, scale);
return 0;
}
合理的使用表達式作爲函數的參數可以簡化程序。
也說明在任何需要數值的地方,都可以使用具有相同類型的表達式。
7. 初始化
當程序開始執行時,某些變量會被自動設置爲0,而大多數不會。沒有默認值並且尚未在程序中被賦值的變量時未初始化的(uninitialized)。
如果試圖訪問未初始化的變量,可能會得到不可預知的值。在某些編譯器中,可能會發生更壞的情況(甚至程序崩潰)。
我們可以用賦值的辦法給變量賦初值,但還有更簡潔的做法:在變量聲明中加入初始值。
例如示例中的 int height = 180
數值 180 就是一個初始化式(initializer)。
同一個聲明中可以對任意數量的變量進行初始化。如:
int a = 10, b = 15, c = 20;
上述每個變量都擁有屬於自己的初始化式。接下來的例子,只有 c 有初始化式,a,b沒有。
int a, b, c = 20;
參考資料:《C Primer Plus》《C語言程序設計:現代方法》
本文GitHub已更新,所有教學和練習代碼都會上傳上去,歡迎 star !
https://github.com/hairrrrr/C-CrashCourse
以上就是本次的內容。
如果文章有錯誤歡迎指正和補充,感謝!
最後,如果你還有什麼問題或者想知道到的,可以在評論區告訴我呦,我可以在後面的文章加上你們的真知灼見。
關注我,看更多幹貨!
我是程序圓,我們下次再見。
每個程序都是其他程序不合適的一部分。 ↩︎