軟件培訓第二講——函數、數組、字符串
文章目錄
01 函數
(1)什麼是函數
首先舉一個餐廳僱傭機器人的例子
餐廳問題: 你開了一家餐廳,僱傭了一個機器人做服務員。機器人比較傻,每次顧客想要點一杯水都需要對他說:先走到廚房,再拿出一個杯子,倒入水,然後拿着杯子給我。久而久之,大家都覺得十分繁瑣。於是你爲機器人增加了設定:每當顧客說“給我一杯水”,機器人就自動的執行上面的幾步。
這種設定,其實就是一種函數的封裝,我們向機器人發出 “給我一杯水” 的指令,就是一次對函數的調用。
函數 就是將一系列具有一定功能和意義的代碼封裝以重用的方式。
(2)爲什麼要用函數
很多小會員可能都會有這樣的一個疑問:爲什麼要用函數?
下面就舉一個小例子告訴大家爲什麼要用函數
首先,我們定義一個計算式
如果我要重複使用這個計算式很多遍,每次都要寫好多,太麻煩了!!
假如我們可以像
這樣的方法去實現計算式的功能,那我們就省去了大量重複的代碼
(3)函數怎麼用
函數既然這麼好,如何才能正確的定義和使用函數呢?
這就要從函數的四要素出發
1.函數的返回類型
2.函數的名稱
3.函數的參數
4.函數的內容
覺得不好記?
沒關係,我們分開來講一講。
1.函數的返回類型
函數的返回類型就是當系統執行過你的程序之後,應該交還給你的東西的屬性
仍然拿前面的機器人的例子打比方:機器人執行一系列操作之後,帶回來一杯水,這一杯水的屬性 “水” 就是機器人的“返回類型”。
C語言的返回類型有int , double , float 等基本類型,還有自定義類型
比如經過各種計算,你的函數最後得到一個int整型值,那麼在定義函數的時候就規定這是一個int型函數
而說到函數的返回類型,又不能不提與它密切關聯的函數返回值。返回值的類型需與返回類型一致。
說明
int myFunc(int a, int b) {
//函數內容
//....
return xxx;
}
上面定義的這個函數中,在myFunc前面的int便是函數的返回類型,它說明了這個函數是一個int型函數,它的返回值也是一個int型的值;在函數的結尾部分,有一個return xxx 這裏的xxx便是函數的返回值,當調用這個函數時,它返回給調用此函數的程序的值便是這裏的xxx
但也有一種情況並不需要return一個結果。有些時候我們調用函數不一定必須得到一個結果,可能函數本身在調用時進行的輸入輸出就已經達到了我們的需求,這時我們就把函數定義爲void型,即
void myFunc(int a, int b){
//函數內容
//...
}
這時,函數不再向調用它的函數返回任何內容。return可以被省略,也可以寫出但是後面不接任何內容。
可以簡單的理解,return用法好比顧客說:機器人,給我拿一杯水!而void用法好比顧客說:機器人,給我催一催菜!
2.函數的名稱
同變量名稱一樣,函數也應該有自己的名稱,這是指定函數的唯一指標。
對於名稱,應當使他能夠簡明扼要的表達函數大概能做的事情。比如函數的目的爲輸出一段星花,那麼我們起名printStar應當是比較合理的一種。
如果命名沒有章法,當我們寫大型程序的時候,調用函數就會變得非常痛苦。
3.函數的參數
有些時候我們需要給函數輸入一些參數才能夠使用。比如我們給機器人設定了找錢函數。當顧客需要找錢時,他首先要聲明顧客有多少錢,然後聲明顧客花了多少錢,最後機器人得出結果。但是我們在設定功能時並沒有辦法預測顧客有多少,花了多少錢。機器人只知道他需要對兩個數字進行減的操作。
像這樣,調用函數時需要傳入的值,就是函數設定時的參數。
規定:
1.不論是否需要參數,在函數的定義是都應該在名字後面加上括號。
2.參數的定義與主函數中變量定義一樣。如果需要一個int型,就定義一個int 變量
3.參數與主函數中傳入的變量並沒有直接的關係
4.函數的內容
函數的內容就是函數的主體,規定了函數要做什麼。就是函數的算法實現
(4)函數應該如何存在於程序中
函數在程序中的定義有一下幾點要求:
*函數的定義不可以在其他函數中進行。
*函數定義位置的選擇有兩個:
1.定義在調用這個函數的函數上面
2.在調用這個函數的下面定義,但要現在上面聲明這個函數
注:若使用第二種,聲明應包括函數的返回值類型,名稱和參數列表
關於這兩點,我們可以舉一個簡單的例子。我們把函數的定義理解爲學習一個知識。在學習(定義)過之後,我們就可以自由的使用(調用)。如果我們把A函數定義在B函數中,那麼我們每一次調用B函數都相當於重新學習了一次A函數,相當於一樣的知識重複的學習了,這肯定是不合理的。因此,函數的定義都應該時獨立的,在其他函數之外的。這“其他函數”,自然包括我們的主函數main。
代碼示例1
#include <stdio.h>
int Six(int a) {
a = a * 100 + a * 10 + a;
return a;
}
int main() {
int num = 0; //養成好習慣,給變量賦初值
scanf("%d", &num);
int result = Six(num);
printf("%d", result);
return 0;
}
代碼示例2
#include <stdio.h>
int Six(int a); //也可寫成 "int Six(int)",或者用其它形參名代替"a",只要保證形參的順序及數量和下面的函數定義一致即可
int main() {
int num = 0; //養成好習慣,給變量賦初值
scanf("%d", &num);
int result = Six(num);
printf("%d", result);
return 0;
}
int Six(int a) {
a = a * 100 + a * 10 + a;
return a;
}
(5)函數應該如何使用
這裏記住以下幾個要點即可
- 函數經過定義就可以通過調用它的名字來使用它
- 如果函數有參數,則使用時應當填滿所有參數
–如果函數不含參數,也不可以丟掉空括號 - 如果函數沒有返回值,則直接作爲一句話輸入在程序中即可
–如果函數有返回值,則應該利用一個變量繼承這個返回值。
(6)令人困擾的問題:形式參數與實際參數
我們先來看一段代碼
#include <stdio.h>
void change(int a, int b);
int main() {
int a = 0, b = 10;
printf("a = %d, b = %d\n", a, b);
change(a, b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
void change(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
試着運行一下上面的代碼。看看它的運行結果與你預期的是否一致。
輸出結果
a = 0, b = 10
a = 0, b = 10
上面的代碼看似完成了交換a, b的值的功能,而程序的運行結果卻是兩次輸出內容保持一致。下面我們就來講一下這個問題。
我們從函數的值傳遞講起。
首先,main函數在調用change函數時,將main函數中a的值,也就是0傳遞給了change中的變量a,又將main函數中b的值,也就是10傳遞給了change中的變量b。這時,main函數與change函數沒有其它任何關聯:main中的a只是它的值與change中的a相等,而change函數中的a發生任何改變都不會影響到main中的a變量。這就好比你在主函數中寫的代碼是
change(0, 10);
主函數只是將其變量的值傳給了change函數,它們只是名字相同,而沒有任何關係。這好比我有一張表格,然後我去打印店複印了一份給你,你即使把那個表格撕掉,都不會影響我的表格。(其中的我就是main函數,你就是change函數)
在以上前提下,change函數中的a變量和b變量再怎麼交換都不會影響到main函數中的變量。
那我們該用什麼方式去解決“使用函數去交換兩個變量的值的問題”呢?
這就是涉及到指針的知識了。等你們對指針有一定的瞭解後,我們會講解這方面的知識點的。
02 數組
什麼是數組
顧名思義,數組就是一堆數放在一起的組合。在C語言中,這“一堆數”被描述爲類型相同的一系列變量。
爲什麼要使用數組
這裏我們想一個實例。比如我們要記錄一個學生的學號,我們可以簡單的通過int StudentNum = 1001來實現。
但如果要記錄100名學生的學號,不僅定義賦值變得十分麻煩,今後對數據進行操作也非常的繁瑣。這時我們一般要使用到數組。
數組的定義
數組的定義爲:
<類型說明符> <數組名>[<常量表達式>]
- 類型說明符,即基本數據類型(int,char等),這規定了數組內容的類型。
- 數組名,名稱的定義應當具有含義
- 常量表達式,指定了數組的長度,這個數字必須是大於零的常量
例如:
float mark[100];
char str[200];
這兩種都是標準的定義方法。
但要注意:不可以利用變量動態的輸入數組長度
數組定義時長度不可以簡單地利用變量定義,也不可以是除了大於零整數的其他內容
int n = 10; int a[n]; //數組的大小不能是變量
int b[10.3]; //數組大小不能是浮點數
int n = 10; int c[n+10]; //數組大小不能是變量表達式
int d[]; //數組大小不明確
數組的賦值
數組的賦值可以通過等號來實現。通過初值表對數組進行賦值的方法,是比較基礎的方法。
要求爲:
初值表需要用 {} 括起來,每個元素用 “,” 分割開。
初值表的內容可以少於定義的長度,但絕對不可以超出該長度。如果少於定義長度,系統會用默認的值填滿數組。
如果在定義數組時沒有指定長度,但是給出了初值表,那麼數組長度就等於初值表內容個數.
舉例
int a[5] = {1, 2, 3, 4, 5}; //可以,標準的賦值
int a[4] = {1, 2, 3, 4, 5}; //不可以,超出範圍
int a[5] = {1, 2, 3}; //可以,系統會用0補足
int a[] = {1, 2, 3}; //可以,系統自動爲數組a規劃長度3
數組的下標
數組的下標是數組的靈魂。在我們定義了數組a之後,直接通過方括號和元素所在的索引值就可以得到該位置的元素。
如
int a[5] = {1, 2, 3, 4 ,5}; //此數組的索引是0, 1, 2, 3, 4
printf(“%d”,a[2]); //輸出3
注意:數組的索引是從0開始的。
有了數組的下標,我們就可以用另一種方式來對數組進行快速的賦值和操作,那就是循環。
基礎變量之所以沒有辦法處理大型數據,除了定義繁瑣以外,還有另外一個問題:那就是沒有辦法快速的調用他們。
而數組之所以可以快速地調用,就是因爲能夠標誌一個變量的指標:變量的名字在數組中的遍歷非常的方便。
a[i]代表的就是數組中的第i+1個元素(索引值+1)。數組的元素的名字也是一個變量,而且這個變量剛好與我們在進行循環遍歷時要用到的循環變量比較相近,因此在數組的輸入輸出等操作中,我們更多的是使用循環。
03 字符串
字符串的另一個名字應該更加讓人感到親切:即字符數組。所謂字符串,就是用字符組成的數組。他既然是一個數組,那麼他的元素排布,初值定義等等就應該與數組完全相同。不過字符串有一點比較特殊:字符串實際佔用的單元數量爲有效字符串長度+1,因爲字符串要求最後一位存放 ‘\0’。在定義字符串時,應當考慮到這一點。
這個’\0’其實是ASCII碼中的NULL,是爲了提示系統 “字符串到這裏已經結束了” 。什麼意思呢?
我們在定義數組時,期待的是能夠快速的處理數組中的每一個元素,重點在於數組的每一項內容。比如我們定義了學號數組,我們期望能對其中的任何一個學號都可以快速地找到,進而進行改變。但是我們定義字符串時,期待的是能夠對字符數組這個整體進行輸入輸出。比如字符串中存放的是 “i love kexie\0” ,我們自然希望對一整句話進行操作,而不是對其中的元素空格,字母e等等進行操作。因此我們需要一個’\0’來提示系統,字符串到此結束了,對字符串整體的讀寫已經可以停止了。
字符串的定義和賦值
字符串的定義和賦初值與數組幾乎一樣。(以下兩種表達式的效果完全一樣)
char str[5] = {'l', 'o', 'v', 'e', '\0'};
char str[] = “love”; // 系統會自動地補上’\0‘
如果一個數組的長度大於字符串常量的長度,剩餘空間全部會被補上 '\0’
但同樣,我們很少利用初值表對字符串進行操作,更多的,我們是通過函數來進行。
字符串的輸入
一般地,我們常用以下兩種方法來讀入字符串
- scanf(“%s”, str); 以空格或者回車作爲結束標誌,就可以完成對字符串的輸入。輸入時應當傳入字符數組首地址,其實就是字符串名。當接收一個字符串時遇到空格,則認爲該字符串已經結束。
gets(str);gets_s(str, maxLengthOfTheString) 以回車作爲結束標誌,可以輸入有空格的字符串(gets函數從C11起已被移除,可使用gets_s代替,其第二個傳入參數爲字符串最大長度,不能超過str字符數組的長度)
字符串的輸出
- printf(“%s”, str);
- puts(str);
兩種輸出方式並沒有太大區別,但要注意,輸出字符串都是從傳入的字符串首地址開始輸出,因此需要弄清楚想要輸出的內容的首地址。