C語言程序設計-第5章_第1頁
C語言程序設計-第5章_第2頁
C語言程序設計-第5章_第3頁
C語言程序設計-第5章_第4頁
C語言程序設計-第5章_第5頁
已閱讀5頁,還剩68頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

C語言與程序設計

TheCProgrammingLanguage

第5章函數與程序結構

華中科技大學計算機學院

盧萍2/2/20231華中科技大學計算機學院C語言課程組第5章函數與程序結構

結構化編程和C程序的一般結構函數的機制,包括函數定義、函數聲明、函數調用、變量的存儲類型、參數數目可變的函數等。2/2/20232華中科技大學計算機學院C語言課程組5.1C程序的一般結構5.1.1結構化程序設計結構化編程是一種解決問題的策略,它包括如下2條編程標準:(1)程序中的控制流應該盡可能簡單。(2)應該自頂向下地設計程序結構。自頂向下設計也稱為逐步細化,即把一個問題按功能分解為若干子問題,如果子問題還較復雜,可將其繼續分解,直到分解成為容易求解的子問題為止。分解而來的每個子問題被稱為模塊,C中提供的函數機制完成每個模塊的編程任務,即用函數編寫由分解而來的子問題的代碼。2/2/20233華中科技大學計算機學院C語言課程組5.1.2蒙特卡羅模擬:猜數游戲模擬算法是最基本的算法,例如,編程實現拋硬幣、擲骰子和玩牌等現實世界中的隨機事件要用模擬算法。在程序設計中,可使用隨機數函數來模擬現實中不可預測情況,這稱為蒙特卡羅模擬。隨機數以其不確定性和偶然性等特點在很多地方都有具體的用處。比如,軟件測試中,用于產生具有普遍意義的測試數據,在加密系統中產生密鑰,在網絡中生成驗證碼等。在C語言中,用rand函數生成隨機數,該函數稱為隨機數發生器,該發生器從稱為種子(一個無符號整型數)的初始值開始用確定的算法產生隨機數。顯然,通過種子產生第一個隨機數后,后續的隨機序列也就是確定的了,這種依靠計算機內部算法產生的“隨機”數稱為偽隨機數。由此可見,隨機數的產生依賴于種子,為了使程序在反復運行時能產生不同的隨機數,必須改變這個種子的值,這稱為初始化隨機數發生器,由函數srand來實現。2/2/20234華中科技大學計算機學院C語言課程組【例5.1】編寫一個猜數的游戲程序在這個游戲中,計算機產生一個1到1000之間的隨機數,并把該數作為要猜的數。玩游戲者輸入所猜的數,如果猜得不正確,繼續猜直到正確為止,同時計算游戲者猜數的次數。為了幫助游戲者一步一步得到正確答案,程序會不斷地發出信息“Toohigh”或“Toolow”。最后,程序向游戲者顯示游戲結果。2/2/20235華中科技大學計算機學院C語言課程組自頂向下的分解問題:既然是一個游戲程序,就應該允許玩家反復玩多次,直到不想玩為止。同時,將玩一次游戲的任務分解成以下兩個子任務:(1)計算機產生一個1到1000的隨機數供游戲者猜測;(2)游戲者猜數,直至猜對。2/2/20236華中科技大學計算機學院C語言課程組主程序結構do{ 計算機產生一個1到1000的隨機數 游戲者猜數,直至猜對繼續玩嗎}while(繼續);2/2/20237華中科技大學計算機學院C語言課程組自頂向下的分解子任務(1)①調用標準庫函數rand產生一個隨機數;②將這個隨機數限制在1~1000之間。利用函數,可以實現程序的模塊化,把程序中常用的一些算法或操作編成通用的函數,以供隨時調用,大大簡化主函數的流程,使程序設計簡單和直觀,提高程序的易讀性和可維護性。把任務1設計成一個獨立的函數,用函數GetNum(void)來實現.2/2/20238華中科技大學計算機學院C語言課程組函數intGetNum(void)/****************************************************************函數名稱:GetNum函數功能:產生一個1到MAX_NUMBER之間的隨機數,供游戲者猜測。函數參數:無函數返回值:返回產生的隨機數****************************************************************/intGetNum(void) /*注意:后面無分號*/{ /*函數開始的標志*/ intx; printf("Amagicnumberbetween1and%dhasbeenchosen.\n",MAX_NUMBER); x=rand(); /*調用標準庫函數rand產生一個隨機數*/ x=x%MAX_NUMBER+1; /*將這個隨機數限制在1~MAX_NUMBER之間*/ return(x); /*返回這個隨機數給調用者,*/} /*函數結束的標志*/2/2/20239華中科技大學計算機學院C語言課程組在函數的頂端用“/*……*/”格式包含的部分是函數頭部注釋,包括函數名稱、函數功能、函數參數、函數返回值等內容,如有必要還可增加作者、創建日期、修改記錄(備注)等相關項目。雖然函數頭部注釋在語法上不是必需的,但可以提高程序的質量和可維護性,在程序設計時要遵從這一編程規范。GetNum是函數名,其后的void說明函數調用時不接收任何參數,即沒有入口參數,函數執行完應該返回所產生的隨機數,即該隨機數是函數的出口參數,函數名前的int說明出口參數的類型為整型。函數體內的rand是接口stdlib.h中的一個函數,它返回一個非負并且不大于常量RAND_MAX的隨機整數,RAND_MAX的值取決于計算機系統。MAX_NUMBER是用#define定義的符號常量,其值為1000。當執行return語句時,其后表達式的值被帶回到調用函數中。2/2/202310華中科技大學計算機學院C語言課程組自頂向下的分解子任務(2)/*子任務2的程序段*/for(;;){輸入猜測的數if(猜對了)結束elseif(小了)輸出太小的提示else輸出太大的提示}將任務(2)也設計成一個獨立的函數GuessNum2/2/202311華中科技大學計算機學院C語言課程組函數voidGuessNum(intx)源程序\ex5_1.cGuessNum是函數名,括號里的“intx”說明該函數有一個入口參數x,其類型為整型,表示被猜測的神秘數,游戲者如果猜對就直接屏幕輸出猜的次數,無具體值返回,即函數無出口參數,所以將函數值的類型說明為void。函數體內有1條for(;;)循環語句,一直循環到執行return語句時結束。2/2/202312華中科技大學計算機學院C語言課程組主函數main源程序\ex5_1.c用蒙特卡羅法模擬該猜數游戲,在開始玩游戲前,需要調用函數srand初始化隨機數種子,可以采用系統時間(由time函數得到)作為種子.函數time返回自1970年1月1日以來經歷的秒數,將該秒數賦值給種子變量,隨機數種子會隨著運行程序的時間而改變,因而產生的隨機數是不可預見的.函數srand和rand的原型在stdlib.h中,函數time的原型在time.h中,需要在源程序的頭部包含這兩個頭文件.2/2/202313華中科技大學計算機學院C語言課程組結構化程序設計的益處使程序編制方便,易于管理、修改和調試。增強了程序的可讀性、可維護性和可擴充性,方便于多人分工合作完成程序的編制。函數可以公用,避免在程序中使用重復的代碼。提高軟件的可重用性,軟件的可重用性是轉向面向對象程序設計的重要因素。2/2/202314華中科技大學計算機學院C語言課程組5.1.3C程序的結構C程序由一個或多個函數組成,其中有且只有一個main函數,程序的執行總是從main開始。除main以外的其它函數分兩類,一類是由系統提供的標準函數。另一類是需要由程序員自己編寫的函數(“自定義函數”)。組成一個C程序的各個函數可以編輯成多個C源文件,每一個C源文件含有一個或多個函數定義。各C源文件中要用到的一些外部變量說明、枚舉類型聲明、結構類型聲明、函數原型和編譯預處理指令等可編輯成一個.h頭文件,然后在每個C文件中包含該頭文件.2/2/202315華中科技大學計算機學院C語言課程組每個源文件可單獨編譯生成目標文件,組成一個C程序的所有源文件都被編譯之后,由連接程序將各目標文件中的目標函數和系統標準函數庫的函數裝配成一個可執行C程序。圖5.1顯示了C語言程序的基本結構。2/2/202316華中科技大學計算機學院C語言課程組圖5.1C語言程序的基本結構2/2/202317華中科技大學計算機學院C語言課程組5.2函數的定義與函數的聲明程序中若要使用自定義函數實現所需的功能,需要做三件事:①按語法規則編寫完成指定任務的函數,即定義函數;②有些情況下在調用函數之前要進行函數聲明;③在需要使用函數時調用函數2/2/202318華中科技大學計算機學院C語言課程組5.2.1函數的定義函數定義的一般形式為:類型名函數名(參數列表){聲明部分語句部分}2/2/202319華中科技大學計算機學院C語言課程組類型名說明函數返回值(即出口參數)的數據類型(簡稱為函數的類型或函數值的類型),可以是除數組以外的任何類型。當返回值類型為void,函數將不返回任何值。參數列表說明函數入口參數的名稱、類型和個數,它是一個用逗號分隔的變量名及其類型列表,它描述了在調用函數時函數所接收的參數。一個函數可能沒有參數,在沒有參數的情況下,參數列表說明為void,否則必須明確地列出每一個參數的類型。2/2/202320華中科技大學計算機學院C語言課程組在參數列表中定義的參數稱為形式參數(簡稱形參),用以強調它作為占位符的角色。形參是函數要處理的數據,在函數調用時要將實際值傳遞給形參。在定義變量時,相同類型的變量可以用一個數據類型關鍵字定義。但是,和變量定義不同的是,每一個形參都必須有數據類型和名字。例如:doublepower(intx,intn){…}/*正確的函數參數定義*/doublepower(intx,n){…} /*錯誤,n必須指定類型*/intGetNum(void){…} /*參數表為空的函數*/GetNum(void){…} /*C89中默認返回int,但C99不允許*/良好的編程風格是:在每個函數的頂端用“/*……*/”格式增加函數頭部注釋,如例5.1所示。為了節省篇幅,本書后面的例子采用簡化的注釋,希望讀者寫程序時按例5.1的格式寫頭部注釋。為函數命名時,要選擇有意義的名稱,以增加程序的可讀性,還可避免過多地使用注釋。Windows風格函數名用小寫字母命名,每個詞的第一個字母大寫,通常用“動詞”或者“動詞+名詞”(動賓詞組)形式。2/2/202321華中科技大學計算機學院C語言課程組5.2.2函數的返回值return語句可以是如下兩種形式之一:(1)return;/*void函數*/(2)return表達式;/*非void函數*/void函數也可以不包含return語句。如果沒有return語句,當執行到函數結束的右花括號時,控制返回到調用處,把這種情況稱為離開結束。表達式值的類型應該與函數定義的返回值類型一致,如果不相同,就把表達式值的類型自動轉換為函數的類型。

2/2/202322華中科技大學計算機學院C語言課程組函數返回的值,程序可以使用它,

也可以不使用它while(…){getchar();/*返回值不被使用*/c=getchar();/*返回值被使用*/…}2/2/202323華中科技大學計算機學院C語言課程組【例5.2】寫一個函數IsPrimeIsPrime判斷整數n是否為素數。如果n是素數,則返回1;如果n不是素數,則返回0。源程序\ex5_2.c在一個函數中可以有多個return語句,此種情況下的return語句通常被作為選擇語句的子句出現,最終被執行的只是其中的一個。因為,一旦某個return語句被執行,控制立即返回到調用處,其后的代碼不可能被執行。2/2/202324華中科技大學計算機學院C語言課程組5.2.3函數的聲明1.函數定義起函數聲明的作用要利用函數定義起函數聲明的作用,在調用函數之前,必須給出調用函數的函數定義。例如,將main函數的定義放在最后。#include<stdio.h>#include<stdio.h>#include<stdlib.h>#include<time.h>#defineMAX_NUMBER1000 intGetNum(void){…}voidGuessNum(intx){…}intmain(void){…}2/2/202325華中科技大學計算機學院C語言課程組2.函數原型函數定義出現在函數調用后被調用函數在其它文件中定義必須在函數調用之前給出函數原型。2/2/202326華中科技大學計算機學院C語言課程組函數原型的一般形式

類型名函數名(參數類型表);參數類型表通常是用逗號隔開的形參類型列表,而形參名可以省略。例如,voidGuessNum(int);等價于

voidGuessNum(intx);無參函數的函數原型參數表必須指定為void2/2/202327華中科技大學計算機學院C語言課程組函數原型的作用函數原型告訴編譯器函數返回值的數據類型,傳遞給函數的參數個數、參數類型和參數順序。編譯器用函數原型校驗函數調用,強制轉換傳遞的參數類型,從而避免錯誤的函數調用導致的致命運行錯誤或微妙而難以檢測的非致命邏輯錯誤。例如,

printf("%.1f\n",sqrt(4));(1)包含了math.h,輸出2.0*/(2)沒有包含math.h,函數調用sqrt(4)不會產生正確的值。2/2/202328華中科技大學計算機學院C語言課程組3.遺漏函數原型編譯器首次遇到沒有聲明的函數調用時,將構造一個缺省聲明并繼續編譯:intf();/*函數是int類型,但不給出參數表信息*/(1)函數類型不是int,給出一個錯誤提示:

Typemismatchinredeclarationof'f‘(2)函數類型是int,就不給出錯誤提示。編譯器對參數類型不作檢查,把正確類型的參數傳遞給函數是程序員的責任。假設函數f只有一個類型為int的參數,函數調用f(2)會產生正確的值,而函數調用f(2.5)將產生錯誤的運行結果。2/2/202329華中科技大學計算機學院C語言課程組良好的編程風格在函數調用之前必須給出它的函數定義、函數原型或兩者都給出。引入標準頭文件的主要原因是它含有函數原型。2/2/202330華中科技大學計算機學院C語言課程組5.2.4新增關鍵字inline和_Noreturninline是C99增加的關鍵字,它用在函數定義的前面,告訴編譯器對該函數的調用進行內聯優化,這意味著編譯器將采用插入的方式處理函數調用,而不是采用函數調用的方式,但是,這只是一個請求,編譯器可以忽略這個請求。只有支持C99的編譯器才能識別inline。_Noreturn是C11增加的關鍵字,它用在函數定義的前面,告訴編譯器這個函數不會返回到調用處,其結果是讓編譯器知道調用說明為_Noreturn的函數之后的代碼不可到達。2/2/202331華中科技大學計算機學院C語言課程組關鍵字inlineinlineintadd(inta,intb){returna+b;}那么,在程序編譯時,編譯器將程序中出現的內聯函數的調用表達式用內聯函數的函數體來進行替換。這將不會產生調用和返回所帶來的開銷,節約了時間,但是由于在編譯時函數體中的代碼被替代到程序中,因此會增加目標程序代碼量,進而增加空間開銷,可見它是以增加目標代碼為代價而換取時間上的節省。2/2/202332華中科技大學計算機學院C語言課程組關于inlineinline函數僅僅是對編譯器的建議,最后能否真正內聯,看編譯器的意思,它如果認為函數不復雜,能在調用點展開,就會真正內聯。因為內聯函數要在調用點展開,所以編譯器必須隨處可見內聯函數的定義,否則,就成了非內聯函數的調用了。所以,內聯函數必須在調用之前定義,并且在每個調用了內聯函數的文件中都要有該內聯函數的定義,因此,通常將內聯函數定義放在頭文件中共享。inline函數是可以被重復定義的。inline的使用是有限制的,內聯函數應該簡潔,只有幾個語句,如果語句較多,不適合定義為內聯函數。內聯函數體中,不能有循環語句、if語句或switch語句,否則,即使函數定義時使用了inline關鍵字,編譯器也會把該函數作為普通函數處理。2/2/202333華中科技大學計算機學院C語言課程組關鍵字_Noreturn_Noreturn用在函數定義的前面,告訴編譯器這個函數不會返回到調用處。例如:_Noreturnvoidf() /*f函數沒有返回,所以說明為_Noreturn*/{abort(); /*終止程序*/}_Noreturnvoidg(inti)/*假如

i<=0,將導致未定義的行為*/{if(i>0)abort();}2/2/202334華中科技大學計算機學院C語言課程組5.3函數調用與參數傳遞5.3.1函數調用1.函數調用的形式

函數名(實參列表)實參是一個表達式,對于無函數,調用形式:

函數名()

2/2/202335華中科技大學計算機學院C語言課程組函數調用的形式(1)作為表達式語句出現。GuessNum();putchar(c);(2)作為表達式中的一個操作數出現。例如:c=getchar()magic=GetNum();(3)作為函數調用的一個實參出現,即嵌套調用。printf("%10.0f",sqrt(x));while(putchar(getchar())!='#');2/2/202336華中科技大學計算機學院C語言課程組2.函數調用的執行過程為了說明函數的調用過程,請看下面程序。【例5.3】編寫程序實現如下功能:分列整齊地顯示整數1到10的2至5次冪。輸出結果如下所示:IntSquareCubeQuarticQuintic11111248163239278124341664256102452512562531256362161296777674934324011680786451240963276898172965615904910100100010000100000源程序\ex5_3.c2/2/202337華中科技大學計算機學院C語言課程組程序分析該程序由主函數main和用戶自定義函數power組成。main函數在printf語句中嵌套調用了power函數。程序從main函數開始執行,當執行到printf("%10.0f",power(i,j));時,首先調用函數power,這時系統為形參x和n分配2或4字節的存儲單元,將實參i的值傳遞給形參x,將實參j的值傳遞給形參n,然后把程序控制轉移到函數power,執行其函數體內的語句,其中的for語句計算xn,并把計算結果保存在變量p中,語句returnp;將程序控制返回到調用處(即main函數中的printf("%10.0f",power(i,j));語句),同時把p的值送回,至此,power函數調用執行完畢,系統釋放形參所占據的存儲單元。程序從main函數調用點繼續執行,輸出返回的p值。main函數用二重for循環結構重復調用了power函數40次,每次傳遞給形參的值不同即可計算出題目要求的所有xn值(x=1~10,n=2~5)。2/2/202338華中科技大學計算機學院C語言課程組3.實參的求值順序實參的求值順序由具體實現確定,有的按從左至右的順序計算,有的按從右至左的順序計算a=1;power(a,a++)----從左至右:power(1,1)----從右至左:power(2,1)(多數)為了保證程序清晰、可移植,應避免使用會引起副作用的實參表達式.2/2/202339華中科技大學計算機學院C語言課程組5.3.2參數的值傳遞參數的傳遞方式是“值傳遞”,實參的值單向傳遞給相應的形參。如果實參、形參都是x,被調用函數不能改變實參x的值。2/2/202340華中科技大學計算機學院C語言課程組【例5.4】改寫例5.3來說明值傳遞概念。

源程序\ex5_4.c由于參數是值傳遞,power函數中的形參n可以當普通的局部變量使用,被用作臨時變量,控制for的循環次數,這樣就不再需要引入局部變量i,從而使程序更簡潔。主函數main中的局部變量可以與函數power的形參x、n同名,但它們的作用域不同,只能作用于定義它的函數。當第1次執行函數調用power(x,n)時,把x的值(即1)傳遞給形參x,把n的值(即2)傳遞給形參n,在函數power內改變了形參n的值(由2變化為0),但并不能改變函數main中變量n的值。2/2/202341華中科技大學計算機學院C語言課程組傳地址(引用)調用傳地址(引用)調用是將變量的地址傳遞給函數,函數既可以使用,也可以改變實參變量的值。標準庫函數scanf就是一個引用調用的例子,通過地址實參返回一個或多個數據。程序員也可以定義這種帶有地址形參的函數,這部分內容將在第9章中介紹。2/2/202342華中科技大學計算機學院C語言課程組5.4作用域與可見性作用域和可見性是對一個問題的兩種角度的思考。作用域是指標識符(變量或函數)的有效范圍,也就是指程序正文中可以使用該標識符的那部分程序段。有局部和全局兩種作用域,局部作用域表示只能在一定的范圍內起作用,只能被一個程序塊訪問,全局作用域表示可以在整個程序的所有范圍內起作用,可由程序中的部分或所有函數共享,程序塊是帶有說明的復合語句(包括函數體)。代碼中的變量,按照作用域可分為局部變量和全局變量。函數都是全局的。2/2/202343華中科技大學計算機學院C語言課程組5.4.1局部變量和全局變量局部變量:在函數內部定義的變量,作用域是定義該變量的程序塊,程序塊是帶有說明的復合語句(包括函數體)。不同函數可同名,同一函數內不同程序塊可同名。形式參數是局部變量。外部變量:在函數外部定義的變量,其作用域從其定義處開始一直到其所在文件的末尾,可由程序中的部分或所有函數共享。2/2/202344華中科技大學計算機學院C語言課程組extern聲明在函數中使用外部變量,一般要對該變量進行引用性聲明,說明它的類型。只有在函數內經過引用性聲明的外部變量才能使用,外部變量的說明符為extern。但在一個外部變量定義之后的函數內使用可不再加以extern聲明。【例5.5】改寫例5.1,把magic定義為外部變量。這需要修改函數GuessNum的調用、原型和定義。源程序\ex5_5.c2/2/202345華中科技大學計算機學院C語言課程組程序分析magic是在函數外部定義的,它是外部變量,由于函數main和GuessNum使用了它,所以在這兩個函數中編寫了一條extern聲明語句,該聲明語句除了在前面加了關鍵字extern外,其余與該外部變量的定義相同。但是,在該例中,兩條extern聲明是多余的,均可以省略,因為magic的定義出現在函數main和GuessNum之前。2/2/202346華中科技大學計算機學院C語言課程組外部變量的定義性聲明和引用性聲明局部變量只有定義性聲明,沒有引用性聲明。而外部變量有定義性聲明和引用性聲明,兩者具有嚴格的區別。外部變量的定義必須在所有的函數之外,且只能定義一次,目的是為之分配存儲單元。外部變量的引用性聲明既可以出現在函數內,也可以出現在函數外,而且可以出現多次,僅用于通報變量的類型,并不分配存儲單元,只是表明在代碼中要按聲明的類型使用它。外部變量的初始化只能出現在其定義中。2/2/202347華中科技大學計算機學院C語言課程組外部變量PK形式參數在C程序的不同源文件之間,或者在同一源文件的不同函數之間必須共享變量時,外部變量是很有用的。函數之間的數據聯系除了通過形式參數外,也可以利用外部變量。從結構化程序設計的觀點來看,不要過于依賴外部變量,因為這將使函數的獨立性降低。很難在其他程序中復用依賴于外部變量的函數,為了在另一個程序中使用該函數,必須帶上此函數需要的外部變量。在程序維護期間,如果改變外部變量(比方說改變它的類型),那么將需要檢查程序中的每個函數,以確認該變化如何對函數產生影響。如果外部變量被賦了錯誤的值,可能很難確定出錯的函數。在大多數情況下,對函數而言,通過形式參數進行通信比通過外部變量通信的方法更好。這樣有助于提高函數的通用性,降低副作用。2/2/202348華中科技大學計算機學院C語言課程組5.4.2作用域規則(1)文件范圍。其作用域開始于文件開頭,結束于文件結尾。全局變量和函數具有文件作用域,從定義它們的位置開始一直到本文件結束。全局變量和函數如果在定義時使用了static存儲類關鍵字,它們的作用域只限定在本文件以內;如果在定義時沒有使用static存儲類關鍵字,它們的作用域可以通過extern聲明語句擴展到定義點之前及其他文件,使得它們在定義之前可以使用,以及其他與定義所在文件不同的源文件中的函數也可以使用。關于static和extern的詳細用法,見5.5節。2/2/202349華中科技大學計算機學院C語言課程組(2)塊范圍。其作用域開始于左大花括號“{”,結束于右大花括號“}”。在復合語句內定義的變量其作用域屬于塊范圍,就在定義該變量的塊內。在函數的開始部分定義的變量和函數的形式參數,其作用域也屬于塊范圍,就在定義該變量的函數內部。在不同函數中的同名變量相互之間沒有任何關系。(3)函數原型范圍。在函數原型中說明的變量只在函數原型內有效,開始于原型左括號“(”,結束于原型右括號“)”。(4)函數范圍。開始于函數體的左大花括號“{”,結束于函數體的右大花括號“}”。只有goto語句的標號屬于函數范圍,只能作用于同一函數內。2/2/202350華中科技大學計算機學院C語言課程組區分外部變量的引用性聲明和定義性聲明外部變量的引用性聲明用于通報變量的性質(主要是變量的類型)外部變量的定義性聲明除此以外還引起存儲分配。如果在函數外部有如下說明語句:intsp;這是外部變量的定義性聲明,一方面說明了sp的類型為int,另一方面系統還要為其分配2字節的存儲單元。而如下語句:externintsp;這是外部變量的引用性聲明,僅僅說明了sp是一個類型為int的外部變量,系統不會為其分配存儲單元。在一個程序中對一個外部變量只能在某個文件中定義一次,而外部變量的引用性聲明可以出現多次。外部變量的初始化只能出現在其定義中。2/2/202351華中科技大學計算機學院C語言課程組5.4.3可見性作用域的嵌套:程序塊可以多重嵌套,每個塊都可以定義自己的變量名。外層塊的變量名在內層塊中是有效的。但是,如果一個變量名a同時出現在外層塊和內層塊中,外層a的作用域包含了內層a的作用域,這稱為作用域的嵌套。當內層的變量和外層的變量同名時,在內層里,外層的變量暫時失去了可見性,是不可見的。2/2/202352華中科技大學計算機學院C語言課程組塊多重嵌套時變量的可見性外層塊的a在內層塊不可見,在內層塊中對a的修改不會影響到外層塊的a。因此,在進入復合語句之前和退出復合語句之后輸出的a均為2。而變量b在內層塊沒有重定義,它在整個函數體都可見。同樣地,全局變量和局部變量也可以同名。在局部變量的作用域內,全局變量不可見。2/2/202353華中科技大學計算機學院C語言課程組5.5存儲類型變量和函數都有兩個屬性:數據類型和存儲類型。函數的存儲類型決定函數的作用域,可使用的關鍵字有extern和static(5.9節)。變量的存儲類型決定變量的作用域、存儲分配方式、生命周期和初始化方式,可使用的關鍵字有:auto、extern、static和register。

存儲分配方式:在何時、何處給變量分配存儲單元;

生命周期:變量在內存中的存在期;

初始化方式:在定義變量時如果未顯示初始化,是否有缺省初值,如果有顯示初始化,賦初值操作如何執行(執行一次還是執行多次)。2/2/202354華中科技大學計算機學院C語言課程組5.5.1存儲類型auto局部變量的缺省存儲類型是auto,稱自動變量autointa;/*等價于inta;也等價于autoa;*/

作用域:局部于定義它的塊,從塊內定義之后直到該塊結束有效。存儲分配方式:動態分配方式,即在程序運行過程中分配和回收存儲單元。生命周期:短暫的,只存在于該塊的執行期間。初始化方式:定義時沒有顯示初始化,其初值是不確定的;有顯示初始化,則每次進入塊時都要執行一次賦初值操作。2/2/202355華中科技大學計算機學院C語言課程組5.5.2存儲類型extern外部變量的存儲類型是extern,但在定義時不使用關鍵字extern。外部變量的作用域:從定義之后直到該源文件結束的所有函數,通過用extern做聲明,外部變量的作用域可以擴大到整個程序的所有文件。存儲分配方式:靜態分配方式,即程序運行之前,系統就為外部變量在靜態區分配存儲單元,整個程序運行結束后所占用的存儲單元才被收回。生命周期:永久的,存在于整個程序的執行期間。初始化方式:定義時沒有顯示初始化,初值0;有顯示初始化,只執行一次賦初值操作。2/2/202356華中科技大學計算機學院C語言課程組【例5.6】關鍵字extern的用法修改例5.5,將整個程序放在兩個文件ex5_6_1.c和ex5_6_2.c中源程序\ex5_6_1.c源程序\ex5_6_2.c當編寫大型程序時,構造成多文件是很重要的,每個文件可以單獨編譯。2/2/202357華中科技大學計算機學院C語言課程組程序分析該程序包含2個源文件ex5_6_1.c和ex5_6_2.c,外部變量magic在ex5_6_1.c文件中定義,在ex5_6_2.c文件中必須聲明后才能使用它。ex5_6_2.c文件中的第4行代碼就是外部變量magic的聲明,extern告訴編譯器變量magic被定義在別處,可能在本文件中,也可能在另一個文件中,但編譯器并不知道它在哪個文件中定義,因此讓連接程序查找。如果連接程序找到了外部變量magic的正確的定義,它就會指明其位置,從而解決對該變量的引用。如果連接程序沒有找到magic的定義,它就會發出錯誤信息并且不生成可執行文件。2/2/202358華中科技大學計算機學院C語言課程組2.外部函數函數一般是全局的,作用域屬于文件范圍,對程序的任何部分都是可見的,其默認存儲類型是extern,這種函數稱為外部函數。在函數定義和函數原型中都可以使用關鍵字extern。例如:externdoublepower(int,int);是函數power的函數原型,其函數定義可為:externdoublepower(intx,intn){…}power是外部函數,它可被本文件和其他文件中的函數調用。函數定義時一般省略extern,在其他需要調用它的文件中,一般用extern聲明。例如,例5.6的ex5_6_1.c文件中的函數原型常寫作:externintGetNum(void);externvoidGuessNum(void);2/2/202359華中科技大學計算機學院C語言課程組5.5.3存儲類型static關鍵字static有兩個重要而截然不同的用法:(1)用于定義局部變量,稱為靜態局部變量。(2)用于定義外部變量,稱為靜態外部變量。

存儲分配方式:靜態分配方式

生命周期:永久的,

缺省初值:0,只執行一次靜態局部變量和自動變量有根本性的區別。靜態外部變量和外部變量區別:作用域不同。2/2/202360華中科技大學計算機學院C語言課程組1.靜態局部變量靜態局部變量的作用域和自動變量一樣,只作用于定義它的塊。由于靜態局部變量在程序執行期間不會消失,因此,它的值有連續性,當退出塊時,它的值能保存下來,以便再次進入塊時使用。而自動變量的值在退出塊時都丟失了。如果定義時靜態局部變量有顯示初始化,只執行一次賦初值操作,而自動變量每次進入時都要執行賦初值操作。2/2/202361華中科技大學計算機學院C語言課程組【例5.7】編程計算1!+2!+3!+4!+…n!將求階乘定義成函數,由于n!=n*(n-1)!,可以直接利用上次調用后的值算出,使計算量最小。為了使局部變量的值在離開函數后能保存,必須在定義時加static,成為靜態局部變量。源程序\ex5_7.c2/2/202362華中科技大學計算機學院C語言課程組程序分析在函數fac內的變量f是靜態局部變量。在第一次調用函數fac時,把f初始化為1,在退出函數時,f的值被保存在內存中。當再次調用函數時,就不會再對f進行初始化,而使用上次退出函數時保存在內存中的值。雖然無論使用與否,f都占據內存,但是,變量f只能被函數fac訪問,其他函數不能訪問。請讀者思考,如果將f定義為自動變量(即去掉關鍵字static),程序的輸出結果如何?使用靜態局部變量是為了多次調用同一函數時使變量能保持上次調用結束時的結果。即靜態局部變量的值有記憶性。2/2/202363華中科技大學計算機學院C語言課程組2.靜態外部變量靜態外部變量和外部變量的區別是作用域的限制。靜態外部變量只能作用于定義它的文件,其它文件中的函數不能使用,外部變量用extern聲明后可以作用于其它文件。使用靜態外部變量的好處在于:當多人分別編寫一個程序的不同文件時,可以按照需要命名變量而不必考慮是否會與其它文件中的變量同名,保證文件的獨立性。和局部變量能夠屏蔽同名的外部變量一樣,一個文件中的靜態外部變量能夠屏蔽其他文件中同名的外部變量。在靜態外部變量所在的文件中,同名的外部變量不可見。2/2/202364華中科技大學計算機學院C語言課程組【例5.8】偽隨機數發生器的實現與使用產生隨機序列需要給定初始種子,因此種子seed是各隨機數發生器函數所共享的變量,應定義在函數外。而且,seed只提供給srandom、random和probability等產生隨機數的函數操作,并不希望任何其他函數訪問它們,因此將函數srandom、random和probability,以及它們所操作的種子seed設計在一個源文件ex5_8_1.c中,在定義seed時加上static,限制其作用域只在本文件內。將函數main設計在另一個源文件ex5_8_2.c中。源程序\ex5_8_1.c源程序\ex5_8_2.c2/2/202365華中科技大學計算機學院C語言課程組程序分析以上程序由兩個文件組成,在文件ex5_8_1.c中定義了一個靜態外部變量seed,依賴變量seed的舊值,調用函數random和probability為變量seed產生一個新值。由于seed是靜態外部變量,它對本文件的這些函數來說是共享的,它的值在函數調用之間被保存下來了,但是,它對本文件來說是私有的,在文件ex5_8_2.c中不能用extern對seed做聲明,也不能在main函數中使用seed。現在可以在文件ex5_8_2.c中調用這些隨機數發生器函數而不必擔心副作用。2/2/202366華中科技大學計算機學院C語言課程組3.靜態函數如果要把函數的作用域限制在定義它的文件中,在函數定義時必須使用關鍵字static,這種函數稱為靜態函數。例如:doublepower(int,int); /*power函數原型*/voidmain(void){ …/*函數power可被調用,但在其他文件不能被調用*/}staticdoublepower(intx,intn);/*靜態函數*/{ …}和靜態外部變量一樣,靜態函數也只作用于所在文件,不同文件中的靜態函數可以同名,保證文件的獨立性。2/2/202367華中科技大學計算機學院C語言課程組5.5.4存儲類型register用來定義局部變量,register建議編譯器把該變量存儲在計算機的高速硬件寄存器中,除此之外,其余特性和自動變量完全相同。使用register的目的是為了提高程序的執行速度。程序中最頻繁訪問的變量,可聲明為register。 registerinti; /*等價于registeri;*/ for(i=0;i<=N;i++){ …}不可多必要時使

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論