程式設計(二) 參考書目: 1. 洪維恩,C++ 教學手冊 第三版,旗標出版圖書公司。 2 程式設計(二) 參考書目: 1.洪維恩,C++ 教學手冊 第三版,旗標出版圖書公司。 2. Walter Savitch, Absolute C++ (4th Edition), Pearson Education, Inc., 2010. 3. Paul J. Deitel and Harvey M. Deitel, C++ How to Program, 7rd Edition, Prentice Hall, 2009.(中文版 C++ 程式設計藝術 第五版 全華書局) 2. 從C到C++(I):函數與指標 Chih-Hung Wang
簡單的函數 (1/2) 下面的範例計算6的平方值, 並在運算結果前後列印星號
簡單的函數 (2/2) 下圖說明函數呼叫與返回的方式:
函數原型的宣告、撰寫與呼叫(1/3) 下面為「函數原型」(prototype)的宣告格式 下面的格式為合法的函數宣告格式
函數原型的宣告、撰寫與呼叫(2/3) 自訂函數撰寫的格式如下所示 呼叫函數的方式有兩種 一種是將傳回值指定給某個變數接收 另一種則是直接呼叫函數,不需要傳回值
函數原型的宣告、撰寫與呼叫(3/3) 下面的敘述為常見的函數呼叫 右邊的格式為自訂函數square() 的宣告與呼叫方式 宣告於函數內的變數稱為「區域變數」(local variable)
不使用函數原型的方式 (1/2) 如果不使用函數原型,可採下面的寫法
不使用函數原型的方式 (2/2) 下面的程式是不使用函數原型的方式所撰寫而成的
函數的引數與參數 (1/2) 傳遞給函數的資料稱為函數的「引數」(argument)。 函數所收到的資料稱為「參數」(parameter)。 傳址呼叫,call by address 是指呼叫函數時,所傳遞的資料是某個變數的位址。 傳值呼叫,call by value 將資料的值當做引數來傳遞給函數
函數的引數與參數 (2/2) 下面是傳入兩個引數的例子
函數的傳回值 (1/3) return敘述的格式如下所示 函數的傳回值可以是變數、常數或是運算式 函數沒有傳回值時,可以在函數結束的地方加上分號
函數的傳回值 (2/3) 下面的程式可以利用函數 傳回兩個整數的較大值
函數的傳回值 (3/3) prog6_5是沒有傳回值 的函數之範例
inline函數 (1/3) inline函數是在函數定義前多加一個inline關鍵字
inline函數 (2/3) 下圖說明編譯器如何將inline函數嵌入到原始程式 編譯器可能會忽略inline函數的時機
inline函數 (3/3) 下面是inline函數的使用範例
變數的等級 C++提供auto、static auto、extern、static extern及 register等五種變數等級 宣告變數時,可以一起將變數名稱及其等級同時宣告, 如下面的敘述:
區域變數 (1/3) 區域變數又稱為「自動變數」(automatic variable) 包含區域變數的程式碼區塊,區域變數的值自動消失 區域變數在程式執行時會以堆疊(stack)的方式存放, 屬於動態的變數 下面的宣告皆是屬於區域變數的一種:
區域變數 (2/3) 下圖是區域變數i在所屬區段中的活動範圍之示意圖
區域變數 (3/3) 由下面的程式裡可以 看到區域變數的使用
靜態區域變數 (1/2) 靜態區域變數是在編譯時就已配置固定的記憶體空間 包含靜態區域變數的程式碼區塊執行完後,靜態區域 變數的值不會自動消失 下面的敘述為靜態區域變數的範例
靜態區域變數 (2/2) 由下面的程式裡可以看到 靜態區域變數a的變化 此行僅在初始化時進行
外部變數 (1/4) 外部變數(external variable)是在函數外面所宣告的 變數 外部變數又稱為「總體變數」或「全域變數」(global Variable) 下面的程式片段是外部變數的宣告範例
外部變數 (2/4) 從下圖的內容中可以看到外部變數i的活動範圍
外部變數 (3/4) 下面的程式定義外部變數pi,利用它求取圓周及圓面積
外部變數 (4/4)
Practice 1 今年 2011 年的每個月的天數如下: 請寫一個函數,輸入月份及日期,回傳值為今年的第 幾天。請於主程式中將答案印出。 (使用 Golbal 變數) 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月 31 28 30
靜態外部變數 (1/2) 靜態外部變數只能在一個程式檔內使用 下圖為靜態外部變數i的活動範圍
靜態外部變數 (2/2) 下面的程式可以認識靜態外部 變數的生命週期與活動範圍
暫存器變數 (1/3) 暫存器變數利用CPU的暫存器(register)來存放資料 暫存器變數以register 來宣告
暫存器變數 (2/3) 下面的程式是使用 暫存器變數的範例
暫存器變數 (3/3)
呼叫多個函數 (1/2) 下面的程式碼是在主程式裡呼叫多個函數的例子
Scope Rules Local variables Declared inside body of given function Available only within that function Can have variables with same names declared in different functions Scope is local: "that function is it’s scope" Local variables preferred Maintain individual control over data Need to know basis Functions should declare whatever local data needed to "do their job"
Procedural Abstraction Need to know "what" function does, not "how" it does it! Think "black box" Device you know how to use, but not it’s method of operation Implement functions like black box User of function only needs: declaration Does NOT need function definition Called Information Hiding Hide details of "how" function does it’s job
Global Constants and Global Variables Declared "outside" function body Global to all functions in that file Declared "inside" function body Local to that function Global declarations typical for constants: const double TAXRATE = 0.05; Declare globally so all functions have scope Global variables? Possible, but SELDOM-USED Dangerous: no control over usage!
Blocks Declare data inside compound statement Called a "block" Has "block-scope" Note: all function definitions are blocks! This provides local "function-scope" Loop blocks: for (int ctr=0;ctr<10;ctr++) { sum+=ctr; } Variable ctr has scope in loop body block only
Nested Scope Same name variables declared in multiple blocks Very legal; scope is "block-scope" No ambiguity Each name is distinct within its scope
呼叫多個函數 (2/2)
函數之間的相互呼叫 (1/2) 下面的程式碼是在函數間呼叫其它函數的例子
函數之間的相互呼叫 (2/2)
函數的傳值 (1/2) 下面的程式可用來觀察函 數裡,變數值的變化情形
函數的傳值 (2/2) 以prog7_1為例,將函數傳值呼叫的方式繪製成圖
參照的基本認識 (1/3) C++提供參照(reference)來做為資料的別名 參照的效果與使用指標一樣,都會更動到原本資料 參照與指標的差別,在於參照使用起來與一般資料一 樣,較為直覺,且在宣告的時候就要給定初值 參照的宣告格式如下
參照的基本認識 (2/3) 想為整數變數a使用參照ref,可以做出如下的宣告: 如果想將ref的值設成10,可以寫出下面的敘述:
參照的基本認識 (3/3) 下面的程式碼是參照的使用範例常用的流程圖符號
傳遞參照到函數 (1/4) 下面是將參照當成引數傳入函數的原型宣告 在定義函數時,於變數名稱前加上參照運算子&即可
傳遞參照到函數 (2/4) prog7_3是以參照的 方式傳遞到函數
傳遞參照到函數 (3/4) 下圖是以prog7_3為例,說明參照呼叫的方式
傳遞參照到函數 (4/4) 下面的程式是利用print() 函數,印出欲 列印的字元
傳回值為參照的函數 (1/2) 函數的傳回值也可以是參照 舉例來說,於程式中宣告一名為max的函數,可傳回 兩個整數中較大值之參照,函數原型為: 想將傳回的參照值設為100,即可寫出下面的敘述:
傳回值為參照的函數 (2/2) 下面是函數傳回 參照的使用範例
Practice 2 試利用 void sum(int &, int &) 函數,傳入 a, b 兩個整數 ,於函數中計算 a+b 的值,並將計算結果存入 a 中。a 與 b 的值請自行設定。
Answer for Practice 2 #include <iostream> #include <cstdlib> using namespace std; void sum(int &,int &); int main(void) { int a=6, b=10; sum(a,b); cout << "a=" << a << ", b=" << b << endl; system("pause"); return 0; } void sum(int &x,int &y) x=x+y;
Practice 3 試寫一個函數,將引數 a, b 以大小排列。其函數原形 為 void sort (int &, int &);
Answer for Practice 3 #include <iostream> #include <cstdlib> using namespace std; void sort(int &,int &); int main(void) { int a=6, b=10; cout << "排序前,a=" << a << ", b=" << b << endl; sort(a,b); cout << "排序後,a=" << a << ", b=" << b << endl; system("pause"); return 0; } void sort(int &x,int &y) int temp; if(x<y) temp=x; x=y; y=temp;
多載 (1/5) overloading a Function Name 以一個簡單的例子說明「函數的多載」之使用
多載 (2/5)
多載 (3/5) 如果只有傳回值型態不同,則不能多載 只有傳回值型態不同,則會讓編譯器難以分辨到底該 使用哪一個函數 舉例來說,某個函數的原型如下 這個函數原型會與下面的原型相衝突而產生錯誤 只有傳回值型態不同,則會讓編譯器難以分辨到底該 使用哪一個函數
多載 (4/5) 接下來再看一個引數個數不同的函數多載
多載 (5/5)
Practice 4 (1) 寫一個平均的函數,可輸入二個或三個 double平均 ,回傳值為 double。 (2) 試寫一個 max() 函數的多載,其中 max 的引數型 態為 int,可以有兩個或三個引數,函數回傳值位這些 引數的最大值,傳回型態亦為int。 (3) 寫一個可以計算梯形面積的多載化函數,格式為 trapezoid(upper, base, height),其中此三值可同為int 或 double,傳回值型態為 double。
小測驗(務必寫出) 試寫 power(x,n) 函數的多載,用來計算 x 的 n 次方, n 為 int 型態。當引數 x 型態為 int 時,函數回傳值的 形態為 int;當引數 x 的形態為 double 時,函數回傳 值的形態為 double。
預設引數 (1/4) 未傳入足夠的引數到函數時,預設的引數值就會被使 用,這種方式稱為「預設引數」(default argument) 要設定預設,可在定義原型時,於引數後面設值給它
預設引數 (2/4) 下面的程式是函數引數預設值的使用範例
預設引數 (3/4) 沒有使用預設值的引數,要放置在引數列的左邊 舉例來說,函數原型如下 下面都是合法的func() 函數呼叫 下列的函數呼叫,會造成編譯時期或是邏輯上的錯誤:
預設引數 (4/4) 下面的程式是有加入引 數預設值的函數呼叫
Predefined Functions Libraries full of functions for our use! Two types: Those that return a value Those that do not (void) Must "#include" appropriate library e.g., <cmath>, <cstdlib> (Original "C" libraries) <iostream> (for cout, cin)
Using Predefined Functions Math functions very plentiful Found in library <cmath.h> Most return a value (the "answer") Example: theRoot = sqrt(9.0); Components: sqrt = name of library function theRoot = variable used to assign "answer" to 9.0 = argument or "starting input" for function In I-P-O: I = 9.0 P = "compute the square root" O = 3, which is returned & assigned to theRoot
The Function Call Back to this assignment: theRoot = sqrt(9.0); The expression "sqrt(9.0)" is known as a function call, or function invocation The argument in a function call (9.0) can be a literal, a variable, or an expression The call itself can be part of an expression: bonus = sqrt(sales)/10; A function call is allowed wherever it’s legal to use an expression of the function’s return type
A Larger Example: Display 3 A Larger Example: Display 3.1 A Predefined Function That Returns a Value (1 of 2)
A Larger Example: Display 3 A Larger Example: Display 3.1 A Predefined Function That Returns a Value (2 of 2)
More Predefined Functions #include <cstdlib> Library contains functions like: abs() // Returns absolute value of an int labs() // Returns absolute value of a long int *fabs() // Returns absolute value of a float *fabs() is actually in library <cmath>! Can be confusing Remember: libraries were added after C++ was "born," in incremental phases Refer to appendices/manuals for details
More Math Functions pow(x, y) Returns x to the power y double result, x = 3.0, y = 2.0; result = pow(x, y); cout << result; Here 9.0 is displayed since 3.02.0 = 9.0 Notice this function receives two arguments A function can have any number of arguments, of varying data types
Even More Math Functions: Display 3 Even More Math Functions: Display 3.2 Some Predefined Functions (1 of 2)
Even More Math Functions: Display 3 Even More Math Functions: Display 3.2 Some Predefined Functions (2 of 2)
Predefined Void Functions No returned value Performs an action, but sends no "answer" When called, it’s a statement itself exit(1); // No return value, so not assigned This call terminates program void functions can still have arguments All aspects same as functions that "return a value" They just don’t return a value!
Random Number Generator Return "randomly chosen" number Used for simulations, games rand() Takes no arguments Returns value between 0 & RAND_MAX Scaling Squeezes random number into smaller range rand() % 6 Returns random value between 0 & 5 Shifting rand() % 6 + 1 Shifts range between 1 & 6 (e.g., die roll)
Random Number Seed Pseudorandom numbers Calls to rand() produce given "sequence" of random numbers Use "seed" to alter sequence srand(seed_value); void function Receives one argument, the "seed" Can use any seed value, including system time: srand(time(0)); time() returns system time as numeric value Library <time> contains time() functions
Random Examples Random double between 0.0 & 1.0: (RAND_MAX – rand())/static_cast<double>(RAND_MAX) Type cast used to force double-precision division Random int between 1 & 6: rand() % 6 + 1 "%" is modulus operator (remainder) Random int between 10 & 20: rand() % 10 + 10
使用 random number 的例子 (1) #include <iostream> #include <cstdlib> using namespace std; int main( ) { int month, day; cout << "Welcome to your friendly weather program.\n" << "Enter today's date as two integers for the month and the day:\n"; cin >> month; cin >> day; srand(month*day); int prediction; char ans; cout << "Weather for today:\n";
使用 random number 的例子 (2) do { prediction = rand() % 3; switch (prediction) case 0: cout << "The day will be sunny!!\n"; break; case 1: cout << "The day will be cloudy.\n"; case 2: cout << "The day will be stormy!.\n"; default: cout << "Weather program is not functioning properly.\n"; } cout << "Want the weather for the next day?(y/n): "; cin >> ans; }while (ans == 'y' || ans == 'Y'); cout << "That's it from your 24 hour weather program.\n"; return 0;
什麼是指標 (1/2) 指標(pointer)是用來存放變數在記憶體中的位址 如果指標ptr存放變數a的位址,則
什麼是指標 (2/2) 變數a與指標變數ptr的配置可由下圖來表示 通常編譯器是採「位元組定址法」決定變數的位址:
為什麼要用指標? 更有效率 較複雜的資料結構,需要指標才能將資料鏈結在一起 許多函數必須利用指標來傳達記憶體的訊息
記憶體的位址 (1/2) 下面的程式印出變數的 值、記憶體的大小,與 變數的位址
記憶體的位址 (2/2) 下圖是prog9_1中,變數於記憶體內配置的情形:
指標變數的宣告 (1/2) 指標變數的宣告格式如下所示 下面的敘述為指標變數宣告的範例
指標變數的宣告 (2/2) 把指標ptr指向整數變數num 下面是設定ptr=&num的示意圖 在宣告指標變數時,也可以立即將它指向某個整數
位址運算子 位址運算子「&」可用來取得變數的位址 舉例來說,&num即代表取出num在記憶體中的位址
依址取值運算子 (1/3) 依址取值運算子「*」可取得指標所指向變數的內容 舉例來說,*ptr可取得num的值:
依址取值運算子 (2/3) 下面的範例印出變數的位址與變數值
依址取值運算子 (3/3) 執行完第7行後,記憶體的配置 執行完第9行後,記憶體的配置
指標變數的使用 (1/2) 指標可以重新指向另一個相同型態的變數
指標變數的使用 (2/2) 記憶體的配置情形 執行完第8行 int *ptr; 執行完第13行 ptr=&b; 執行完第10行 ptr=&a;
指標變數的大小 下面的程式是利用sizeof() 求出指標變數所佔的位元組
指標的操作練習 (1/3) 下面是一個簡單的範例,藉以熟悉指標的操作
指標的操作練習 (2/3)
指標的操作練習 (3/3) 下表是變數a、b與指標ptr1、ptr2之值的變化情形
指標變數指向之型態 (1/2) 下面的程式示範錯誤的指標用法
指標變數指向之型態 (2/2) 編譯器會在編譯時發出下面的錯誤訊息 第9、10行的程式應修改成 prog9_6經過修正、編譯後的執行結果如下
傳遞指標到函數 (1/4) 將指標傳入函數裡,可利用如下的語法
傳遞指標到函數 (2/4) 設計address(),可接收指向整數的指標,沒有傳回值: 原型的宣告: 函數的定義: 函數的呼叫: void address(int *); // 宣告函數address()的原型 函數的定義: 函數的呼叫:
傳遞指標到函數 (3/4) 下面是函數address() 的完整範例
傳遞指標到函數 (4/4) prog9_7內,指標ptr與p1均指向同一個變數
傳遞指標的應用 下面的程式是透過位址來 改變呼叫端變數的內容
錯誤的示範 (1/4) 有些運算必須透過指標傳遞才 能達成,下面為錯誤的示範
錯誤的示範 (2/4) 記憶體的配置情形 執行完第8行後,記憶體的配置情形 進入swap() 函數時,記憶體配置的情形
錯誤的示範 (3/4) 執行完第18行後,記憶體配置的情形 執行完第19行後,記憶體配置的情形
錯誤的示範 (4/4) 執行完第20行後,記憶體配置的情形
正確的範例 (1/4) 將程式prog9_9修改成 prog9_10
正確的範例 (2/4) 記憶體的配置情形 執行完第8行後,記憶體配置的情形 進入swap() 函數時,記憶體配置的情形
正確的範例 (3/4) 執行完第18行後,記憶體配置的情形 執行完第19行後,記憶體配置的情形
正確的範例 (4/4) 執行完第20行後,記憶體配置的情形
想一想 將 Practice 3 轉換成指標形式
傳回值為指標的函數 (1/4) 從函數傳回指標的格式
傳回值為指標的函數 (2/4) 下面的範例說明如何 從函數傳回指標
傳回值為指標的函數 (3/4) 記憶體的配置情形 執行完第8行後,記憶體配置的情形 進入max() 函數時,記憶體配置的情形
傳回值為指標的函數 (4/4) 執行完第9行後,記憶體配置的情形
利用指標傳遞一維陣列到函數裡 可接收一維陣列之函數的定義格式如下 例如func() 的定義可撰寫成如下的格式 在呼叫函數func() 時
以指標傳遞一維陣列的範例 (1/2) 下面的範例說明如何以指標傳遞一維陣列
以指標傳遞一維陣列的範例 (2/2)
利用函數傳回指標 (1/2) 下面的程式是示範如何利用函數傳回指標
利用函數傳回指標 (2/2)
綜合範例 Enter 10 numbers: 34 82 49 102 7 94 23 11 50 31 Largest: 102 傳入 10 個數字,存於一陣列中。利用一個函數輸入 兩個指標以及一個陣列,在函數中找出最大及最小值 。 Enter 10 numbers: 34 82 49 102 7 94 23 11 50 31 Largest: 102 Smallest: 7
解答 (1) #include <iostream> #define N 10 using namespace std; #define N 10 using namespace std; void max_min(int a[], int n, int *max, int *min); int main(void) { int b[N], i, big, small; cout <<"Enter ”<< N << “ numbers:”<<endl; for (i = 0; i < N; i++) cin >> b[i];
解答 (2) max_min(b, N, &big, &small); cout <<"Largest: "<<big <<endl; cout <<"Smallest: “ <<small<< endl; return 0; } void max_min(int a[], int n, int *max, int *min) { int i; *max = *min = a[0]; for (i = 1; i < n; i++) { if (a[i] > *max) *max = a[i]; else if (a[i] < *min) *min = a[i];