C語(yǔ)言函數(shù)超詳細(xì)講解上篇
前言
本文主要學(xué)習(xí)函數(shù)的相關(guān)內(nèi)容。
1、函數(shù)是什么?
維基百科中對(duì)函數(shù)的定義:子程序
- 在計(jì)算機(jī)科學(xué)中,子程序(英語(yǔ):Subroutine, procedure, function, routine, method,subprogram, callable unit),是一個(gè)大型程序中的某部分代碼, 由一個(gè)或多個(gè)語(yǔ)句塊組成。它負(fù)責(zé)完成某項(xiàng)特定任務(wù),而且相較于其他代 碼,具備相對(duì)的獨(dú)立性。
- 一般會(huì)有輸入?yún)?shù)并有返回值,提供對(duì)過(guò)程的封裝和細(xì)節(jié)的隱藏。這些代碼通常被集成為軟件庫(kù)。
2、C語(yǔ)言中函數(shù)的分類(lèi)
- 庫(kù)函數(shù)
- 自定義函數(shù)
2.1 庫(kù)函數(shù)
- 在學(xué)習(xí)C語(yǔ)言編程時(shí),總是在一個(gè)代碼編寫(xiě)完成之后,想把這個(gè)結(jié)果打印到屏幕上看看。這時(shí)使用一個(gè)功能:將信息按照一定的格式打印到屏幕上(printf)。
- 在編程的過(guò)程中我們會(huì)頻繁的做一些字符串的拷貝工作(strcpy)
- 在編程是我們也計(jì)算,總是會(huì)計(jì)算n的k次方這樣的運(yùn)算(pow)
上面的函數(shù)不用自己編寫(xiě),直接可以調(diào)用。為了支持可移植性和提高程序的效率,所以C語(yǔ)言的基礎(chǔ)庫(kù)中提供了一系列類(lèi)似的庫(kù)函數(shù),方便程序員進(jìn)行軟件開(kāi)發(fā)。
C語(yǔ)言常用的庫(kù)函數(shù)都有:
- IO函數(shù)
- 字符串操作函數(shù)
- 字符操作函數(shù)
- 內(nèi)存操作函數(shù)
- 時(shí)間/日期函數(shù)
- 數(shù)學(xué)函數(shù)
- 其他庫(kù)函數(shù)
使用庫(kù)函數(shù),必須包含 #include 對(duì)應(yīng)的頭文件。
2.1.1 如何學(xué)會(huì)使用庫(kù)函數(shù)
推薦查詢(xún)工具官網(wǎng):
MSDN(Microsoft Developer Network)
http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
2.1.2 自定義函數(shù)
自定義函數(shù)和庫(kù)函數(shù)一樣,有函數(shù)名,返回值類(lèi)型和函數(shù)參數(shù)。這些都是我們自己來(lái)設(shè)計(jì),函數(shù)的組成:
ret_type fun_name(para1, * ) { statement;//語(yǔ)句項(xiàng) } //ret_type 返回類(lèi)型 //fun_name 函數(shù)名 //para1 函數(shù)參數(shù)
//舉例:寫(xiě)一個(gè)函數(shù)可以找出兩個(gè)整數(shù)中的最大值 //get_max函數(shù)的設(shè)計(jì) int get_max(int x, int y) { return (x>y)?(x):(y); } int main() { int num1 = 10; int num2 = 20; int max = get_max(num1, num2); printf("max = %d\n", max); return 0; }
3、函數(shù)的參數(shù)
3.1 實(shí)際參數(shù)(實(shí)參)
- 真實(shí)傳給函數(shù)的參數(shù),叫實(shí)參
- 實(shí)參可以是:常量、變量、表達(dá)式、函數(shù)等
- 無(wú)論實(shí)參是何種類(lèi)型的量,在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須有確定的值,以便把這些值傳送給形參
3.2 形式參數(shù)(形參)
- 形式參數(shù)是指函數(shù)名后括號(hào)中的變量,因?yàn)樾问絽?shù)只有在函數(shù)被調(diào)用的過(guò)程中才實(shí)例化(分配內(nèi)存單元),所以叫形式參數(shù)
- 形式參數(shù)當(dāng)函數(shù)調(diào)用完成之后就自動(dòng)銷(xiāo)毀了。因此形式參數(shù)只在函數(shù)中有效
//舉例2:寫(xiě)一個(gè)函數(shù)可以交換兩個(gè)整形變量的內(nèi)容 //實(shí)現(xiàn)成函數(shù),但是不能完成任務(wù) int exchange1(int x, int y) {//當(dāng)實(shí)參傳給形參時(shí)候,形參是實(shí)參的一份臨時(shí)拷貝, //對(duì)形參的修改不會(huì)影響實(shí)參 int temp = x; x = y; y = temp; } //正確的版本 int exchange2(int* pa, int* pb)//定義指針,接收地址 { int temp = *pa; *pa = *pb; *pb = temp; } int main() { int a = 3; int b = 5; exchange1(a, b);//傳參是值 printf("exchange1::a = %d b = %d\n", a, b);//交換前 exchange2(&a, &b);//傳參是地址 printf("exchange2::a = %d b = %d\n", a, b);//交換后的 //傳入地址,自定義的形參和實(shí)參聯(lián)系更加緊密,能改變地址存儲(chǔ)的數(shù)值 //此時(shí),形參的地址與實(shí)參的地址是一樣的 return 0; }
上面代碼中, exchange1和 exchange2函數(shù)中的參數(shù) x,y,pa,pb 都是形式參數(shù)。在main函數(shù)中傳給 exchange1的 a ,b 和傳給 exchange2函數(shù)的 &a ,&b是實(shí)際參數(shù)。
運(yùn)行結(jié)果如下所示,exchange1并沒(méi)有起到預(yù)想的交換數(shù)值的作用,exchange2可以。
通過(guò)監(jiān)視變量發(fā)現(xiàn):
名稱(chēng) | 值 | 意義 |
---|---|---|
a | 5 | 數(shù)值為5 |
&a | 0x113f8e4 | 數(shù)值a的地址 |
x | 5 | 形參x數(shù)值為5 |
&x | 0x113f800 | 形參x的地址 |
pa | 0x113f8e4 | 形參pa數(shù)值為a的地址 |
- 變量a在內(nèi)存中開(kāi)辟了空間,地址是0x113f8e4
- 函數(shù)exchange1將實(shí)參a傳遞給形參x,x的數(shù)值也是5,但此時(shí)形參x另外重新再內(nèi)存中開(kāi)辟了空間,地址 0x113f800,形參和實(shí)參的地址是不一樣的
- 函數(shù)exchange2將實(shí)參a傳遞給形參pa,pa的值是變量a的地址0x113f8e4,這個(gè)地址里面存放變量a的數(shù)值5。
這里可以看到 exchange1函數(shù)在調(diào)用的時(shí)候, x , y 擁有自己的空間,同時(shí)擁有了和實(shí)參一模一樣的內(nèi)容。所以我們可以簡(jiǎn)單的認(rèn)為:
形參實(shí)例化之后其實(shí)相當(dāng)于實(shí)參的一份臨時(shí)拷貝。形參x、y的值發(fā)生交換,但是不影響實(shí)參a、b的值。一般性的只是使用數(shù)值大小,利用形參傳值就可以了,傳值表明,形參和實(shí)參的關(guān)系膚淺,僅限于表面數(shù)值的拷貝。
如果需要對(duì)主函數(shù)的實(shí)參值進(jìn)行操作,比如交換,此時(shí)形參需傳地址,功能更為強(qiáng)大。傳地址表明,形參和實(shí)參的關(guān)系更深一步,直接可以通過(guò)地址修改實(shí)參的數(shù)值。
4、函數(shù)的調(diào)用
4.1 傳值調(diào)用
函數(shù)的形參和實(shí)參分別占有不同內(nèi)存塊,對(duì)形參的修改不會(huì)影響實(shí)參。
4.2 傳址調(diào)用
- 傳址調(diào)用是把函數(shù)外部創(chuàng)建變量的內(nèi)存地址傳遞給函數(shù)參數(shù)的一種調(diào)用函數(shù)的方式
- 這種傳參方式可以讓函數(shù)和函數(shù)外邊的變量建立起真正的聯(lián)系,也就是函數(shù)內(nèi)部可以直接操作函數(shù)外部的變量
4.3 練習(xí)
4.3.1 判斷一個(gè)數(shù)是不是素?cái)?shù)
寫(xiě)一個(gè)函數(shù)可以判斷一個(gè)數(shù)是不是素?cái)?shù)
//返回1 表示是素?cái)?shù) //返回-1 表示不是素?cái)?shù) //寫(xiě)法1 2-n-1 試除法 int is_sushu(int a) {//素?cái)?shù)就是除1和自身外,不能被其他數(shù)整除 for (int i = 2; i < a-1; i++) {//用2-n-1的數(shù)一一試除 if (a%i==0) {//若有能除的,直接返回-1,跳出后面的循環(huán)了 return -1;//表明不是素?cái)?shù), } }//break退出循環(huán)會(huì)調(diào)到這里 //return返回時(shí)直接退出這個(gè)函數(shù)了,返回到主函數(shù)了,級(jí)別更高 return 1; } int main() { int a = 0; scanf("%d", &a); int num = is_sushu(a); if (num==1) { printf("%d: 是素?cái)?shù)", a); } else { printf("%d:不是素?cái)?shù)", a); } return 0; } //寫(xiě)法2 2-sqrt(n) 試除法,速度更快 #include<math.h> int is_sushu(int num) { for (int i = 2; i <=sqrt(num); i++) { if (num%i == 0) { return -1; } } return 1; } int main() { int num = 0; for (int num = 100; num < 201; num++) { if ((is_sushu(num)) == 1) { printf("%d ", num); } } return 0; }
4.3.2 判斷一年是不是閏年
寫(xiě)一個(gè)函數(shù)判斷一年是不是閏年
//閏年時(shí)是4的倍數(shù)且不是100的倍數(shù),或者是400的倍數(shù) int is_run_nian(int y) { if (((num%4 == 0)&& (num%100 != 0))|| (num % 400 == 0)) { return 1; } //return ((num%4 == 0)&& (num%100 != 0))|| (num % 400 == 0);//簡(jiǎn)寫(xiě) } int main() { int num = 0; for (int num = 1000; num <= 2000; num++) { if ((is_run_nian(num)) == 1) { printf("%d ", num); } } return 0; }
4.3.3 二分查找
寫(xiě)一個(gè)函數(shù),實(shí)現(xiàn)一個(gè)整形有序數(shù)組的二分查找
//找到了就返回下標(biāo) //找不到返回-1 int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 0; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); int left = 0;//左下標(biāo) int right = sz - 1;//右下標(biāo) scanf("%d", &k); while (left<=right) {//每次left right mid都要更新 int mid = left + (right - left) / 2; if (arr[mid] > k) {//尋找的數(shù)值在左半邊,所以左不動(dòng),右邊動(dòng) right = mid - 1; } else if (arr[mid] < k) {//尋找的數(shù)值在右半邊,所以左邊動(dòng),右不動(dòng) left = mid + 1; } else { printf("找到了:%d ", mid); break; } } if (left>right)//循環(huán)結(jié)束了 { printf("找不到"); } return 0; }
可以將二分法進(jìn)行改進(jìn),寫(xiě)成獨(dú)立的函數(shù),讓函數(shù)功能單一化:
int is_erfen(int arr[],int k, int sz) {//數(shù)組傳遞參數(shù)進(jìn)來(lái)就是數(shù)組首元素的地址,并不是整個(gè)數(shù)組 //地址的大小是4或8個(gè)字節(jié),前面有講到過(guò)的 //x86平臺(tái)中,地址占4個(gè)字節(jié),sizeof(arr)是4, 而不是40了 //int sz = sizeof(arr) / sizeof(arr[0]);//sz要在主函數(shù)計(jì)算, int left = 0; int right = sz - 1; while (left <= right) {//每次left right mid都要更新 int mid = left + (right - left) / 2; if (arr[mid] > k) { right = mid - 1; } else if (arr[mid] < k) { left = mid + 1; } else { return mid; } } return -1; } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int k = 0; int i = 0; scanf("%d", &k); //主函數(shù)中 按數(shù)組名找到整個(gè)數(shù)組,求出占用字節(jié)是40 int sz = sizeof(arr) / sizeof(arr[0]); int num = is_erfen(arr, k, sz); if (num ==-1) { printf("找不到"); } else { printf("找到了:%d ", num); } return 0; }
在上面的函數(shù)中需要注意,數(shù)組的大小必須在主函數(shù)中計(jì)算,下面代碼可說(shuō)明在主函數(shù)中和其他函數(shù)中求數(shù)組長(zhǎng)度的區(qū)別:
int erfen(int arr[]) {//數(shù)組傳遞進(jìn)來(lái),只能是首地址,這個(gè)arr數(shù)組里只有{1},長(zhǎng)度為4 int sz1 = sizeof(arr) / sizeof(arr[0]);,//4/4 = 1求出只有1個(gè)元素 printf("erfen求數(shù)組大小,sz1:%d\n ", sz1); } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //數(shù)組長(zhǎng)度為40 int sz = sizeof(arr) / sizeof(arr[0]);//40/4 = 10 求出數(shù)組10個(gè)元素 printf("main中求數(shù)組大小,sz:%d\n ", sz); erfen(arr); return 0; }
按F10進(jìn)入調(diào)試界面,按F11,可觀察到:
- 主函數(shù)中sizeof(arr)長(zhǎng)度是40,數(shù)組中包含10個(gè)元素。
- 而在函數(shù)erfen中,sizeof(arr)長(zhǎng)度是4,數(shù)組中包含1個(gè)元素,就是首元素{ 1 }。
由此,可知道,參數(shù)里傳遞數(shù)組時(shí),實(shí)際傳遞的數(shù)組就是數(shù)組的地址,也是數(shù)組首元素的地址。數(shù)組名傳遞進(jìn)來(lái),只能是首地址,這個(gè)arr數(shù)組里只有首元素{1},長(zhǎng)度為4。整個(gè)數(shù)組是傳遞不了的。因此,必須在主函數(shù)里求取數(shù)組的長(zhǎng)度。
因?yàn)閭鬟f的參數(shù)是地址,所以erfen中也可以定義指針來(lái)接受數(shù)組:
//int erfen(int arr[])//接受數(shù)組,只能接受一個(gè)首元素 int erfen(int* arr) {//數(shù)組傳遞進(jìn)來(lái),只能是首地址,這個(gè)arr數(shù)組里只有{1},長(zhǎng)度為4 int sz1 = sizeof(arr) / sizeof(arr[0]);,//4/4 = 1求出只有1個(gè)元素 printf("erfen求數(shù)組大小,sz1:%d\n ", sz1); } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //數(shù)組長(zhǎng)度為40 int sz = sizeof(arr) / sizeof(arr[0]);//40/4 = 10 求出數(shù)組10個(gè)元素 printf("main中求數(shù)組大小,sz:%d\n ", sz); erfen(arr); return 0; }
運(yùn)行結(jié)果一樣
4.3.4 數(shù)值自增增加1
寫(xiě)一個(gè)函數(shù)用一次這個(gè)函數(shù),就會(huì)將 num 的值增加1
int add(int* p) { (*p)++;//參數(shù)傳地址,可以操作實(shí)參的值 } int main() { int a = 10; add(&a);//傳地址 printf("%d\n", a); add(&a); printf("%d\n", a); add(&a); printf("%d\n", a); add(&a); printf("%d\n", a); return 0; }
5、函數(shù)的嵌套調(diào)用和鏈?zhǔn)皆L(fǎng)問(wèn)
函數(shù)和函數(shù)之間可以根據(jù)實(shí)際的需求進(jìn)行組合的,也就是互相調(diào)用的。
5.1 嵌套調(diào)用
//套娃 void newline() { printf("hehe\n"); } void threeline() { int i = 0; for (int i = 0; i < 3; i++) { newline(); } } int main() { threeline(); newline; return 0; }
5.2 鏈?zhǔn)皆L(fǎng)問(wèn)
把一個(gè)函數(shù)的返回值作為另外一個(gè)函數(shù)的參數(shù)。
int main() { char arr[20] = "hello"; int ret = strlen(strcat(arr, "bit")); printf("%d\n", ret); return 0; } int main() { printf("%d", printf("%d", printf("%d", 43)));//輸出4321, //前面是打印43 2是返回兩個(gè)字符(因?yàn)? 、3是兩個(gè)字符) //1是返回1個(gè)字符(2是1個(gè)字符) return 0; }
輸出結(jié)果見(jiàn)下圖:
總結(jié)
本文主要介紹了函數(shù)相關(guān)的知識(shí),下一篇接著介紹函數(shù)的內(nèi)容。
到此這篇關(guān)于C語(yǔ)言函數(shù)超詳細(xì)講解上篇的文章就介紹到這了,更多相關(guān)C語(yǔ)言 函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言控制臺(tái)實(shí)現(xiàn)字符飛機(jī)大戰(zhàn)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言控制臺(tái)實(shí)現(xiàn)字符飛機(jī)大戰(zhàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12C語(yǔ)言數(shù)組和指針,內(nèi)存之間的關(guān)系
這篇文章主要介紹了C語(yǔ)言數(shù)組和指針,內(nèi)存之間的關(guān)系,首先論證一維數(shù)組和一級(jí)指針之前的關(guān)系,我們常常使用一級(jí)指針指針的方式訪(fǎng)問(wèn)一維數(shù)組,只有對(duì)內(nèi)存的理解到位才能理解它們直接的關(guān)系。需要的小伙伴可以參考一下2022-02-02C語(yǔ)言位段(位域)機(jī)制結(jié)構(gòu)體的特殊實(shí)現(xiàn)及解析
這篇文章主要為大家介紹了C語(yǔ)言位段位域機(jī)制結(jié)構(gòu)體的特殊實(shí)現(xiàn)講解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-02-02關(guān)于C語(yǔ)言多線(xiàn)程pthread庫(kù)的相關(guān)函數(shù)說(shuō)明
下面小編就為大家?guī)?lái)一篇關(guān)于C語(yǔ)言多線(xiàn)程pthread庫(kù)的相關(guān)函數(shù)說(shuō)明。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05VC++?2019?"const?char*"類(lèi)型的實(shí)參與"LPCTSTR"
這篇文章主要給大家介紹了關(guān)于VC++?2019?"const?char*"類(lèi)型的實(shí)參與"LPCTSTR"類(lèi)型的形參不兼容的解決方法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-03-03C++詳細(xì)實(shí)現(xiàn)完整圖書(shū)管理功能
隨著網(wǎng)絡(luò)技術(shù)的高速發(fā)展,計(jì)算機(jī)應(yīng)用的普及,利用計(jì)算機(jī)對(duì)圖書(shū)館的日常工作進(jìn)行管理勢(shì)在必行,本篇文章涵蓋一個(gè)圖書(shū)管理系統(tǒng)的全部實(shí)現(xiàn)代碼,大家可以查缺補(bǔ)漏,提升水平2022-05-05C/C++?函數(shù)的存儲(chǔ)位置和占用空間詳解
Lambda函數(shù)的代碼部分在代碼段中,被捕獲的變量存儲(chǔ)在Lambda函數(shù)對(duì)象的內(nèi)部,這些變量的存儲(chǔ)位置取決于Lambda函數(shù)對(duì)象的存儲(chǔ)位置,這篇文章主要介紹了C/C++函數(shù)的存儲(chǔ)位置和占用空間,需要的朋友可以參考下2023-06-06