C語言最全入門筆記
c語言入門
C語言一經出現就以其功能豐富、表達能力強、靈活方便、應用面廣等特點迅速在全世界普及和推廣。C語言不但執行效率高而且可移植性好,可以用來開發應用軟體、驅動、作業系統等。C語言也是其它眾多高階語言的鼻祖語言,所以說學習C語言是進入程式設計世界的必修課。
hello,world
1 #include<stdio.h> 2 int main(){ 3 /*在雙引號中間輸入Hello World*/ 4 printf("Hello World"); 5 return 0; 6 }
注:在最新的C標準中,main函式前的型別為int而不是void
c語言的具體結構
簡單來說,一個C程式就是由若干標頭檔案和函式組成。
#include <stdio.h>就是一條預處理命令, 它的作用是通知C語言編譯系統在對C程式進行正式編譯之前需做一些預處理工作。
- 函式就是實現程式碼邏輯的一個小的單元。
必不可少之主函式
一個C程式有且只有一個主函式,即main函式。
C程式就是執行主函式裡的程式碼,也可以說這個主函式就是C語言中的唯一入口。
- 而main前面的int就是主函式的型別.
- printf()是格式輸出函式,這裡就記住它的功能就是在螢幕上輸出指定的資訊
- return是函式的返回值,根據函式型別的不同,返回的值也是不同的。
- \n是轉義字元中的換行符。(注意:C程式一定是從主函式開始執行的)
良好習慣之規範
- 一個說明或一個語句佔一行,例如:包含標頭檔案、一個可執行語句結束都需要換行。
- 函式體內的語句要有明顯縮排,通常以按以下Tab鍵為一個縮排。
- 括號要成對寫,如果需要刪除的話也要成對刪除。
- 當一句可執行語句結束的時候末尾需要有分號。
- 程式碼中所有符號均為英文半形符號。
程式解釋——註釋
註釋是寫給程式設計師看的,不是寫給電腦看的。
C語言註釋方法有兩種:
多行註釋: /* 註釋內容 */
單行註釋: //註釋一行
有名有姓的C(識別符號)
C語言規定,識別符號可以是字母(A~Z,a~z)、數字(0~9)、下劃線_組成的字串,並且第一個字元必須是字母或下劃線。在使用識別符號時還有注意以下幾點:
識別符號的長度最好不要超過8位,因為在某些版本的C中規定識別符號前8位有效,當兩個識別符號前8位相同時,則被認為是同一個識別符號。
- 識別符號是嚴格區分大小寫的。例如Imooc和imooc 是兩個不同的識別符號。
- 識別符號最好選擇有意義的英文單片語成做到"見名知意",不要使用中文。
- 識別符號不能是C語言的關鍵字。想了解更多C語言關鍵字的知識。
變數及賦值
變數就是可以變化的量,而每個變數都會有一個名字(識別符號)。變數佔據記憶體中一定的儲存單元。使用變數之前必須先定義變數,要區分變數名和變數值是兩個不同的概念。
變數定義的一般形式為:資料型別 變數名;
多個型別相同的變數:資料型別 變數名, 變數名, 變數名...;
注意:在定義中不允許連續賦值,如int a=b=c=5;是不合法的。
變數的賦值分為兩種方式:
- 先宣告再賦值
- 宣告的同時賦值
基本資料型別
C語言中,資料型別可分為:
- 基本資料型別
- 構造資料型別
- 指標型別
- 空型別四大類
最常用的整型, 實型與字元型(char,int,float,double):
整型資料是指不帶小數的數字(int,short int,long int, unsigned int, unsigned short int,unsigned long int):
注:
- int short int long int是根據編譯環境的不同,所取範圍不同。
- 而其中short int和long int至少是表中所寫範圍, 但是int在表中是以16位編譯環境寫的取值範圍。
- 另外 c語言int的取值範圍在於他佔用的位元組數 ,不同的編譯器,規定是不一樣。
- ANSI標準定義int是佔2個位元組,TC是按ANSI標準的,它的int是佔2個位元組的。但是在VC裡,一個int是佔4個位元組的。
浮點資料是指帶小數的數字。
生活中有很多資訊適合使用浮點型資料來表示,比如:人的體重(單位:公斤)、商品價格、圓周率等等。
因為精度的不同又分為3種(float,double,long double):
注:C語言中不存在字串變數,字串只能存在字元陣列中,這個後面會講。
格式化輸出語句
格式化輸出語句,也可以說是佔位輸出,是將各種型別的資料按照格式化後的型別及指定的位置從計算機上顯示。
其格式為:printf("輸出格式符",輸出項);
當輸出語句中包含普通字元時,可以採用一下格式:
printf("普通字元輸出格式符", 輸出項);
注意:格式符的個數要與變數、常量或者表示式的個數一一對應
不可改變的常量
在程式執行過程中,值不發生改變的量稱為常量。
mtianyan: C語言的常量可以分為直接常量和符號常量。
- 直接常量也稱為字面量,是可以直接拿來使用,無需說明的量,比如:
- 整型常量:13、0、-13;
- 實型常量:13.33、-24.4;
- 字元常量:‘a’、‘M’
- 字串常量:”I love imooc!”
在C語言中,可以用一個識別符號來表示一個常量,稱之為符號常量。符號常量在使用之前必須先定義,其一般形式為:
#define 識別符號 常量值
1 #include <stdio.h> 2 #define POCKETMONEY 10 //定義常量及常量值 3 int main(){ 4 // POCKETMONEY = 12; //小明私自增加零花錢對嗎? 5 printf("小明今天又得到%d元零花錢\n", POCKETMONEY); 6 return 0; 7 }
符號常量不可以被改變。
自動型別轉換
資料型別存在自動轉換的情況.自動轉換髮生在不同資料型別運算時,在編譯的時候自動完成。
char型別資料轉換為int型別資料遵循ASCII碼中的對應值.
注:
位元組小的可以向位元組大的自動轉換,但位元組大的不能向位元組小的自動轉換
char可以轉換為int,int可以轉換為double,char可以轉換為double。但是不可以反向。
強制型別轉換
強制型別轉換是通過定義型別轉換運算來實現的。其一般形式為:
(資料型別) (表示式)
其作用是把表示式的運算結果強制轉換成型別說明符所表示的型別
在使用強制轉換時應注意以下問題:
- 資料型別和表示式都必須加括號, 如把(int)(x/2+y)寫成(int)x/2+y則成了把x轉換成int型之後再除2再與y相加了。
- 轉換後不會改變原資料的型別及變數值,只在本次運算中臨時性轉換。
- 強制轉換後的運算結果不遵循四捨五入原則。
運算子號
C語言中運算子:
※ 算術運算子
※ 賦值運算子
※ 關係運算符
※ 邏輯運算子
※ 三目運算子
算術運算子
c語言基本運算子:
除法運算中注意:
如果相除的兩個數都是整數的話,則結果也為整數,小數部分省略,如8/3 = 2;
而兩數中有一個為小數,結果則為小數,如:9.0/2 = 4.500000。
mtianyan:取餘運算中注意:
該運算只適合用兩個整數進行取餘運算,如:10%3 = 1;
mtianyan: notes: 而10.0%3則是錯誤的;運算後的符號取決於被模數的符號,如(-10)%3 = -1;而10%(-3) = 1;
mtianyan: %%表示這裡就是一個%符.
注:C語言中沒有乘方這個運算子,也不能用×, ÷等算術符號。
自增與自減運算子
- 自增運算子為++,其功能是使變數的值自增1
- 自減運算子為--,其功能是使變數值自減1。
它們經常使用在迴圈中。自增自減運算子有以下幾種形式:
賦值運算子
C語言中賦值運算子分為簡單賦值運算子和複合賦值運算子
簡單賦值運算子=號了,下面講一下複合賦值運算子:
複合賦值運算子就是在簡單賦值符=之前加上其它運算子構成.
例如+=、-=、*=、/=、%=
分析:定義整型變數a並賦值為3,a += 5;這個算式就等價於a = a+5; 將變數a和5相加之後再賦值給a
注意:複合運算子中運算子和等號之間是不存在空格的。
關係運算符
C語言中的關係運算符:
關係表示式的值是真和假,在C程式用整數1和0表示。
注意:>=, <=, ==, !=這種符號之間不能存在空格。
邏輯運算子
C語言中的邏輯運算子:
邏輯運算的值也是有兩種分別為真和假,C語言中用整型的1和0來表示。其求值規則如下:
- 與運算 &&
參與運算的兩個變數都為真時,結果才為真,否則為假。例如:5>=5 && 7>5 ,運算結果為真;
- 或運算 ||
參與運算的兩個變數只要有一個為真,結果就為真。兩個量都為假時,結果為假。例如:5>=5||5>8,運算結果為真;
- 非運算!
參與運算的變數為真時,結果為假;參與運算量為假時,結果為真。例如:!(5>8),運算結果為真。
三目運算子
C語言中的三目運算子:?:,其格式為:
表示式1 ? 表示式2 : 表示式3;
執行過程是:
先判斷表示式1的值是否為真,如果是真的話執行表示式2;如果是假的話執行表示式3。
1 #include <stdio.h> 2 int main(){ 3 //定義小編兜裡的錢 4 double money =12.0 ; 5 //定義打車回家的費用 6 double cost =11.5 ; 7 printf("小編能不能打車回家呢:"); 8 //輸出y小編就打車回家了,輸出n小編就不能打車回家 9 printf("%c\n",money>=cost?'y':'n' ); 10 return 0; 11 }
運算子大比拼之優先順序比較
各種運算子號的順序:
優先級別為1的優先順序最高,優先級別為10的優先級別最低。
分支結構之簡單if語句
C語言中的分支結構語句中的if條件語句。
簡單if語句的基本結構如下:
if(表示式)
{
執行程式碼塊;
}
其語義是:如果表示式的值為真,則執行其後的語句,否則不執行該語句。
注意:if()後面沒有分號,直接寫{}
分支結構之簡單if-else語句
簡單的if-else語句的基本結構:
語義是: 如果表示式的值為真,則執行程式碼塊1,否則執行程式碼塊2。
注意:
if()後面沒有分號,直接寫{},else後面也沒有分號,直接寫{}
分支結構之多重if-else語句
C語言中多重if-else語句,其結構如下:
語義是:依次判斷表示式的值,當出現某個值為真時,則執行對應程式碼塊,否則執行程式碼塊n。
注意:當某一條件為真的時候,則不會向下執行該分支結構的其他語句。
分支結構之巢狀if-else語句
C語言中巢狀if-else語句。巢狀if-else語句的意思,就是在if-else語句中,再寫if-else語句。其一般形式為:
迴圈結構之while迴圈
反覆不停的執行某個動作就是江湖人稱的迴圈。
C語言中有三種迴圈結構,先看一下C語言while迴圈的結構
其中表達式表示迴圈條件,執行程式碼塊為迴圈體。
while語句的語義是:計算表示式的值,當值為真(非0)時, 執行迴圈體程式碼塊。
- while語句中的表示式一般是關係表達或邏輯表示式,當表示式的值為假時不執行迴圈體,反之則迴圈體一直執行。
- 一定要記著在迴圈體中改變迴圈變數的值,否則會出現死迴圈(無休止的執行)。
- 迴圈體如果包括有一個以上的語句,則必須用{}括起來,組成複合語句。
迴圈結構之do-while迴圈
C語言中的do-while迴圈,一般形式如下:
do-while迴圈語句的語義是:
它先執行迴圈中的執行程式碼塊,然後再判斷while中表達式是否為真,如果為真則繼續迴圈;如果為假,則終止迴圈。因此,do-while迴圈至少要執行一次迴圈語句。
注意:mtianyan: 使用do-while結構語句時,while括號後必須有分號。
迴圈結構之for迴圈(一)
c語言中for迴圈一般形式:
它的執行過程如下:
- 執行表示式1,對迴圈變數做初始化;
- 判斷表示式2,若其值為真(非0),則執行for迴圈體中執行程式碼塊,然後向下執行;若其值為假(0),則結束迴圈;
- 執行表示式3,(i++)等對於迴圈變數進行操作的語句;
- 執行for迴圈中執行程式碼塊後執行第二步;第一步初始化只會執行一次。
- 迴圈結束,程式繼續向下執行。
注意:for迴圈中的兩個分號一定要寫
迴圈結構之for迴圈(二)
在for迴圈中:
- 表示式1是一個或多個賦值語句,它用來控制變數的初始值;
- 表示式2是一個關係表示式,它決定什麼時候退出迴圈;
- 表示式3是迴圈變數的步進值,定義控制迴圈變數每迴圈一次後按什麼方式變化。
- 這三部分之間用分號 ; 分開。
使用for語句應該注意:
- for迴圈中的“表示式1、2、3”均可不寫為空,但兩個分號(;;)不能預設。
- 省略“表示式1(迴圈變數賦初值)”,表示不對迴圈變數賦初始值。
- 省略“表示式2(迴圈條件)”,不做其它處理,迴圈一直執行(死迴圈)。
- 省略“表示式3(迴圈變數增減量)”,不做其他處理,迴圈一直執行(死迴圈)。
- 表示式1可以是設定迴圈變數的初值的賦值表示式,也可以是其他表示式。
- 表示式1和表示式3可以是一個簡單表示式也可以是多個表示式以逗號分割。
- 表示式2一般是關係表示式或邏輯表示式,但也可是數值表示式或字元表示式,只要其值非零,就執行迴圈體。
- 各表示式中的變數一定要在for迴圈之前定義。
怎麼獲得一個數的百位,十位和個位
- 百位數:num/100 可以獲得,因為 int 是整數型,小數部分會省略。比如 765/100 的結果是7
- 十位數:num%100/10 。比如765%100先得到65,65/10得到6
- 個位數:num%10。765%10得到5
迴圈結構之三種迴圈比較
while, do-while和for三種迴圈在具體的使用場合上是有區別的,如下:
在知道迴圈次數的情況下更適合使用for迴圈;
- 在不知道迴圈次數的情況下適合使用while或者do-while迴圈:
- 如果有可能一次都不迴圈應考慮使用while迴圈
- 如果至少迴圈一次應考慮使用do-while迴圈。
但是從本質上講,while,do-while和for迴圈之間是可以相互轉換的。
迴圈結構之多重迴圈
多重迴圈就是在迴圈結構的迴圈體中又出現迴圈結構。
在實際開發中一般最多用到三層重迴圈。
因為迴圈層數越多,執行時間越長,程式越複雜,所以一般用2-3層多重迴圈就可以了。另外不同迴圈之間也是可以巢狀的。
多重迴圈在執行的過程中,外層迴圈為父迴圈,內層迴圈為子迴圈,
父迴圈一次,子迴圈需要全部執行完,直到跳出迴圈。父迴圈再進入下一次,子迴圈繼續執行...
mtianyan: 列印三角形星星堆
1 #include <stdio.h> 2 int main(){ 3 int i, j, k; 4 for(i=1; i<5; i++) 5 { 6 /* 觀察每行的空格數量,補全迴圈條件 */ 7 for(j=i; j<5; j++) 8 { 9 printf(" "); //輸出空格 10 } 11 /* 觀察每行*號的數量,補全迴圈條件 */ 12 for( k=0;k<2*i-1;k++) 13 { 14 printf("*"); //每行輸出的*號 15 } 16 printf("\n"); //每次迴圈換行 17 } 18 return 0; 19 }
使用for迴圈列印9×9乘法表
1 #include <stdio.h> 2 int main() { 3 // 定義相乘數字i,j以及結果result 4 int i, j, result; 5 for(i=9;i>=1;i--) 6 { 7 for(j=1;j<=i;j++) 8 { 9 printf("%d*%d=%d ",i,j,result=i*j); 10 } 11 printf("\n"); 12 } 13 return 0; 14 }
結束語句之break語句
那麼迴圈5次的時候,需要中斷不繼續訓練。在C語言中,可以使用break語句進行該操作.
使用break語句時注意以下幾點:
在沒有迴圈結構的情況下,break不能用在單獨的if-else語句中。
- 在多層迴圈中,一個break語句只跳出當前迴圈。
結束語句之continue語句
那麼迴圈5次的時候,需要中斷後繼續訓練。在C語言中,可以使用continue語句進行該操作
continue語句的作用是結束本次迴圈開始執行下一次迴圈。
break語句與continue語句的區別是:
break是跳出當前整個迴圈,continue是結束本次迴圈開始下一次迴圈。
分支結構之switch語句
switch語句結構如下:
mtianyan: switch語句時還應注意以下幾點:
- 在case後的各常量表達式的值不能相同,否則會出現錯誤。
- 在case子句後如果沒有break;會一直往後執行一直到遇到break;才會跳出switch語句。
- switch後面的表示式語句只能是整型或者字元型別。
- 在case後,允許有多個語句,可以不用{}括起來。
- 各case和default子句的先後順序可以變動,而不會影響程式執行結果。
- default子句可以省略不用。
mtianyan: switch與if語句的應用(計算是該年的第幾天)
1 #include <stdio.h> 2 3 int main() 4 5 { 6 7 /* 定義需要計算的日期 */ 8 9 int date = 0; 10 11 int year = 2008; 12 13 int month = 8; 14 15 int day = 8; 16 17 switch(month) 18 19 { 20 21 case 12:date+=30; 22 23 case 11:date+=31; 24 25 case 10:date+=30; 26 27 case 9:date+=31; 28 29 case 8:date+=31; 30 31 case 7:date+=30; 32 33 case 6:date+=31; 34 35 case 5:date+=30; 36 37 case 4:date+=31; 38 39 case 3: 40 41 if((year%4==0&&year%100!=0)||year%400==0) 42 43 { 44 45 date+=29; 46 47 } 48 49 else 50 51 { 52 53 date+=28; 54 55 } 56 case 2: 57 58 date+=31; 59 60 case 1: 61 62 date+=day; 63 64 printf("%d年%d月%d日是該年的第%d天",year,month,day,date); 65 66 break; 67 68 default: 69 70 printf("error"); 71 72 break; 73 74 } 75 76 return 0; 77 78 }
正確: continue只能用在迴圈體內
臭名遠揚之goto語句
C語言中也有這樣的語句,就是goto語句,goto語句是一種無條件分支語句.
goto 語句的使用格式為:
goto 語句標號;
自創函式
C語言提供了大量的庫函式: 比如stdio.h提供輸出函式
自定義函式的一般形式:
注意:
- [] 包含的內容可以省略,資料型別說明省略,預設是 int 型別函式; 引數省略表示該函式是無參函式,引數不省略表示該函式是有參函式;
- 函式名稱遵循識別符號命名規範;
- mtianyan: 自定義函式儘量放在 main 函式之前,如果要放在main函式後面的話, 需要在main函式之前先宣告自定義函式,宣告格式為:
[資料型別說明] 函式名稱([引數]);
函式呼叫
我們需要用到自定義的函式的時候,就得呼叫它,那麼在呼叫的時候就稱之為函式呼叫。
在C語言中,函式呼叫的一般形式為:
函式名([引數]);
注意:
對無參函式呼叫的時候可以將[]包含的省略。
- []中可以是常數,變數或其它構造型別資料及表示式,多個引數之間用逗號分隔。
有參與無參
在函式中不需要函式引數的稱之為無參函式,在函式中需要函式引數的稱之為有參函式。
有參和無參函式的一般形式如下:
有參函式和無參函式的唯一區別在於:函式 () 中多了一個引數列表。
- 有參函式更為靈活,輸出的內容可以隨著n的改變而隨意變動,只要在main函式中傳遞一個引數就可以了
- 而在無參函式中輸出的相對就比較固定,當需要改動的時候還需要到自定義的方法內改變迴圈變數的值。
mtianyan: 形參與實參
函式的引數分為形參和實參兩種。
- 形參是在定義函式名和函式體的時候使用的引數,目的是用來接收呼叫該函式時傳入的引數。
就類似小明,說了的話而不實際行動;
- 實參是在呼叫時傳遞該函式的引數。
就如小剛能實際行動起來。
函式的形參和實參具有以下特點:
- 形參只有在被呼叫時才分配記憶體單元,在呼叫結束時,即刻釋放所分配的記憶體單元。因此,形參只有在函式內部有效。
函式呼叫結束返回主調函式後則不能再使用該形參變數。
- 實參可以是常量、變數、表示式、函式等。
無論實參是何種型別的量,在進行函式呼叫時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值等辦法使實參獲得確定值。
- 在引數傳遞時,實參和形參在數量上,型別上,順序上應嚴格一致,否則會發生型別不匹配的錯誤。
函式的返回值
函式的返回值是指函式被呼叫之後,執行函式體中的程式段所取得的並返回給主調函式的值。
函式的返回值要注意以下幾點:
- 函式的值只能通過return語句返回主調函式。
return語句的一般形式為:
return 表示式 或者為:return (表示式);
- 函式值的型別和函式定義中函式的型別應保持一致。
notes: 如果兩者不一致,則以函式返回型別為準,自動進行型別轉換。
- 沒有返回值的函式,返回型別為 void。
注意:
void 函式中可以有執行程式碼塊,但是不能有返回值.
mtianyan: void函式中如果有return語句,該語句只能起到結束函式執行的功能。其格式為: return;
遞迴函式(一)
遞迴就是一個函式在它的函式體內呼叫它自身。
執行遞迴函式將反覆呼叫其自身,每呼叫一次就進入新的一層。
注意遞迴函式必須有結束條件
遞迴函式(二)
5的階乘這個例子進行一下剖析,看一看他的運算過程:
程式在計算5的階乘的時候,先執行遞推,當n=1或者n=0的時候返回1,再回推將計算並返回。由此可以看出遞迴函式必須有結束條件。
遞迴函式特點:
- 每一級函式呼叫時都有自己的變數,但是函式程式碼並不會得到複製,如計算5的階乘時每遞推一次變數都不同;
- 每次呼叫都會有一次返回,如計算5的階乘時每遞推一次都返回進行下一次;
- 遞迴函式中,位於遞迴呼叫前的語句和各級被呼叫函式具有相同的執行順序;
- 遞迴函式中,位於遞迴呼叫後的語句的執行順序和各個被呼叫函式的順序相反;
- 遞迴函式中必須有終止語句。
一句話總結遞迴:自我呼叫且有完成狀態
任務
猴子第一天摘下N個桃子,當時就吃了一半,還不過癮,就又多吃了一個。第二天又將剩下的桃子吃掉一半,又多吃了一個。以後每天都吃前一天剩下的一半零一個。到第10天在想吃的時候就剩一個桃子了,問第一天共摘下來多少個桃子?並反向列印每天所剩桃子數。
1 #include <stdio.h> 2 int getPeachNumber(int n) { 3 int num; 4 if(n==10) 5 { 6 return 1; 7 } 8 else 9 { 10 num = (getPeachNumber(n+1)+1)*2; 11 printf("第%d天所剩桃子%d個\n", n, num); 12 } 13 return num; 14 } 15 int main(){ 16 int num = getPeachNumber(1); 17 printf("猴子第一天摘了:%d個桃子。\n", num); 18 return 0; 19 }
遞迴demo。
有5個人坐在一起,問第5個人多少歲?他說比第4個人大2歲。問第4個人歲數,他說比第3個人大2歲。問第3個人,又說比第2人大兩歲。問第2個人,說比第1個人大兩歲。最後 問第1個人,他說是10歲。請問第5個人多大?
程式分析:
利用遞迴的方法,遞迴分為回推和遞推兩個階段。要想知道第5個人歲數,需知道第4人的歲數,依次類推,推到第1人(10歲),再往回推。
1 #include <stdio.h> 2 int dfs(int n) { 3 return n == 1 ? 10 : dfs(n - 1) + 2; 4 } 5 int main() { 6 7 printf("第5個人的年齡是%d歲", dfs(5)); 8 return 0; 9 }
區域性與全域性
C語言中的變數,按作用域範圍可分為兩種,即區域性變數和全域性變數。
- 區域性變數也稱為內部變數。區域性變數是在函式內作定義說明的。其作用域僅限於函式內, 離開該函式後再使用這種變數是非法的。在複合語句中也可定義變數,其作用域只在複合語句範圍內。
- 全域性變數也稱為外部變數,它是在函式外部定義的變數。它不屬於哪一個函式,它屬於一個源程式檔案。其作用域是整個源程式。
變數儲存類別
mtianyan: C語言根據變數的生存週期來劃分,可以分為靜態儲存方式和動態儲存方式。
- 靜態儲存方式:是指在程式執行期間分配固定的儲存空間的方式。靜態儲存區中存放了在整個程式執行過程中都存在的變數,如全域性變數。
- 動態儲存方式:是指在程式執行期間根據需要進行動態的分配儲存空間的方式。動態儲存區中存放的變數是根據程式執行的需要而建立和釋放的,通常包括:函式形式引數;自動變數;函式呼叫時的現場保護和返回地址等。
C語言中儲存類別又分為四類:
- 自動(auto)、
- 靜態(static)、
- 暫存器的(register)
- 外部的(extern)。
1、用關鍵字auto定義的變數為自動變數,auto可以省略,auto不寫則隱含定為“自動儲存類別”,屬於動態儲存方式。如:
2、用static修飾的為靜態變數,如果定義在函式內部的,稱之為靜態區域性變數;如果定義在函式外部,稱之為靜態外部變數。如下為靜態區域性變數:
注意:靜態區域性變數屬於靜態儲存類別,在靜態儲存區內分配儲存單元,在程式整個執行期間都不釋放;靜態區域性變數在編譯時賦初值,即只賦初值一次;如果在定義區域性變數時不賦初值的話,則對靜態區域性變數來說,編譯時自動賦初值0(對數值型變數)或空字元(對字元變數)。
3、為了提高效率,C語言允許將區域性變數得值放在CPU中的暫存器中,這種變數叫“暫存器變數”,用關鍵字register作宣告。例如:
mtianyan: 注意:只有區域性自動變數和形式引數可以作為暫存器變數;一個計算機系統中的暫存器數目有限,不能定義任意多個暫存器變數;區域性靜態變數不能定義為暫存器變數。
4、用extern宣告的的變數是外部變數,外部變數的意義是某函式可以呼叫在該函式之後定義的變數。如:
內部函式與外部函式
- 在C語言中不能被其他原始檔呼叫的函式稱為內部函式 ,內部函式由static關鍵字來定義,因此又被稱謂靜態函式,形式為:
static [資料型別] 函式名([引數]) - 這裡的static是對函式的作用範圍的一個限定,限定該函式只能在其所處的原始檔中使用,因此在不同檔案中出現相同的函式名稱的內部函式是沒有問題的。
- 在C語言中能被其他原始檔呼叫的函式稱為外部函式 ,外部函式由extern關鍵字來定義,形式為:
extern [資料型別] 函式名([引數]) - C語言規定,在沒有指定函式的作用範圍時,系統會預設認為是外部函式,因此當需要定義外部函式時extern也可以省略。
靜態變數只賦值一次
外部函式練習
hello.c
1 #include <stdio.h> 2 #include "test.c" //引用test.c檔案 3 extern void printLine() //這裡定義的方法對嗎?{ 4 printf("**************\n"); 5 } 6 int main(){ 7 say(); 8 return 0; 9 }
test.c
1 #include <stdio.h> 2 void printLine(); 3 static void say(){ 4 printLine(); 5 printf("I love imooc\n"); 6 printf("good good study!\n"); 7 printf("day day up!\n"); 8 printLine(); 9 }
對於hello.c來說,直接引入了test.c檔案。那麼就可以呼叫testc中的static方法say()
而對於test.c並沒有引入,可以通過宣告來呼叫另一個原始檔中暴露出來的方法。
綜合練習
北京市計程車打車計費規則如下:
- 每公里單價計費2.3元
- 起步價13元(包含3公里)
- 晚上23點(含)至次日凌晨5點(不含)打車,每公里單價計費加收20%。
- 每次乘車加收1元錢的燃油附加稅。
小明每天上下班都要打車,公司和家的距離為12公里,上午上班時間為9點,下午下班時間為6點。請編寫一個小程式計算小明每天打車的總費用。
1 #include <stdio.h> 2 3 float taxifee(int clock,int miles){ 4 float money; 5 if(miles<=3) 6 { 7 money=14; 8 printf("費用為14\n"); 9 } 10 else 11 { 12 if(clock>=23 || clock<5) 13 { 14 money=13+1+2.3*(miles-3)*1.2; 15 printf("夜間車費為:%f\n",money); 16 } 17 else 18 { 19 money=13+1+2.3*(miles-3); 20 printf("日間車費為:%f\n",money); 21 } 22 } 23 24 return money; 25 } 26 int main(){ 27 printf("打的總費用:%.1f\n",taxifee(9,12)+taxifee(18,12)); 28 return 0; 29 }
陣列初體驗
程式中也需要容器,只不過該容器有點特殊,它在程式中是一塊連續的,大小固定並且裡面的資料型別一致的記憶體空間,它還有個好聽的名字叫陣列。可以將陣列理解為大小固定,所放物品為同類的一個購物袋,在該購
物袋中的物品是按一定順序放置的。
我們來看一下如何宣告一個數組:
資料型別 陣列名稱[長度];
陣列只宣告也不行啊,看一下陣列是如何初始化的。說到初始化,C語言中的陣列初始化是有三種形式的,分別是:
- 資料型別 陣列名稱[長度n] = {元素1,元素2…元素n};
- 資料型別 陣列名稱[] = {元素1,元素2…元素n};
- 資料型別 陣列名稱[長度n]; 陣列名稱[0] = 元素1; 陣列名稱[1] = 元素2; 陣列名稱[n-1] = 元素n;
我們將資料放到陣列中之後又如何獲取陣列中的元素呢?
獲取陣列元素時:陣列名稱[元素所對應下標];
如:初始化一個數組 int arr[3] = {1,2,3}; 那麼arr[0]就是元素1。
注意:
- 陣列的下標均以0開始;
- 陣列在初始化的時候,陣列內元素的個數不能大於宣告的陣列長度;
- mtianyan: 如果採用第一種初始化方式,元素個數小於陣列的長度時,多餘的陣列元素初始化為0;
- 在宣告陣列後沒有進行初始化的時候,靜態(static)和外部(extern)型別的陣列元素初始化元素為0,自動(auto)型別的陣列的元素初始化值不確定。
陣列的遍歷
陣列就可以採用迴圈的方式將每個元素遍歷出來,而不用人為的每次獲取指定某個位置上的元素,例如我們用for迴圈遍歷一個數組:
注意以下幾點:
- 最好避免出現數組越界訪問,迴圈變數最好不要超出陣列的長度.
- C語言的陣列長度一經宣告,長度就是固定,無法改變,並且C語言並不提供計算陣列長度的方法。
由於C語言是沒有檢查陣列長度改變或者陣列越界的這個機制,可能會在編輯器中編譯並通過,但是結果就不能肯定了,因此還是不要越界或者改變陣列的長度
c語言獲取陣列長度
1 int length = sizeof(arr)/sizeof(arr[0]);
陣列作為函式引數
陣列可以由整個陣列當作函式的引數,也可以由陣列中的某個元素當作函式的引數:
- 整個陣列當作函式引數,即把陣列名稱傳入函式中,例如:
- 陣列中的元素當作函式引數,即把陣列中的引數傳入函式中,例如:
陣列作為函式引數時注意以下事項:
- 陣列名作為函式實參傳遞時,函式定義處作為接收引數的陣列型別形參既可以指定長度也可以不指定長度。
- 陣列元素作為函式實參傳遞時,陣列元素型別必須與形引數據型別一致。
mtianyan: 陣列的應用(一)[氣泡排序]
以升序排序為例氣泡排序的思想:相鄰元素兩兩比較,將較大的數字放在後面,直到將所有數字全部排序。就像小學排隊時按大小個排一樣,將一個同學拉出來和後面的比比,如果高就放後面,一直把隊伍排好。
1 #include <stdio.h> 2 int main(){ 3 double arr[]={1.78, 1.77, 1.82, 1.79, 1.85, 1.75, 1.86, 1.77, 1.81, 1.80}; 4 int i,j; 5 printf("\n************排隊前*************\n"); 6 for(i=0;i<10;i++) 7 { 8 if(i != 9) 9 printf("%1.2f, ", arr[i]); //%1.2f表示小數點前一位,小數點後精確到兩位 10 else 11 printf("%1.2f", arr[i]); //%1.2f表示小數點前一位,小數點後精確到兩位 12 } 13 for(i=8; i>=0; i--) 14 { 15 for(j=0;j<=i;j++) 16 { 17 if( arr[j]>arr[j+1]) //當前面的數比後面的數大時 18 { 19 double temp; //定義臨時變數temp 20 temp=arr[j];//將前面的數賦值給temp 21 arr[j]=arr[j+1]; //前後之數顛倒位置 22 arr[j+1]=temp;//將較大的數放在後面 23 } 24 } 25 } 26 printf("\n************排隊後*************\n"); 27 for(i=0;i<10;i++) 28 { 29 if(i != 9) 30 printf("%1.2f, ", arr[i]); //%1.2f表示小數點前一位,小數點後精確到兩位 31 else 32 printf("%1.2f", arr[i]); //%1.2f表示小數點前一位,小數點後精確到兩位 33 } 34 return 0; 35 }
陣列的應用(二)[陣列查詢功能]
當我們購物之後,拎著購物袋回到家,會一一檢查購物袋中的物品看是否缺少或者都是想購之物。
那麼應用到程式中,可以使用陣列查詢功能,看看是否存在該資料,如果存在並返回該元素的下標。
1 #include <stdio.h> 2 int getIndex(int arr[5],int value) 3 { 4 int i; 5 int index; 6 for(i=0;i<5;i++) 7 { 8 /* 請完善陣列查詢功能 */ 9 if(arr[i]==value) 10 { 11 index=i; 12 break; 13 } 14 index=-1; 15 } 16 return index; 17 } 18 19 int main() 20 { 21 int arr[5]={3,12,9,8,6}; 22 int value = 8; 23 int index = getIndex(arr,value); //這裡應該傳什麼引數呢? 24 if(index!=-1) 25 { 26 printf("%d在陣列中存在,下標為:%d\n",value,index); 27 } 28 else 29 { 30 printf("%d在陣列中不存在。\n",value); 31 } 32 return 0; 33 }
字串與陣列
C語言中,是沒有辦法直接定義字串資料型別的,但是我們可以使用陣列來定義我們所要的字串。一般有以下兩種格式:
- char 字串名稱[長度] = "字串值";
- char 字串名稱[長度] = {'字元1','字元2',...,'字元n','\0'};
注意:
- []中的長度是可以省略不寫的;
- 採用第2種方式的時候最後一個元素必須是'\0','\0'表示字串的結束標誌;
- 採用第2種方式的時候在陣列中不能寫中文。
在輸出字串的時候要使用:printf(“%s”,字元陣列名字);或者puts(字元陣列名字);。
mtianyan:字串函式
常用的字串函式如下(strlen,strcmp,strcpy,strcat,atoi):
使用字串函式注意以下事項:
- strlen()獲取字串的長度,在字串長度中是不包括‘\0’而且漢字和字母的長度是不一樣的。比如:
- strcmp()在比較的時候會把字串先轉換成ASCII碼再進行比較,返回的結果為0表示s1和s2的ASCII碼相等,返回結果為1表示s1比s2的ASCII碼大,返回結果為-1表示s1比s2的ASCII碼小,例如:
- strcpy()拷貝之後會覆蓋原來字串且不能對字串常量進行拷貝,比如:
- strcat在使用時s1與s2指的記憶體空間不能重疊,且s1要有足夠的空間來容納要複製的字串,如:
多維陣列
多維陣列的定義格式是:
資料型別 陣列名稱[常量表達式1][常量表達式2]...[常量表達式n];
定義了一個名稱為num,資料型別為int的二維陣列。其中第一個[3]表示第一維下標的長度,就像購物時分類存放的購物;第二個[3]表示第二維下標的長度,就像每個購物袋中的元素。
多維陣列的初始化與一維陣列的初始化類似也是分兩種:
- 資料型別 陣列名稱[常量表達式1][常量表達式2]...[常量表達式n] = {{值1,..,值n},{值1,..,值n},...,{值1,..,值n}};
- 資料型別 陣列名稱[常量表達式1][常量表達式2]...[常量表達式n]; 陣列名稱[下標1][下標2]...[下標n] = 值;
多維陣列初始化要注意以下事項:
- 採用第一種始化時陣列宣告必須指定列的維數。mtianyan: 因為系統會根據陣列中元素的總個數來分配空間,當知道元素總個數以及列的維數後,會直接計算出行的維數;
- 採用第二種初始化時陣列宣告必須同時指定行和列的維數。
二維陣列定義的時候,可以不指定行的數量,但是必須指定列的數量
二維陣列定義的時候,可以不指定行的數量,但是必須指定列的數量。
多維陣列的遍歷
多維陣列也是存在遍歷的,和一維陣列遍歷一樣,也是需要用到迴圈。不一樣的就是多維陣列需要採用巢狀迴圈。
注意:多維陣列的每一維下標均不能越界。
綜合練習:
1 #include <stdio.h> 2 #define N 10 3 //列印分數 4 void printScore(int score[]){ 5 int i; 6 printf("\n"); 7 for(i=0;i<N;i++) 8 { 9 printf("%d ",score[i]); 10 } 11 printf("\n"); 12 } 13 //計算考試總分 14 int getTotalScore(int score[]){ 15 int sum = 0; 16 int i; 17 for(i=0;i<N;i++) 18 { 19 sum+=score[i]; 20 } 21 return sum; 22 } 23 //計算平均分 24 int getAvgScore(int score[]){ 25 return getTotalScore(score)/N; 26 } 27 //計算最高分 28 int getMax(int score[]){ 29 int max = -1; 30 int i; 31 for(i=0;i<N;i++) 32 { 33 if(score[i]>max) 34 { 35 max = score[i]; 36 } 37 } 38 return max; 39 } 40 //計算最低分 41 int getMin(int score[]){ 42 int min =100; 43 int i; 44 for(i=0;i<N;i++) 45 { 46 if(score[i]< min) 47 { 48 min = score[i]; 49 } 50 } 51 return min; 52 } 53 //分數降序排序 54 void sort(int score[]){ 55 int i,j; 56 for(i=N-2;i>=0;i--) 57 { 58 for(j=0;j<=i;j++) 59 { 60 if(score[j]<score[j+1]) 61 { 62 int temp; 63 temp = score[j]; 64 score[j] = score[j+1]; 65 score[j+1]=temp; 66 } 67 } 68 } 69 printScore(score); 70 } 71 72 int main(){ 73 int score[N]={67,98,75,63,82,79,81,91,66,84}; 74 int sum,avg,max,min; 75 sum = getTotalScore(score); 76 avg = getAvgScore(score); 77 max = getMax(score); 78 min = getMin(score); 79 printf("總分是:%d\n",sum); 80 printf("平均分是:%d\n",avg); 81 printf("最高分是:%d\n",max); 82 printf("最低分是:%d\n",min); 83 printf("----------成績排名---------\n"); 84 sort(score); 85 return 0; 86 }
轉自:https://www.toutiao.com/i6830707631779217933/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1×tamp=1605171071&app=news_article&utm_source=weixin&utm_medium=toutiao_android&use_new_style=1&req_id=20201112165111010026077074280373D6&group_id=6830707631779217933