C是一種具備模塊化設計的命令式編程語言,具備簡約、直觀的設計風格,與相對清晰、簡單的語言結構。編程
在談C的語言結構以前,須要先解釋一些基本元素的含義。編程語言
表達式是一個或多個變量、常量、函數與運算符按照特定規則的組合,表達式根據特定的優先級與運算符進行計算並返回一個值。模塊化
注意:單個變量、常量或函數名也是一個表達式。函數
如下面表達式爲例:學習
var = fn(1) + 5優化
其中var、fn、一、5都是表達式,其返回值爲自身的值;fn(1)也是一個表達式,返回函數調用的返回值;fn(1) + 5也是一個表達式,返回算術運算的結果;var = fn(1) + 5也是一個表達式,返回賦值號左邊的值,此例中此值被丟棄。spa
特別地,調用返回值爲void類型的函數將返回一個void類型的值,但此值沒法被使用,只能丟棄。設計
若是一個表達式不是其餘表達式的子表達式,則稱這個表達式爲「完整表達式」。3d
如下面幾個語句爲例:指針
var = 1 + 2;
fn(var + 1);
if (var + 1) ;
✪ 表達式語句中的整個表達式爲完整表達式,如上面的var = 1 + 2和fn(var + 1),但第二行的var + 1不屬於完整表達式。(函數調用其實是運算符()對函數指針和參數進行運算)
✪ if、while、switch括號中的表達式以及for括號中的每一個份量都是完整表達式,因此第三行的var + 1是完整表達式。
除了返回值之外對程序形成的其餘影響稱爲反作用。好比修改變量的值,執行I/O操做等。
對於以下表達式:
var = 5
表達式的返回值爲5,反作用爲將5賦值給變量var。
而對於如下表達式:
1 + 2
表達式返回3,沒有反作用。
一般提及「反作用」,老是以爲可有可無或儘可能避免,但對於命令式編程語言來講,反作用纔是程序執行的主要目的。
好比咱們調用printf函數,咱們一般並不關心它的返回值,而是須要它把特定的字符輸出到屏幕,而標準輸出正是這個函數的反作用。
語句是C的基本執行單元,語句不返回結果,僅執行反作用。語句可分爲簡單語句和複合語句。
在C語言中,「;」 不是分隔符(for語句中的 「;」 除外),而是大部分語句的結尾。
申明不屬於語句,由於申明一般不產生反作用,即便有時候會產生反作用(如初始化),但仍不將其視爲語句,申明也以「;」結尾。
C有5種語句:
✪ 表達式語句
✪ 跳轉語句
✪ 選擇語句
✪ 循環語句
✪ 標籤語句
簡單語句指內部不包含其餘語句的語句。如表達式語句和跳轉語句。最簡單的語句是隻有一個 「;」 的空語句。
複合語句的定義與簡單語句相反,即其內部有其餘語句。
將幾個語句用 {} 括起來就造成了複合語句「塊」,最簡單的複合語句是空塊 {}。
複合語句能夠進行屢次複合,好比塊能夠嵌套,複合語句的子語句能夠是其餘複合語句。
C語言沒有else if關鍵字,這種語法結構只是將上一個if語句的else部分複合了另外一個if語句,將他們寫在一塊兒是爲了使代碼更簡潔。
表達式語句爲一個完整表達式後跟一個分號構成的語句。若表達式爲空,就構成了空語句。
表達式語句是最簡單也是最多見的語句。如下語句都是表達式語句:
;
1 + 2;
var = 5;
printf("hello, world\n");
跳轉語句用於改變代碼的執行順序。跳轉語句包括continue、break、return、goto語句。
選擇語句是複合語句,其做用是根據特定表達式的值對程序執行進行跳轉。如if、if else、switch語句。
循環語句是複合語句,其做用是根據特定表達式的值讓一部分代碼反覆執行屢次,如while、dowhile、for語句。循環語句也能夠經過選擇語句和跳轉語句實現。
在其餘語句前加上標籤便是標籤語句。標籤語句是複合語句,能夠在任何語句(包括標籤語句)前添加標籤。
由於申明不是語句,因此不能在申明前添加標籤。對於下面的代碼,gcc給出以下錯誤提示:
lable:
int var = 0;
error: a label can only be part of a statement and a declaration is not a statement
case 標籤是一種特殊的標籤,其標誌是在標籤前的case關鍵字。case標籤只能在switch語句中使用,case標籤容許且只容許標籤名使用整數,而且把標籤的做用域限定在當前的switch語句中。
標籤是語句的一部分,而不僅是個記號,因此塊末尾不能是標籤。
好比下面語句:
switch (var) {
case 1:
case 2:
case 3:
;
}
最後的分號是不能夠省略的,空語句複合case 3標籤造成標籤語句,而後又複合case 1和case 2,因此這個塊內只有一條完整的複合語句。
C語言代碼文件包括源文件和頭文件,源文件能夠進行編譯和連接,頭文件通常經過預處理指令包含到源文件中使用。
源文件由預處理指令、申明、類型定義、函數定義和註釋組成。
預處理指令和註釋能夠出如今源文件的任何位置而不影響其功能,而申明和類型定義的位置決定了其做用域。
申明有時會伴隨定義,定義必定會包含申明。
函數定義由返回值類型、函數名、參數列表和語句塊組成。語句只能出如今函數定義內部。
C源文件必須有且只能有一個main函數,C89規定,main函數的返回值必須爲int類型,若是程序正常終止,應返回0。
標準的main函數應寫爲 int main(void); 或 int main(int argc, char const *argv[]); 。
C語言經過序列點控制反作用的執行。在該點處以前的求值的全部的反作用已經發生,在它以後的求值的全部反作用仍未開始。
序列點的存在必定程度上保證了程序按照預期執行,但仍存在一些未定義的行爲。
C中的序列點不多,由於C追求效率,更少的序列點能夠給編譯器更多優化的空間。
注意:C中有不少符號同時承擔多種功能,在不一樣語境下扮演不一樣的身份。
C的序列點包括:
&& 與 || 運算符會先對左邊的表達式求值並執行反作用。
對 && 運算符來講,只有當左邊表達式的值爲1時纔對右邊的表達式求值並執行反作用。
這是對程序的一種優化,由於根據「與」邏輯,若是左邊表達式的值爲0,則總表達式的值定爲0,無需對右邊表達式進行計算。根據這一特性,能夠寫出更加符合人類邏輯的代碼。
if (var != 0 && 3 == 100 / var) {}
若是沒有此序列點,則可能會出現0作除數的錯誤。
|| 運算符同理,只有當左邊表達式的值爲0時纔對右邊的表達式求值並執行反作用。
「,」 在C語言中有不少用途,在某些地方它是分隔符,在某些地方它是運算符。好比如下表達式:
var = 1, var = 2
這裏的 「,」 不是分隔符,而是運算符。此逗號運算符的兩邊是兩個賦值表達式,逗號表達式先對左邊的表達式求值並執行反作用,此時var的值被修改成1,以後對右邊的表達式求值並執行反作用,var的值被修改成2,最後,逗號表達式返回右邊表達式的值,即2。
逗號表達式的特性可使兩個表達式像兩個表達式語句那樣執行,適合用在須要用表達式代替語句塊的地方,如 for 語句的括號內。
在 ? 前的表達式求值並執行反作用後,才判斷返回其後哪一個表達式的值。而且,若是肯定返回某個表達式的值,則不會對另外一個表達式求值或執行反作用。
? : 表達式的這個特性使其行爲與if else表現一致。
完整表達式的末尾也是一個序列點,這保證了表達式語句的反作用按照其書寫順序執行。
同時,根據前面對完整表達式的定義,if、while、switch括號中的表達式以及for括號中的每一個份量都是完整表達式,這些表達式的反作用也都會在語句其餘部分開始前執行。
函數調用時參數列表中的逗號不是表達式,而是分隔符。
參數列表的求值順序是未定義的,好比 fn(a++, b--),a++和b--的求值順序是未知的,取決於編譯器。
此處的序列點表現爲在進入函數前,全部表達式的反作用都已經完成;函數返回時,返回值已經拷貝到調用處。
由於初始化是申明的一部分,不屬於語句或表達式,因此不能套用表達式的說法,但其表現是相似的。
以下申明:
int var = 5;
在分號前已經完成反作用,即把var初始化爲5。
初始化列表中的逗號是分隔符而不是運算符。
初始化列表中的表達式按照從左到右的順序求值並執行反作用。
以下代碼:
int var = 0;
int array[] = { var++, var++, var++ };
申明中的逗號是分隔符而不是運算符。
以下代碼:
int a = 0;
int b = a++, c = a++;
b、c分別被初始化爲0、1。
並且,在逗號前的變量已經申明完成,逗號後的則否則。
以下代碼:
int a = 0, b = a; //Correct
int c = d, d = 0; //Error
在申明b前,a已經申明並初始化完成,因此能夠用a初始化b。而在申明c時尚未申明d,因此初始化會報錯。
由於缺乏序列點,C會產生不少未定義的行爲。最典型的例子是:
int var = 0;
var = var++;
根據優先級,表達式var = var++的值是肯定的,然而賦值和自增反作用的執行順序是未定義的,因此var的值是未知的。若是用gcc編譯這段代碼,var的值爲0,比較符合預期;但在VC++中,var的值爲1。
因此咱們應避免在表達式中同時使用某一變量和它的自增表達式。
最後,無論你是轉行也好,初學也罷,進階也可,若是你想學編程~
【值得關注】個人 C/C++編程學習交流俱樂部!【點擊進入】
問題答疑,學習交流,技術探討,還有超多編程資源大全,零基礎的視頻也超棒~