一、一個簡單的函數
函數在先前已經用到過,這裡就一個程式對於函數進行說明:
#include<stdio.h>
int a=0; //全域變數
int main()
{
void function1(); //函數1
int fuction2(int temp); //函數2
int b=0; //變數
printf("%d\n",fuction2(5));
return 0;
}
void function1()
{
int c=0; //局部變數
}
int fuction2(int temp)
{
temp+=2;
return temp;
}
1、 首先我們看到了注釋中的全域變數這個詞,在程式中有整個程式都有效變數,我們可以認定為全域變數;而在函數中定義的變數我們認為只有在這個函數中才會有效,我們叫做是局部變數,而在迴圈體或者更小的結構中定義的變數,那麼出了這個結構,這個變數將不再有效。
2、 然後我們看整個程式的構架,函數在主函數中被聲明,那麼在主函數中就可以被調用,或者說這個函數的定義在主函數的前面,那麼無需聲明,主函數在執行的時候也可以被調用,但是在主函數中聲明是一種比較好的方法,這就好像是標頭檔的聲明一樣,只是兩者聲明的地方不同,其實都是相同的,想要在什麼有效生命週期內使用,就在恰當的位置聲明。
3、 然後就是函數的格式:函數類型 函數名(參數類型 參數1,參數類型 參數2…)
函數類型是指這個函數在執行完畢的時候需要返回給調用者的數值,如果是void則不用返回,假如是int,則在調用函數內的return後面必須是int的資料類型。至於參數,在函數調用的時候程式放進去的參數我們叫做實參,而在聲明或者定義函數的地方的參數我們又叫做形參。
二、函數參數問題 2.1 值傳遞和地址傳遞
程式中資料的儲存可以這麼理解,每個數值存放在對應的地址中,那麼在函數的參數傳遞中,加入傳入的參數是一個數值,那麼在函數內部對數值進行任何修改都不會影響到在調用這個函數的地方的實參的數值。而地址傳遞則相當於把這個數值的地址當作參數傳入,那麼在函數內部對存放在這個地址的數值的修改將會有效,在學習C++的過程中我們還可以知道在用引用這個功能的時候也可以實現影響原先數值的作用。這裡我們就知道要讓函數內部影響外部的某個值,方式有:
1、 地址傳遞;
2、 引用;
3、 不用參數的方式,用全域變數的方式;
4、 用到了容器;
2.2 函數的嵌套調用和遞迴調用
函數的嵌套調用和迴圈中的迴圈可以等同考慮,比較簡單,可以直觀地代入瞭解。而遞迴調用則不一定,遞迴就是一個棧,先進後出的原理,是動態規劃的靈魂,這裡用遞迴實現一個字串正向輸入,逆向輸出的功能,代碼如下:
#include<stdio.h>
int main()
{
void recursion(); //函式宣告
recursion(); //函數調用
return 0;
}
void recursion()
{
char c;
scanf("%c",&c); //輸入字元
if(c!=10) //一直輸入當輸入的是斷行符號為止
{
recursion();
}
printf("%c",c); //輸出
}
大概的思路就是一個字元一個字元輸入,當輸入停止(這裡判斷為斷行符號)時候,就開始輸出當前字元,然後結束當前函數,跳回到上一個函數,輸出上一個函數的輸入的字元,一直到程式運行結束,就實現了輸入的字串逆向輸出的功能。
三、變數的範圍和儲存方式
範圍就是局部變數和全域變數的使用中涉及到的問題,在先前的說明中提到過,這裡講講儲存方式。
3.1 動態儲存和靜態儲存
動態儲存是指在程式執行過程中,使用到變數時才分配儲存單元,使用完畢立刻釋放。例如函數的形參就是屬於這種方式。
靜態儲存是指在變數定義時就分配儲存單元並一直保持,直至整個程式結束。例如,全域變數就屬於這種方式。
這種由於儲存方式不同而產生的特性稱為變數的生存期。生存期和範圍是從時間和空間這兩個不同的概念來描述變數的特性,兩者既有關聯,又有區別。一個變數究竟屬於那種儲存方式,範圍之外還有儲存類型。
3.2 儲存類型
| 關鍵字 |
類型名 |
說明 |
備忘 |
| auto |
自動類型 |
預設類型 |
沒有賦初值,初值是隨機的 |
| register |
寄存器類型 |
動態儲存,在CPU中的通用寄存器中 |
一般只能是int、char和指標 |
| static |
靜態類型 |
有外部和內部靜態變數之分 |
都局限於某個某個範圍 |
| extern |
外部類型 |
定義在所有函數之外的全域變數 |
用於直接共用資料 |