C語言基本概念(下)【C語言入門到精通】

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 是不同的。

聲明變量的理由

  1. 把所有變量放在一處,方便讀者查找和理解程序的用途。
  2. 聲明變量可以促使你在編寫程序之前做好計劃(比如你的程序要用什麼變量,你可以提前規劃)。
  3. 聲明變量有助於發現程序中的小錯誤,如拼寫錯誤。
  4. 不提前聲明變量,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 ,這和我們想的不一樣,我們知道結果應該是個小數,那麼這是爲什麼呢?

我想可能的原因有兩個:

  1. %d 將小數轉換爲整數
  2. 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


以上就是本次的內容。

如果文章有錯誤歡迎指正和補充,感謝!

最後,如果你還有什麼問題或者想知道到的,可以在評論區告訴我呦,我可以在後面的文章加上你們的真知灼見。

關注我,看更多幹貨!

我是程序圓,我們下次再見。


  1. 每個程序都是其他程序不合適的一部分。 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章