《第3章 函數和程序結構》由會員分享,可在線閱讀,更多相關《第3章 函數和程序結構(46頁珍藏版)》請在裝配圖網上搜索。
1、Chapter 3 函數和程序結構3.1 函數的組織3.2 局部變量和全局變量、命名空間std3.3 形參與實參3.4 引用和const3.5 缺省參數值3.6 函數重載3.7 內聯函數3.8 編譯預處理3.1 函數的組織函數的組織結構化程序設計方法結構化程序設計方法使用結構化程序設計方法解決復雜的問題:分解大問題小問題更小問題求解(調用)main()函數函數一個函數是根據進去的信息(輸入)和產生的東西(輸出結果)所定義的一個黑盒。函數用于把較大的計算任務分解成若干個較小的任務,使程序人員可以在其他函數的基礎上構造程序,而不需要從頭做起。一個設計得當的函數可以把具體操作細節(jié)對程序中不需要知道它
2、們的那些部分隱藏掉,從而使整個程序結構清楚,減輕了因修改程序所帶來的麻煩。程序示例程序示例計算計算(12+10)!/(12!+10!)for example:ch3/3-1-1.cpp for example:ch3/3-1-2.cpp2層結構,2個函數,降低程序的構思、編寫、調試的復雜度,可讀性好函數定義返回類型 函數名(參數聲明列表)函數體;double max(double d1,double d2)return(d1d2?d1:d2);return語句用于從被調用函數向調用者返回值。沒有返回值的函數、沒有參數的函數,用關鍵字void修飾。void f(void)cout “aaaa”d
3、2?d1:d2);Void main()double f1,f2;cin f1 f2;cout max(f1,f2);cout max(67.4,23);形參實參實際參數可以是一個常量,變量,或甚至可以是一個表達式。3.2 局部變量和全局變量局部變量和全局變量變量作用范圍(作用域)在函數內定義的變量(包括形參)局部變量 作用范圍:本函數內部 定義在復合語句內的變量 作用范圍:復合語句內部全局變量 在函數以外定義的變量,不從屬于任一函數 作用范圍:從定義處到源文件結束3-4.cpp變量作用范圍示例int x=1;void main()int a=2;.int b=3;.f();.int t=4;
4、void f()int x=5,b=6;.int a=7;x=?a=?b=?b=?x=?b=?t=?a=?x=1 a=2 b=3b沒定義 x=5 b=6 t=4 a沒定義 若局部變量與全局變量同名,局部變量優(yōu)先變量作用范圍示例int x=1;int f(int x)return(x+);main()int y;y=f(2);x=f(x);printf(“%d%d”,y,x);x=1變量作用范圍l如果局部變量與全局變量同名,局部變量優(yōu)先。l不要濫用全局變量有副作用int x=1;void f1()x+;void f2()x=5;void main()x=10;f1();cout x;f2();c
5、out x;11 5 程序是變量(全局)定義和函數定義的集合。函數之間的通信可以通過參數、函數返回值以及外部變量進行。函數可以以任意次序出現在源文件中。源程序可以分成多個文件,只要不把一個函數分在幾個文件中就行。關鍵字extern變量的定義與是聲明是有不同的意義。作用域運算符:命名空間std 3-5-1.cpp 3-5-2.cpp3.3 形參與實參形參與實參函數的參數 函數的參數分為形參和實參兩種。形參出現在函數定義中,在整個函數體內都可以使用,離開該函數則不能使用。實參出現在主調函數中,進入被調函數后,實參變量也不能使用。形參和實參的功能是作數據傳送。發(fā)生函數調用時,主調函數把實參的值傳送給
6、被調函數的形參從而實現主調函數向被調函數的數據傳送。函數的形參和實參具有以下特點:1.形參變量只有在被調用時才分配內存單元,在調用結束時,即刻釋放所分配的內存單元。因此,形參只有在函數內部有效。函數調用結束返回主調函數后則不能再使用該形參變量。2.實參可以是常量、變量、表達式、函數等,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應預先用賦值,輸入等辦法使實參獲得確定值。3.實參和形參在數量上,類型上,順序上應嚴格一致,否則會發(fā)生“類型不匹配”的錯誤。值傳遞 函數調用中發(fā)生的數據傳送是單向的。即只能把實參的值傳送給形參,而不能把形參的值反向地傳送
7、給實參。因此在函數調用過程中,形參的值發(fā)生改變,而實參中的值不會變化。For example:ch3/3-6.cpp 如何實現函數調用中發(fā)生的數據傳送是雙向的。即修改了形參所指的單元,也就修改了實參。3.4 引用和引用和const引用就是給某一已存在的對象取一別名引用就是給某一已存在的對象取一別名(alias)(alias)。refcount is a reference of an intWhich int to reference exactly?is countas:int count=0;int&refcount=count;refcount=1;/here,count=1 count
8、+;/here,refcount=2;For example:ch3/3-8.cpprules when using references:1.A reference must be initialized when it is created.(Pointers can be initialized at any time.)2.int i;3.int&j;/4.j=i;5.2.Once a reference is initialized to an object,it cannot be changed to refer to another object.(Pointers can b
9、e pointed to another object at any time.)6.int i,k;7.int&j=i;8.int&j=k;/9.rules when using references:3.You cannot have NULL references.You must always be able to assume that a reference is connected to a legitimate piece of storage.int a=1;int&refint1=NULL;/References in functions int&fun(int&i)i+;
10、return i;用用constconst表示常量對象表示常量對象#define BUFSIZE 100 BUFSIZE只是一個名字,沒有存儲空間只是一個名字,沒有存儲空間問題:宏定義常量沒有類型信息;宏定義常量是問題:宏定義常量沒有類型信息;宏定義常量是全局的全局的(extern linkage);用宏定義常量編程,調用宏定義常量編程,調試時,不能直接知道它的名字。試時,不能直接知道它的名字。常量處理的老方法常量處理的老方法:C+C+中的常量處理方法中的常量處理方法:const:const的最初動機是的最初動機是取代預處理器取代預處理器definedefine進行值替代進行值替代For ex
11、ample:ch3/3-9.cppconst int bufsize=100;(1)有類型信息;有類型信息;const int i=20;chat chi;/正確正確 /以下程序錯:以下程序錯:int i;char chi;constconst用于函數參數和返回值用于函數參數和返回值intint f1(const f1(const intint&i)&i)i+;/Illegal-compile-time error,i+;/Illegal-compile-time error,Returning by const value例例1 1:const const intint g()g()retu
12、rn 1;return 1;例例2 2:const char*v()const char*v()return result of function v();return result of function v();3.5 缺省參數值缺省參數值在在C+C+中,函數聲明時,可以為一個或多個參中,函數聲明時,可以為一個或多個參數指定缺省參數值。數指定缺省參數值。例如:例如:/function definitionvoid delay(int k,int time)/function code body/function declarationvoid delay(int k,int time=30
13、0);/function calldelay(5);/等價與等價與delay(5,300);理解要點:理解要點:當函數調用執(zhí)行時,編譯器從左到右順序將實參與形參結合,當函數調用執(zhí)行時,編譯器從左到右順序將實參與形參結合,若未指定足夠的實參,則編譯器按同樣順序用函數聲明中的缺若未指定足夠的實參,則編譯器按同樣順序用函數聲明中的缺省值來補足所缺少的實參。省值來補足所缺少的實參。由于對缺省參數的處理是在函數調用表達式執(zhí)行時進行的,由于對缺省參數的處理是在函數調用表達式執(zhí)行時進行的,所以,在函數聲明中,所以,在函數聲明中,“=”后面可以是任意復雜的表達式,后面可以是任意復雜的表達式,甚至是函數調用。如
14、:甚至是函數調用。如:void delay(int k,int time=f(5);/其中其中f(5)是函數調用是函數調用所有帶缺省值的參數一律放在函數聲明中參數表的最右端。所有帶缺省值的參數一律放在函數聲明中參數表的最右端。void f(int,int y=1,int z);/因為:函數調用因為:函數調用f(2,4)將產生二義性:將產生二義性:4 y 還是還是 4 z void f(int,int y=1,int z=2);/在函數定義中,不再為參數指定缺省值。在函數定義中,不再為參數指定缺省值。(內聯函數除外內聯函數除外)3.6 函數重載函數重載一個函數名(標識符)可以被用作多個函數抽一個
15、函數名(標識符)可以被用作多個函數抽象的名字。編譯時,根據這些函數的參數個數、象的名字。編譯時,根據這些函數的參數個數、參數類型來區(qū)分(束定參數類型來區(qū)分(束定/綁定綁定binding)binding)For example:unit three:function_overloading.dsw二義性錯誤二義性錯誤Outline:要確保不同的參數類型確實不同。要確保不同的參數類型確實不同。例如:例如:typedef float real;float abs(float)/real abs(real)/函數的返回類型不能區(qū)分重載函數函數的返回類型不能區(qū)分重載函數例如:例如:int process
16、(int)/float process(int)/僅僅用了僅僅用了constconst而使參數類型有所不同,則而使參數類型有所不同,則不能區(qū)分。不能區(qū)分。例如例如:int process(int)/float process(const int)/Void Complex()apart=0.0;ipart=0.0;Void Complex(double d)apart=d;ipart=0.0;Void Complex(double a,double b)apart=a;ipart=b;Void Complex(double a=0.0,double b=0.0)apart=a;ipart=b;
17、When:Complex();/be equal to Complex(0.0,0.0)Complex(2);/Complex(2,0.0)3.7 內聯函數內聯函數function call need costs:involved from pushing arguments,making an assembly-language CALL,returning arguments,and performing an assembly-language RETURNFor example:ch3/3-10.cpp 在在C+C+中,用中,用inline functioninline functio
18、n來替換來替換帶參宏的使用。帶參宏的使用。C+C+的內聯機制和的內聯機制和C C中的帶中的帶參宏定義都是為了獲得較高的運行速度。參宏定義都是為了獲得較高的運行速度。使用宏和使用宏和內聯函數增加了代碼空間,但減內聯函數增加了代碼空間,但減少了程序執(zhí)行時間少了程序執(zhí)行時間。因為不再有函數調。因為不再有函數調用開銷。但用開銷。但C+C+的內聯機制比的內聯機制比C C中的帶參宏中的帶參宏定義更安全。定義更安全。#include inline float area(float r)return 3.14*r*r;int main()float f=2;float s=area(f+);coutfendl
19、sendl;return 0;inline function作用:在一個函數定義之前作用:在一個函數定義之前加上關鍵字加上關鍵字inline,則稱該則稱該函數為內聯函數。每當在其函數為內聯函數。每當在其它程序中有對該內聯函數的它程序中有對該內聯函數的調用,調用,C+編譯器都使用該編譯器都使用該內聯函數的代碼替換該內聯內聯函數的代碼替換該內聯函數的調用表達式。函數的調用表達式。使用內聯函數的優(yōu)點:使用內聯函數的優(yōu)點:參數的類型和函數返回值的類型都在函數聲明中進行明確的指定。參數的類型和函數返回值的類型都在函數聲明中進行明確的指定。這便于編譯器發(fā)現函數調用中的類型不一致的錯誤。這便于編譯器發(fā)現函數
20、調用中的類型不一致的錯誤。如果傳入函數的是表達式,則此表達式僅求值一次。如果傳入函數的是表達式,則此表達式僅求值一次。內聯函數只宜用于比較簡單的函數(不含循環(huán)語句和內聯函數只宜用于比較簡單的函數(不含循環(huán)語句和switch語句)。語句)。如果內聯函數的體很大,有可能使代碼的規(guī)模急劇增加。如果內聯函數的體很大,有可能使代碼的規(guī)模急劇增加。編譯器如何內聯編譯器如何內聯?64.內聯函數和編譯器內聯函數和編譯器 (1)編譯器如何實現內聯?編譯器如何實現內聯?(2)內聯的局限性內聯的局限性:a)假如函數太復雜假如函數太復雜,編譯器將不能執(zhí)行內聯編譯器將不能執(zhí)行內聯(復雜的函數定義順序、構造函數和復雜的函
21、數定義順序、構造函數和析構函數析構函數).b)假如在程序中假如在程序中,隱含或顯式取該函數的地址隱含或顯式取該函數的地址,則則 c)在程序的在程序的Debug版本版本,不執(zhí)行內聯不執(zhí)行內聯 3.8 編譯預處理編譯預處理 在前面各章中,已多次使用過以“#”號開頭的預處理命令。如包含命令#include,宏定義命令#define等。在源程序中這些命令都放在函數之外,而且一般都放在源文件的前面,它們稱為預處理部分。所謂預處理是指在進行編譯的第一遍掃描(詞法掃描和語法分析)之前所作的工作。預處理是語言的一個重要功能,它由預處理程序負責完成。當對一個源文件進行編譯時,系統(tǒng)將自動引用預處理程序對源程序中的
22、預處理部分作處理,處理完畢自動進入對源程序的編譯。語言提供了多種預處理功能,如宏定義、文件包含、條件編譯等。合理地使用預處理功能編寫的程序便于閱讀、修改、移植和調試,也有利于模塊化程序設計。本章介紹常用的幾種預處理功能。無參宏#define PI 3.14159#define NoBall 0#define PlayerBall 1#define ComputerBall 2/無參宏,用符號表示常量,For example:3-9.cpp有參宏和inline(內聯)函數#define aera(x)PI*(x)*(x)利用有參宏來替代一些簡單的函數調用,為了減少由于調用函數所帶來的一些開銷.F
23、or example:3-10.cpp文件包含#include#include“chess.h 文件包含命令的功能是把指定的文件插入該命令行位置取代該命令行,從而把指定的文件和當前的源程序文件連成一個源文件。在程序設計中,文件包含是很有用的。一個大的程序可以分為多個模塊,由多個程序員分別編程。有些公用的符號常量或宏定義等可單獨組成一個文件,在其它文件的開頭用包含命令包含該文件即可使用。這樣,可避免在每個文件開頭都去書寫那些公用量,從而節(jié)省時間,并減少出錯。對文件包含命令還要說明以下幾點:1.使用尖括號表示在包含文件目錄中去查找(包含目錄是由用戶在設置環(huán)境時設置的),而不在源文件目錄去查找;使用雙引號則表示首先在當前的源文件目錄中查找,若未找到才到包含目錄中去查找。用戶編程時可根據自己文件所在的目錄來選擇某一種命令形式。2.一個include命令只能指定一個被包含文件,若有多個文件要包含,則需用多個include命令。3.文件包含允許嵌套,即在一個被包含的文件中又可以包含另一個文件。條件編譯 可以按不同的條件去編譯不同的程序部分,因而產生不同的目標代碼文件。這對于程序的移植和調試是很有用的。條件編譯有三種形式,下面分別介紹:1.第一種形式:#ifdef 標識符 程序段1#else 程序段2#endif Lab and ExerciseLab:實驗五