โปรแกรมที่มีประโยชน์และนำไปงานได้จริงส่วนใหญ่จะมีขนาดใหญ่กว่าตัวอย่างโปรแกรมที่พบเห็นในบทเรียนที่ผ่านมา เพื่อให้การจัดการโปรแกรมสามารถทำได้ง่าย ผู้เขียนโปรแกรมควรแบ่งโปรแกรมเป็นโปรแกรมย่อย (subprograms) ในภาษา C++ เรียกโปรแกรมย่อยเหล่านี้ว่า ฟังก์ชัน (functions) ผู้เขียนโปรแกรมสามารถพัฒนาและทดสอบโปรแกรมย่อยแต่ละโปรแกรมแยกจากกัน แล้วนำโปรแกรมย่อยที่ผ่านการทดสอบแล้วไปใช้ในโปรแกรมอื่น ๆ การจัดการโปรแกรมโดยแบ่งเป็นส่วนๆ (modularization) เป็นคุณลักษณะของการพัฒนาโปรแกรมเชิงวัตถุ (object-oriented software)
คลังโปรแกรมภาษา C++ มาตรฐาน (standard C++ library) เป็นแหล่งรวมฟังก์ชันและส่วนของโปรแกรมที่มีการประกาศและนิยามไว้แล้วในภาษา C++ ผู้เขียนโปรแกรมสามารถเรียกใช้งานสร้างฟังก์ชันและส่วนของโปรแกรมเหล่านี้ผ่านการอ้างอิงถึงไฟล์ส่วนหัว (header files) ในโปรแกรมตัวอย่างที่ผ่านมา มีหลายโปรแกรมที่มีการใช้งานฟังก์ชัน เช่น ในตัวอย่าง 2.3 มีการใช้งานค่าคงที่ INT_MAX ซึ่งนิยามไว้ในไฟล์ ส่วนหัว <climits> ตัวอย่าง 2.17 ใช้ฟังก์ชันชื่อ sqrt ( ) ซึ่งนิยามไว้ในไฟล์ส่วนหัว <cmath> ตัวอย่าง 4.26 ใช้ฟังก์ชันชื่อ rand ( ) ซึ่งนิยามไว้ในไฟล์ส่วนหัว <cstdlib> และ ตัวอย่าง 4.28 ใช้ฟังก์ชันชื่อ time ( ) ซึ่งนิยามไว้ในไฟล์ส่วนหัว <ctime>
การทำงานของฟังก์ชันมีลักษณะคล้าย ๆ กับกล่องดำ ซึ่งคนภายนอกไม่ต้องรู้ว่ามีอะไรอยู่ในกล่องดำ ในทำนองเดียวกัน ผู้เขียนโปรแกรมสามารถเรียกใช้ฟังก์ชัน โดยไม่จำเป็นต้องทราบว่าในฟังก์ชันนั้นมีคำสั่งอะไรบ้าง เพียงแต่รู้ว่า เมื่อใส่ค่าให้ฟังก์ชันจะได้ผลลัพธ์อะไรกลับมา เช่น ฟังก์ชันรากที่สอง sqrt ( ) จะรับค่าเลขจำนวนบวกเป็นแล้วคำนวณหารากที่สองของเลขจำนวนดังกล่าวเป็นผลลัพธ์ ถ้าใส่เลขจำนวนบวก 9 ให้ฟังก์ชัน sqrt ( ) จะได้เลขจำนวน 3 กลับมา และ ถ้าเปลี่ยนมาใส่เลขจำนวนบวก 2 เข้าไปในฟังก์ชัน sqrt ( ) จะได้เลขจำนวน 1.41421 เป็นผลลัพธ์กลับมา ตัวอย่างต่อไปจะแสดงการใช้งานฟังก์ชันทางคณิตศาสตร์ sqrt ( ) ที่นิยามไว้ในไฟล์ส่วนหัว <cmath>
ตัวอย่างที่ 5.1 ฟังก์ชันรากที่สอง (square root) sqrt ( )
#include <cmath> // define the sqrt() function #include <iostream> // define the cout object using namespace std; int main() // tests the sqrt() function { for (int x = 0;x < 5;x++) cout << "\t" << x << "\t" << sqrt(x) << endl; }
ทดสอบโปรแกรม
0 0 1 1 2 1.41421 3 1.73205 4 2 5 2.23607
โปรแกรมจะแสดงค่ารากที่สองของเลขจำนวนบวกตั้งแต่เลข 0 ถึง 5 โดยในแต่ละครั้งที่ฟังก์ชัน sqrt(x) ถูกประมวลผลในคำสั่ง for จะมีการกระโดดไปทำงานในส่วนคำสั่งของฟังก์ชัน sqrt ( ) ที่นิยามไว้ในคลังโปรแกรมภาษา C++ มาตรฐาน เมื่อการทำงานในฟังก์ชัน sqrt ( ) สิ้นสุด จะได้ผลลัพธ์ของฟังก์ชันมาแทนที่นิพจน์ sqrt(x) ในคำสั่ง for
ให้สังเกตว่า ในบรรทัดแรกของโปรแกรมตัวอย่างจำเป็นต้องมีคำสั่งประกาศ #include <cmath> เพื่อที่จะแจ้งให้โปรแกรมแปลโปรแกรมภาษา C++ รู้ว่า จะหานิยามของฟังก์ชัน sqrt ( ) ได้จากไหน ซึ่งจากตัวอย่าง โปรแกรมแปลโปรแกรมจะไปค้นหาฟังก์ชันในไฟล์ส่วนหัว <cmath>
ผู้เขียนโปรแกรมสามารถใช้งานฟังก์ชัน เช่น sqrt ( ) เหมือนกับเป็นตัวแปรตัวหนึ่งภายในคำสั่ง เช่น
y = sqrt(x);
ในทางคอมพิวเตอร์ การใช้งานฟังก์ชัน เรียกว่า การปลุกฟังก์ชัน (invoking the function) หรือ การเรียกฟังก์ชัน (calling the function) ในตัวอย่าง 5.1 รหัสคำสั่ง sqrt(x) จะไปเรียกฟังก์ชัน sqrt ( ) ค่านิพจน์ x ในเครื่องหมายวงเล็บเรียกว่า อาร์กิวเมนต์(argument) หรือ พารามิเตอร์จริง (actual parameter) ของการเรียกฟังก์ชัน โดยนิพจน์ x จะถูกส่งผ่านเพื่อไปทำงานในฟังก์ชันแบบส่งผ่านโดยค่า (passed by value) ดังนั้น หาก x มีค่าเท่ากับ 3 ค่า 3 จะถูกส่งไปยังฟังก์ชัน sqrt ( ) ในการเรียกใช้ sqrt(x)
กระบวนการเรียกใช้งานฟังก์ชัน sqrt ( ) ของคำสั่งข้างต้น สมมติว่า ตัวแปร x และ y ถูกประกาศในฟังก์ชัน main ( ) ค่าของ x (3) จะถูกส่งไปยังฟังก์ชัน sqrt ( ) ซึ่งจะคำนวณและได้ผลลัพธ์เป็น 1.73205 กลับมายังฟังก์ชัน main ( ) แล้วจึงนำไปเก็บในตัวแปร y
ตัวอย่างที่ 5.2 ทดสอบฟังก์ชันตรีโกณมิติ
int main() { // tests the identity sin 2x = 2 sin x cos x: for (float x = 0;x < 2;x += 0.2) cout << x << "\t\t" << sin(2*x) << "\t\t" << 2*sin(x)*cos(x) << endl; }
ทดสอบโปรแกรม
0 0 0 0.2 0.389418 0.389418 0.4 0.717356 0.717356 0.6 0.932039 0.932039 0.8 0.999574 0.999574 1 0.909297 0.909297 1.2 0.675463 0.675463 1.4 0.334988 0.334988 1.6 -0.0583744 -0.0583744 1.8 -0.442521 -0.442521
โปรแกรมตัวอย่างเรียกใช้ฟังก์ชันที่นิยามในไฟล์ส่วนหัว <cmath> โดยมีจุดประสงค์เพื่อแสดงว่าค่าของ sin 2x จะมีค่าเท่ากับ 2 sin x cos x โปรแกรมแสดงค่าของ x ในสดมภ์แรก ค่าของ sin 2x ในสดมภ์ที่ สอง และ ค่าของ 2 sin x cos x ในสดมภ์สุดท้าย
จากตัวอย่างที่ผ่านมา จะเห็นว่า การเขียนคำสั่งเรียกใช้ฟังก์ชันในนิพจน์ จะเปรียบเสมือนกับการใช้ค่าตัวแปรตัวหนึ่งในนิพจน์ เช่น
y = sqrt(2); cout << 2*sin(x)*cos(x);
นอกจากนี้ ผู้เขียนโปรแกรมยังสามารถเขียนฟังก์ชันซ้อนในฟังก์ชันได้ เช่น
y = sqrt(1+2*sqrt(3+4*sqrt(5)));
ฟังก์ชันทางคณิตศาสตร์ส่วนใหญ่ที่ใช้ในเครื่องคิดเลข มีการนิยามในไฟล์ส่วนหัว <cmath> ทั้งสิ้น รวมทั้งฟังก์ชันที่แสดงดังตาราง 5-1
ตาราง 5-1 ตัวอย่างฟังก์ชันในไฟล์ส่วนหัว <cmath>
ฟังก์ชัน | คำอธิบาย | ตัวอย่างการใช้งาน |
acos (x) | ค่าผกผันโคซายน์ของ x (หน่วยเรเดียน) | acos(0.2) returns 1.36944 |
asin (x) | ค่าผกผันซายน์ของ x (หน่วยเรเดียน) | asin(0.2) returns 0.201358 |
atan (x) | ค่าผกผันแทนเจนของ x (หน่วยเรเดียน) | atan(0.2) returns 0.197396 |
ceil (x) | ค่าเลขเต็มจำนวนบนของ x (ปัดเศษขึ้น) | ceil(3.141593) return 4.0 |
cos (x) | ค่าโคซายน์ของ x (หน่วยเรเดียน) | cos(2) returns -0.416147 |
exp (x) | ค่า ex | exp(2) returns 7.38906 |
fabs (x) | ค่าสัมบูรณ์จำนวนจริงของ x | fabs(-2) returns 2.0 |
floor (x) | ค่าเลขเต็มจำนวนล่างของ x (ตัดเศษขึ้น) | floor(3.141593) returns 3.0 |
log (x) | ค่า ln | log(2) returns 0.693147 |
log10 (x) | ค่า log10 | log10(2) returns 0.30103 |
pow (x,p) | ค่า xp | pow(2,3) returns 8.0 |
sin (x) | ค่าซายน์ของ x (หน่วยเรเดียน) | sin(2) returns 0.909297 |
sqrt (x) | ค่ารากที่สองของ x | sqrt(2) returns 1.41421 |
tan (x) | ค่าแทนเจนของ x (หน่วยเรเดียน) | tan(2) returns -2.18504 |
ให้สังเกตว่าฟังก์ชันที่บรรจุในไฟล์ส่วนหัว <cmath> จะคืนค่าข้อมูลชนิด double เสมอ ดังนั้น ถ้าหากเราป้อนข้อมูลชนิด int เข้าไป ข้อมูลจะถูกเปลี่ยนเป็นชนิด double ก่อนจะนำไปใช้งานในฟังก์ชันตาราง 5-2 แสดงตัวอย่างไฟล์ส่วนหัวที่มีในคลังโปรแกรมภาษา C++ มาตรฐาน
ตาราง 5-2 ตัวอย่างไฟล์ส่วนหัวที่มีในคลังโปรแกรมภาษา C++ มาตรฐาน
ชื่อไฟล์ส่วนหัว | คำอธิบาย |
<casssert> | นิยามฟังก์ชัน assert ( ) |
<ctype> | นิยามฟังก์ชันทดสอบอักขระ |
<cfloat> | นิยามค่าคงที่ที่เกี่ยวกับข้อมูลชนอด float |
<climits> | นิยามพิสัยข้อมูลชนิดจำนวนเต็มของเครื่องคอมพิวตอร์นั้น ๆ |
<cmath> | นิยามฟังก์ชันทางคณิตศาสตร์ |
<cstdio> | นิยามฟังก์ชันการนำเข้าและส่งออกข้อมูลมาตรฐาน |
<cstdlib> | นิยามฟังก์ชันอรรถประโยชน์ |
<cstring> | นิยามฟังก์ชันประมวลผลสายอักขระ |
<ctime> | นิยามฟังก์ชันวันที่ และ เวลา |
ฟังก์ชันในคลังโปรแกรมภาษา C++ มาตรฐาน พัฒนามาจากคลังโปรแกรมภาษา C มาตรฐาน ในภาษา C++ ผู้เขียนโปรแกรมสามารถเรียกใช้งานฟังก์ชันในคลังโปรแกรมภาษา C มาตรฐานได้ โดยเขียนเป็นคำสั่งประกาศเช่นเดียวกับการเรียกใช้ไฟล์ส่วนหัว <iostream> ดังตัวอย่างที่ผ่านมา เช่น ถ้าหากว่าผู้เขียนโปรแกรมต้องการเรียกใช้ฟังก์ชันตัวเลขสุ่ม ที่อยู่ในไฟล์ส่วนหัว <cstdlib> จะต้องเขียนส่วนประกาศไว้ในส่วนคำสั่งประกาศตัวประมวลผลก่อน ดังนี้
#include <cstdlib>
แม้ว่า คลังโปรแกรมภาษา C++ มาตรฐานจะมีมากมาย แต่ก็ยังไม่เพียงพอสำหรับงานเขียนโปรแกรมโดยทั่วไป ดังนั้น นักพัฒนาโปรแกรมจึงจำเป็นจะต้องสร้างฟังก์ชันขึ้นมาเพื่อใช้งานในโปรแกรม ตัวอย่าง 5.3 แสดงให้เห็นการสร้างฟังก์ชัน cube ( ) ที่ผู้เขียนโปรแกรมเป็นผู้นิยามรูปแบบขึ้นมาเอง
ตัวอย่างที่ 5.3 ฟังก์ชัน ยกกำลัง 3 cube ( )
int cube (int x) { // return cube of x return x*x*x; }
ฟังก์ชันตัวอย่างจะส่งคืนค่ากำลัง 3 ของเลขจำนวนเต็มที่เข้าไป ดังนั้น cube(2) จะได้ค่า 8 คืนกลับมาจากฟังก์ชัน
ฟังก์ชันที่ผู้เขียนโปรแกรมนิยามในภาษา C++ ประกอบด้วยส่วน 2 ส่วน คือ ส่วนหัว (head) และ ส่วนโครงร่าง (body) รูปแบบการเขียนส่วนหัวในฟังก์ชัน มีดังนี้
return-type name (parameter-list)
โดยที่ return-type คือ ชนิดของข้อมูลที่จะส่งค่ากลับจากฟังก์ชัน (ชนิดข้อมูลที่เก็บในชื่อฟังก์ชัน) name คือ ชื่อของฟังก์ชันตามทีผู้เขียนโปรแกรมกำหนด และ parameter-list คือ ชุดของพารามิเตอร์ที่ต้องการให้มีการรับและส่งค่าระหว่างฟังก์ชัน จากตัวอย่างที่ 5.3 return-type เป็นชนิด int ฟังก์ชันมีชื่อว่า cube รับ พารามิเตอร์ข้อมูล int ชื่อ x ดังนั้น ส่วนหัวของฟังก์ชันตัวอย่าง คือ
int cube(int x)
ส่วนโครงร่างของฟังก์ชัน คือ กลุ่มชุดคำสั่งที่อยู่ต่อจากส่วนหัวของฟังก์ชัน เป็นส่วนที่ประกอบด้วยชุดคำสั่งที่ต้องการให้เกิดการทำงาน เมื่อมีการเรียกฟังก์ชัน ซึ่งรวมถึงคำสั่ง return ที่ใช้ในการส่งค่าคืนค่าไปยังคำสั่งที่เรียกใช้ฟังก์ชัน ดังนั้น ส่วนโครงร่างในฟังก์ชันตัวอย่าง คือ
{ // return cube of x return x*x*x; }
จะเห็นว่า ส่วน main ( ) ในโปรแกรม ก็คือ ฟังก์ชันนั่นเอง โดยที่ ฟังก์ชัน main ( ) จะส่งค่าจำนวนเต็มกลับคืน และ ไม่มีการส่งค่าใดๆ มาทำงานในฟังก์ชัน main ( ) ในขณะที่เริ่มต้นการประมวลผลโปรแกรม ส่วนหัวของฟังก์ชัน main ( ) คือ
int main()
และ ส่วนโครงร่างของฟังก์ชัน main ( ) ก็คือ ตัวโปรแกรมทั้งหมด
คำสั่ง return ของฟังก์ชันมีจุดประสงค์ประสงค์เพื่อจบการทำงานของฟังก์ชัน และ เก็บค่าในชื่อฟังก์ชันเพื่อส่งคืนค่าดังกล่าวกลับไปยังคำสั่งที่เรียกใช้มัน โดยมีรูปแบบการเขียน ดังนี้
return expression;
โดยที่ expression จะเป็นนิพจน์ใดๆ ที่มีค่าชนิดเดียวกับชนิดข้อมูลที่ส่งออกจากฟังก์ชัน
เมื่อใดก็ตาม ที่ผู้เขียนโปรแกรมนิยามฟังก์ชันขึ้นมาเพื่อใช้งาน ผู้เขียนโปรแกรมต้องทำการทดสอบฟังก์ชันก่อน ด้วยการเขียนส่วนโปรแกรมที่ไม่ซับซ้อนให้ทำการเรียกใช้ฟังก์ชันที่พัฒนาขึ้น ส่วนของโปรแกรมดังกล่าว เรียกว่า โปรแกรมตัวขับการทดสอบ (test driver) โดยมีจุดประสงค์เพื่อทดสอบฟังก์ชันก่อนนำไปใช้งานจริง โปรแกรมตัวขับการทดสอบเป็นโปรแกรมชั่วคราวที่ควรจะทำงานได้เร็ว โดยผู้เขียนโปรแกรมไม่ต้องคำนึงถึงรายละเอียดต่างๆ มากนัก โปรแกรมดังกล่าวจะถูกลบทิ้งไปหลังจากที่ฟังก์ชันผ่านการทดสอบแล้ว
ตัวอย่างที่ 5.4 การทำ test driver สำหรับ cube ( )
int cube(int x) { // returns cube of x: return x*x*x; } int main() { //tests the cube() function int n = 1; while (n != 0) { cin >> n; cout << "\tcube(" << n << ") = " << cube(n) << endl; } return 0; }
ทดสอบโปรแกรม เมื่อป้อนค่า n = 5, -6 และ 0 ตามลำดับ
5 cube(5) = 125 -6 cube(-6) = -216 0 cube(0) = 0
โปรแกรมนี้จะวนรอบเพื่อรับค่าที่ผู้ใช้ป้อน แล้วพิมพ์ค่ากำลัง 3 ของค่าดังกล่าวทางจอภาพ จนกว่าผู้ใช้โปรแกรมจะป้อนค่าจำนวนเต็ม 0 ค่าจำนวนเต็มแต่ละตัวจะถูกส่งไปเป็นพารามิเตอร์ให้กับฟังก์ชัน cube( ) ในการเรียก cube(n) และ ค่าของ cube(n) ที่ได้กลับมา จะถูกนำไปแสดงผลตามวัตถุ cout
ฟังก์ชัน main( ) ส่ง 5 ให้กับฟังก์ชัน cube( ) และ ได้รับค่า 125 กลับมาจากฟังก์ชัน cube( ) โดยมีการส่งค่าอาร์กิวเมนต์ n ในฟังก์ชัน main( ) ผ่านไปยังพารามิเตอร์ x ในฟังก์ชัน cube( ) นั่นหมายความว่า x ถูกกำหนด ให้มีค่าเท่ากับ n เมื่อฟังก์ชัน cube( ) ถูกเรียกใช้ การที่ผู้เขียนโปรแกรมนิยามฟังก์ชัน cube( ) ไว้ก่อนการเขียนฟังก์ชัน main( ) เพื่อให้โปรแกรมแปลโปรแกรมภาษา C++ รู้จักฟังก์ชัน cube( ) ก่อนที่จะมีการเรียกใช้งานในฟังก์ชัน main( )
ตัวอย่างต่อไปจะเป็นการสร้างฟังก์ชัน max( ) ซึ่งเป็นฟังก์ชันที่ผู้เขียนโปรแกรมสร้างขึ้น เพื่อรับเลขจำนวนเต็ม 2 จำนวน เปรียบเทียบ และส่งค่าที่มากที่สุดคืนออกมา ฟังก์ชันนี้มีการใช้งานอาร์กิวเมนต์ 2 ตัว
ตัวอย่างที่ 5.5 การทดสอบฟังก์ชัน max()
#include <iostream.h> int max(int x, int y) { // returns larger of the two given integers: if (x < y) return y; else return x; } int main() { // tests the max() function int m, n; do { cin >> m >> n; cout << "\tmax(" << m << "," << n << ") = " << max(m,n) << endl; } while (m != 0); return 0; }
ทดสอบโปรแกรม เมื่อป้อนค่า n = (5, 8), (4, -3) และ (0, 0) ตามลำดับ
5 8 max(5, 8) = 8 4 -3 max(4, -3) = 4 0 0 max(0, 0) = 0
ให้สังเกตว่าฟังก์ชันนี้มีคำสั่ง return 2 คำสั่ง จากตัวอย่าง ฟังก์ชันจะสิ้นสุดการทำงานเมื่อประมวลผลไปถึงคำสั่ง return ตัวใดตัวหนึ่ง โดยทั้งสองคำสั่งจะส่งค่าที่แตกต่างกันคืนกลับไปยังฟังก์ชัน main ( )
คำสั่ง return มีผลการทำงานคล้ายกับคำสั่ง break ในบทที่ผ่านมา แต่คำสั่ง return จะทำให้การประมวลผลของฟังก์ชันสิ้นสุด แล้วโดดไปทำงานในคำสั่งที่มีเรียกใช้ฟังก์ชัน โดยส่วนใหญ่คำสั่ง return จะอยู่เป็นคำสั่งสุดท้ายในฟังก์ชัน แต่บางกรณี ผู้เขียนโปรแกรมอาจเขียนคำสั่ง return ไว้ก่อนหน้า เพื่อให้การประมวลผลฟังก์ชันจบลงก่อนกำหนด โดยไม่จำเป็นต้องทำงานในส่วนคำสั่งที่เหลือในฟังก์ชันนั้น
ในตัวอย่างผ่านมา จะเห็นว่าการนิยามฟังก์ชัน (function definition) เขียนไว้หน้าฟังก์ชัน main ( ) ซึ่งเป็นวิธีการง่ายๆ สำหรับการเขียนโปรแกรมส่วนทดสอบฟังก์ชัน แต่การพัฒนาโปรแกรมในชีวิตจริง เรานิยมเขียนส่วนหัวของฟังก์ชัน (function header) ประกาศไว้ก่อนหน้าฟังก์ชัน main ( ) จากนั้น แล้วเขียนส่วนนิยามฟังก์ชันไว้หลังฟังก์ชัน main ( ) เพื่อให้โปรแกรมง่ายแก่การศึกษา
ส่วนประกาศฟังก์ชัน (function declaration) หรือ ต้นแบบฟังก์ชัน (function prototype) คือ ส่วนหัวของฟังก์ชันที่ตามด้วยเครื่องหมาย ; ในขณะที่ส่วนนิยามฟังก์ชัน ประกอบด้วย ส่วนหัวของฟังก์ชัน และ ส่วนโครงร่างของฟังก์ชัน เมื่อผู้เขียนโปรแกรมเขียนส่วนประกาศฟังก์ชันไว้ก่อนฟังก์ชัน main ( ) โปรแกรมแปลโปรแกรมจะสามารถเรียนรู้เกี่ยวกับฟังก์ชันว่า ฟังก์ชันชื่ออะไร รับพารามิเตอร์กี่ตัว อะไรบ้าง และ ส่งคืนข้อมูลชนิดอะไร โดยไม่จำเป็นต้องทราบว่าฟังก์ชันทำงานอย่างไร ด้วยเหตุนี้ ผู้เขียนโปรแกรมก็สามารถวางส่วนตัวนิยามฟังก์ชัน ไว้ที่ใดก็ได้หลังจากฟังก์ชัน main ( ) หรือ แม้แต่เขียนไว้ในไฟล์อื่น
ตัวแปรที่ประกาศอยู่ในส่วนหัวของฟังก์ชัน เรียกว่า พารามิเตอร์ (parameters) เช่น ตัวแปร x และ y ในตัวอย่าง 5.5 ตัวแปรเหล่านี้จัดเป็นตัวแปรท้องถิ่น (local variables) ของฟังก์ชัน การสำรองเนื้อที่สำหรับตัวแปรพารามิเตอร์ จะกระทำเฉพาะในช่วงการประมวลผลฟังก์ชันเท่านั้น
ตัวแปรที่เขียนอยู่ในส่วนการเรียกฟังก์ชัน เรียกว่า อาร์กิวเมนต์ (arguments) เช่น ตัวแปร m และ n ในตัวอย่าง 5.5 โดยที่ อาร์กิวเมนต์จะถูกส่งผ่านไปยังฟังก์ชันในรูปแบบส่งผ่านโดยค่า (passed by value) หมายความว่า ค่าของอากิวเมนต์จะถูกส่งไปกำหนดให้เป็นค่าเริ่มต้นของพารามิเตอร์ ในตัวอย่าง 5.5 ค่าของ m และ n จะถูกกำหนดให้เป็นค่าเริ่มต้นของ x และ y ตามลำดับ ก่อนการประมวลผลฟังก์ชัน max ( )
ตัวอย่างที่ 5.6 ฟังก์ชัน max ( ) ที่มีการประกาศฟังก์ชันแยกจากการนิยามฟังก์ชัน
int max(int, int); // returns larger of the two given integers: int main() { // test the max() function: int m, n; do { cin >> m >> n; cout << "\tmax(" << m << "," << n << ") = " << max(m,n) << endl; } while (m != 0); return 0; } int max(int x, int y) { if (x < y) return y; else return x; }
โปรแกรมนี้มีผลการทำงานเช่นเดียวกับโปรแกรมตัวอย่าง 5.5 เพียงแต่ โปรแกรมนี้เขียนส่วนประกาศฟังก์ชัน แยกจากส่วนนิยามฟังก์ชัน ให้สังเกตว่า ในส่วนหัวของการกำหนดฟังก์ชันจำเป็นต้องประกาศชื่อของพารามิเตอร์ เช่น x และ y ในขณะที่ ในส่วนของการประกาศฟังก์ชัน ผู้เขียนโปรแกรมไม่จำเป็นต้องระบุชื่อของพารามิเตอร์ที่ใช้งานในฟังก์ชัน
การประกาศฟังก์ชันมีความคล้ายคลึงกับการประกาศตัวแปร โดยเฉพาะฟังก์ชันที่ไม่มีการระบุพารามิเตอร์ เช่น
int length ();
จะคล้ายกับการประกาศตัวแปร คือ
int length;
จุดที่แตกต่างกันระหว่างการประกาศฟังก์ชันและการประกาศตัวแปร คือ เครื่องหมายวงเล็บ( )
ในการพัฒนาฟังก์ชัน ผู้เขียนโปรแกรมนิยมเขียนนิยามฟังก์ชันเป็นไฟล์แยกต่างหาก โดยฟังก์ชันจะถูกแปลเพื่อสร้างเป็นโปรแกรมภาษาเครื่อง (object program) ในลักษณะที่เป็นโมดูล (module) เช่นเดียวกับ ฟังก์ชันที่ถูกนิยามไว้ในคลังโปรแกรมภาษา C++ มาตรฐาน เหตุผลหนึ่งในการพัฒนาฟังก์ชันเป็นโมดูลก็เพื่อจุด ประสงค์ในการหลบซ่อนข้อมูล (information hiding) ข้อมูลนั้น ผู้เขียนโปรแกรมสามารถเรียกใช้ฟังก์ชัน โดยไม่จำเป็นต้องรู้หรือเข้าใจส่วนคำสั่งในฟังก์ชันที่ถูกซ่อนไว้ จากประสบการณ์ที่ผ่านมาพบว่า การซ่อนข้อมูลจะช่วยทำให้การพัฒนาซอฟต์แวร์ขนาดใหญ่ประสบความสำเร็จ เนื่องจาก นักพัฒนาโปรแกรมแต่ละคนไม่ต้องสนใจข้อมูลรายละเอียดที่ไม่เกี่ยวข้องกับ ส่วนที่ตนเองได้รับมอบหมายในการพัฒนาโปรแกรม
ตัวอย่างที่ 5.7 การแปลโปรแกรมฟังก์ชัน max() แบบแยกส่วน
ไฟล์ test_max.cpp:
int max(int, int); // returns larger of the two given integers: int main() { // tests the main function int m, n; do { cin >> m >> n; cout << "\tmax(" << m << "," << n << ") = " << max(m,n) <<< endl; } while (m != 0); return 0; }
ไฟล์ max.cpp:
int max(int x, int y) { if (x < y) return y; else return x; }
คำสั่งที่ใช้ในการแปลโปรแกรมจะแตกต่างกันขึ้นกับระบบปฏิบัติการที่ใช้ เช่น บนระบบปฏิบัติการ UNIX จะใช้คำสั่งรูปแบบ ดังต่อไปนี้
$ c++ -c max.cpp $ c++ -c test_max.cpp $ c++ -o test_max.exe test_max.obj max.obj $ test_max
–c เป็นทางเลือกในการแปลโปรแกรม โดยกำหนดให้สร้างเป็นโปรแกรมภาษาเครื่อง เช่น คำสั่ง c++ –c max.cpp จะทำการสร้างไฟล์ max.obj โดยอัตโนมัติ ซึ่งเป็นไฟล์ที่เก็บรหัสคำสั่งภาษาเครื่องของโปรแกรม max.cpp ไฟล์ max.obj และ test_max.obj จะถูกเชื่อม (link) เข้าเป็นโปรแกรมเดียวกัน เพื่อใช้ในการประมวลผล ตามคำสั่ง c++ text_max.exe test_max.obj max.obj ซึ่งจะทำให้ได้ไฟล์ชื่อ test_max.exe ที่สามารถนำไปประมวลผลได้
ข้อดีของการแปลโปรแกรมแบบแยก คือ ทำให้มีการตรวจสอบฟังก์ชันแบบแยก ซึ่งจะช่วยทำให้ผู้พัฒนาโปรแกรมทราบได้รวดเร็วขึ้นว่า มีข้อผิดพลาดอยู่ในฟังก์ชันหรือไม่ ผู้พัฒนาโปรแกรมสามารถใช้ไฟล์ของฟังก์ชันที่ผ่านการทดสอบว่าถูกต้องเรียบร้อยกับโปรแกรมอื่นได้ นอกจากนี้ ถ้าหากพบภายหลังว่ามีฟังก์ชันใหม่ที่ถูกพัฒนาขึ้น และมีการทำงานที่มีประสิทธิภาพมากขึ้น ผู้พัฒนาโปรแกรมก็สามารถปรับปรุงโปรแกรมได้ง่าย โดยเปลี่ยนเพียงไฟล์เดียว (ไฟล์ของฟังก์ชันใหม่) โดยไม่ต้องทำการเปลี่ยนแปลงทั้งโปรแกรม เช่น ถ้าหากพบว่ามีฟังก์ชัน max ที่ดีกว่าฟังก์ชันที่ใช้อยู่ในปัจจุบัน ก็สามารถเปลี่ยนเฉพาะไฟล์ max.cpp ผู้พัฒนาโปรแกรมเพียงแต่เชื่อมโปแกรม test_max.obj เข้ากับฟังก์ชันตัวใหม่ ก็จะได้โปรแกรมที่มีประสิทธิภาพมากขึ้นไปใช้งาน
ตัวแปรท้องถิ่น (local variables) คือ ตัวแปรที่ประกาศอยู่ในบล็อกคำสั่ง โดยที่ ตัวแปรท้องถิ่นสามารถเข้าถึงได้เฉพาะคำสั่งในบล็อกที่ทำการประกาศเท่านั้น เนื่องจาก ส่วนโครงร่างของฟังก์ชัน คือ ส่วนของบล็อกคำสั่ง ฉะนั้น ตัวแปรที่ถูกประกาศในฟังก์ชันจะถือว่าเป็นตัวแปรท้องถิ่นในฟังก์ชัน ตัวแปรเหล่านี้จะถูกใช้ได้เฉพาะตอนที่ฟังก์ชันกำลังทำงานเท่านั้น พารามิเตอร์ของฟังก์ชันจัดว่าเป็นตัวแปรท้องถิ่นของฟังก์ชันนั้น
ตัวอย่างที่ 5.8 ฟังก์ชันแฟคทอเรียล
ฟังก์ชันแฟคทอเรียล นิยามโดย การคูณจำนวนเต็มบวก n กับ จำนวนเต็มบวกที่น้อยกว่า n ตัวต่อ ๆ ไป จนกระทั่งถึง จำนวนเต็มที่มีค่าเท่ากับ 1
n! = (n)*(n-1)*(n-2)*….*(3)*(2)*(1)
เช่น 5! = (5)*(4)*(3)*(2)*(1) = 120
ฟังก์ชันแฟคทอเรียล
long fact(int n) { // returns n! = (n)*(n-1)*(n-2)*...*(3)*(2)*1 if (n < 0) return 0; int f = 1; while ((n > 1) f *= n--; return f; }
ในฟังก์ชันนี้มีตัวแปรท้องถิ่น 2 ตัว คือ ตัวแปร f และ n โดยที่ n เป็นพารามิเตอร์ ดังนั้น n จึงเป็นตัวแปรท้องถิ่น ในขณะที่ f เป็นตัวแปรที่ถูกประกาศในบล็อกคำสั่งของฟังก์ชัน เพราะฉะนั้น f ก็เป็นตัวแปรท้องถิ่นเช่นกัน
โปรแกรมทดสอบการทำงานของฟังก์ชัน fact ( )
long fact(int); // returns n! = n*(n-1)*(n-2)* ... *(2)*(1) int main() { // tests the factorial() function: for (int i = -1; i < 6 ; i++ ) cout << " " << fact(i); cout << endl; return 0; }
ทดสอบโปรแกรม เมื่อป้อนค่า n = (5, 8), (4, -3) และ (0, 0) ตามลำดับ
0 1 1 2 6 24 120
ตัวอย่างที่ 5.9 ฟังก์ชันสำหรับการสับเปลี่ยน (The Permutation Function)
การสับเปลี่ยน (permutation) คือ การจัดเรียงสมาชิกของเซ็ตจำกัด (finite set) โดยที่ ฟังก์ชัน การสับเปลี่ยน P(n,k) จะให้ค่าจำนวนการจัดเรียงตัวเลข k ตัว ในแบบต่าง ๆ จากตัวเลข n ตัว วิธีหนึ่งในการคำนวณฟังก์ชันการสับเปลี่ยน คือ
$$P(n, k) = n! / (n-k)!$$
เช่น
P(5, 2) = 5! / (5 – 2)! = 5! / 3! = 120 / 6 = 20
หมายความว่า มีการจัดเรียงอยู่ 20 วิธี สำหรับตัวเลขหรือตัวอักษร 2 ตัว จากตัวเลขหรือตัวอักษรทั้งหมด 5 ตัว เช่น ถ้าเรามีอยู่ 5 ตัวอักษร {A,B,C,D,E} ก็จะมีวิธีการจัดเรียงอยู่ทั้งสิ้น 20 วิธี ดังนี้
AB, AC, AD, AE, BC, BD, BE, CD, CE, DE, BA, CA, DA, EA, CB, DB, EB, DC, EC, ED
ฟังก์ชันการสับเปลี่ยน
long fact(int); long perm(int n, int k) { //returns P(n,k) the number of permutations of k from n if (n < 0|| k < 0 || k > n) return 0; else return fact(n)/fact(n-k); }
ให้สังเกตว่า นิพจน์ (n < 0 || k < 0 || k > n) มีไว้เพื่อตรวจสอบพารามิเตอร์ ที่มีค่าอยู่ในช่วงที่ไม่ถูกต้อง หรือ อยู่นอกพิสัยข้อมูล (out of range) ในกรณีนี้ ฟังก์ชันจะส่ง ค่า 0 คืนมา เพื่อให้โปรแกรมทราบว่ามีข้อผิดพลาด
ตัวอย่างโปรแกรมสำหรับทดสอบการทำงานของฟังก์ชัน perm ( )
long perm(int, int); // returns P(n,k), the number of permutations of k from n int main() { // tests the perm() function for (int i = -1; i < 8; i++) { for (int j = -1; j <= i+1;j++) cout << " " << perm(i,j); cout << endl; } return 0; }
หมายเหตุ โปรแกรมทดสอบฟังก์ชัน ตรวจสอบการทำงานของฟังก์ชัน perm ( ) โดยส่งค่า i = -1, j = -1 และ j > i ค่าของตัวแปร i และ j ที่กำหนด เป็นค่าขอบเขต (boundary value) ของการหาค่าฟังก์ชันการสับเปลี่ยน จากตัวอย่าง ฟังก์ชัน perm ( ) จะส่งค่าที่เป็น 0 คืนมา ทดสอบโปรแกรม
0 0 0 1 0 0 1 1 0 0 1 2 2 0 0 1 3 6 6 0 0 1 4 12 24 24 0 0 1 5 20 60 120 120 0 0 1 6 30 120 360 720 720 0 0 1 7 42 210 840 2520 5040 5040 0
การเรียกใช้ฟังก์ชัน ไม่จำเป็นต้องให้ฟังก์ชันทำการคำนวณเพื่อคืนค่ากลับออกมาก็ได้ ในภาษา C++ เรียกฟังก์ชันประเภทนี้ว่า ฟังก์ชัน void ในภาษาคอมพิวเตอร์ภาษาอื่น เรียกฟังก์ชันเหล่านี้ว่า กระบวนงาน(procedure) หรือ ซับรูทีน (subroutine) ในภาษา C++ กำหนดให้ระบุคำสำคัญ void เป็นชนิดข้อมูลที่ฟังก์ชัน void จะคืนค่ากลับ ข้อมูลชนิด void ถูกกำหนดให้ประกอบด้วยข้อมูลเซ็ตว่าง (empty set) ดังนั้น ผู้เขียนโปรแกรมจะประกาศตัวแปรให้เก็บข้อมูลชนิด void ไม่ได้ สำหรับฟังก์ชันชนิด void จึงหมายถึงฟังก์ชันที่ไม่มีการส่งค่าใด ๆ คืนมา
ตัวอย่างที่ 5.10 ฟังก์ชันพิมพ์วันที่
void printDate(int , int , int ); int main() { int month, day, year; do { cout << "Please enter month, day, year:"; cin >> month >> day >> year; printDate(month, day, year); } while (month > 0); } void printDate(int m, int d, int y) { if (m < 1 || m > 12 || d < 1 || d > 31 || y < 0) { cerr << "Error: parameter out of range. \n"; return; } switch (m) { case 1: cout << "January "; break; case 2: cout << "February ";break; case 3: cout << "March "; break; case 4: cout << "April "; break; case 5: cout << "May "; break; case 6: cout << "June "; break; case 7: cout << "July "; break; case 8: cout << "August "; break; case 9: cout << "September ";break; case 10: cout << "October "; break; case 11: cout << "November ";break; case 12: cout << "December ";break; } cout << d << ", " << y << endl; }
ทดสอบโปรแกรม เมื่อป้อนค่า (12,7,1941) , (5,16,1994) และ (0,0,0) ตามลำดับ
12 7 1941 December 7, 1941 5 16 1994 May 16, 1994 0 0 0 Error parameters out of range.
ฟังก์ชัน printDate ( ) ไม่มีการคืนค่า เนื่องจาก ฟังก์ชัน printDate ( ) ไม่มีการคำนวณ แต่มีไว้เพื่อแสดงวันที่ตามรูปแบบที่กำหนดทางจอภาพเท่านั้น จึงมีชนิดข้อมูลเป็น void ฟังก์ชัน printDate ( ) ใช้คำสั่ง switch เพื่อที่จะพิมพ์เลขที่เดือนเป็นชื่อเดือน แล้วพิมพ์วันที่และปีเป็นตัวเลข ให้สังเกตว่าฟังก์ชันจะไม่พิมพ์ค่า ถ้าพบว่าค่า m > 12 หรือ y < 0 แต่ค่าบางค่า เช่น February 31, 1996 สามารถพิมพ์ได้ สำหรับการปรับปรุงโปรแกรมมีอยู่ในแบบฝึกหัดท้ายบท
เนื่องจาก ฟังก์ชัน void ไม่มีการส่งค่าคืนมา ดังนั้น ผู้เขียนโปรแกรมสามารถละการเขียนคำสั่งคืนค่า (return statement) ถ้าพบว่าเป็นคำสั่งสุดท้ายในฟังก์ชัน หรือ เขียนคำสั่ง return;
เพื่อเป็นการจบการทำงานของฟังก์ชันก่อนกำหนด การทำงานในฟังก์ชัน void มีลักษณะเป็นการกระทำ ดังนั้น ผู้เขียนโปรแกรมควรตั้งชื่อฟังก์ชันเป็นชื่อเชิงกิริยา ไม่ใช่คำนาม เช่น ฟังก์ชันตัวอย่างชื่อ printDate แทนที่จะเป็น date
ในในหลายๆ กรณี ผู้เขียนโปรแกรมมักออกแบบฟังก์ชันเพื่อใช้ประเมินเงื่อนไข เพื่อใช้ในคำสั่งเลือกกระทำ เช่น คำสั่ง if และ คำสั่ง if..else หรือ ใช้ในคำสั่งวนรอบ เช่น คำสั่ง while และ คำสั่ง do..while ฟังก์ชันที่ถูกเขียนเพื่อใช้ประเมินเงื่อนไข เรียกว่า ฟังก์ชัน bool
ตัวอย่างที่ 5.11 การแยกประเภทอักขระ
#include <iostream.h> #include <cctype> //defines functions isdigit(),islower(), etc. void printCharCategory(char c); // prints the category to which the given character belongs; int main () { for (int c=65; c < 100; c++) { printCharCategory(c); } return 0; } void printCharCategory (char c) { //prints the category to which the given character belongs: cout << "The character [" << c << "] is a "; if (isdigit(c)) cout << "digit.\n"; else if (islower(c)) cout << "lower-case letter.\n"; else if (isupper(c)) cout << "capital letter.\n"; else if (isspace(c)) cout << "white space character.\n"; else if (iscntrl(c)) cout << "control character.\n"; else if (ispunct(c)) cout << "punctuation mark.\n"; else cout << "Error.\n"; }
ทดสอบโปรแกรม (บางส่วนของผลรันจาก 128 บรรทัด)
. . . The character [ ] is a white space character. The character [!] is a punctuation mark. The character ["] is a punctuation mark. The character [#] is a punctuation mark. The character [$] is a punctuation mark. . . .
ฟังก์ชัน printCharCategory ( ) จะเรียกฟังก์ชัน bool 6 ตัว คือ isdigit ( ), islower ( ), isupper ( ), isspace ( ), iscntrl ( ),และ ispunct ( ) โดยฟังก์ชันทั้ง 6 ตัวถูกนิยามอยู่ในไฟล์ส่วนหัว <cctype> เพื่อใช้ทดสอบประเภทของอักขระของวัตถุชนิด char
ในภาษา C ไม่มีตัวแปรชนิด bool ดังนั้น ฟังก์ชันทั้ง 6 ตัวในตัวอย่างจะส่งคืนค่าชนิดจำนวนเต็มแทน ส่วนในภาษา C++ ค่าจำนวนเต็มที่ส่งคืนจะถูกเปลี่ยนเป็นข้อมูลชนิด bool โดยอัตโนมัติ โดยที่จำนวนเต็มที่ไม่ใช่ 0 ถือว่าเป็นค่าจริง (1) ส่วนจำนวนเต็มที่มีค่า 0 ถือว่าเป็นค่าเท็จ (0)
ตัวอย่างที่ 5.13 ฟังก์ชันสำหรับหาจำนวนเฉพาะ
bool isPrime(int n) { // returns true if n is prime, false otherwise: float sqrtn = sqrt(n); if (n < 2) return false; // o and 1 are not primes if (n < 4) return true; // 2 and 3 are the first primes if (n % 2 == 0) return false; // 2 is the only even prime for (int d = 3; d <= sqrtn; d += 2) if ( n % d == 0) return false; // n has a nontrival divisor return true; // n has no nontrival divisors } int main() { for (int n = 0; n < 80; n++) if (isPrime(n)) cout << n << " "; cout << endl; return 0; }
ทดสอบโปรแกรม
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79
ฟังก์ชัน isPrime ( ) จะหาตัวหาร d ซึ่งหารจำนวนเต็ม n ได้ลงตัว (n % d == 0) ถ้าพบว่ามีจริง แสดงว่า n ไม่ใช่จำนวนเฉพาะ แต่ถ้าพบว่า จำนวนเต็มตั้งแต่ 2 ทุกตัวที่น้อยกว่าค่ารากที่สองของ n ไม่มีตัวใดหาร n ได้ลงตัว แสดงว่า n เป็นจำนวนเฉพาะ
ฟังก์ชัน isPrime ( ) จะวนรอบเพื่อค้นหาตัวหารเฉพาะจำนวนเต็มที่มีค่าน้อยกว่ารากที่สองของ n โดยค่ารากที่สองของ n ถูกคำนวณ และเก็บค่าในตัวแปร sqrtn นอกลูป ทำให้การประมวลผลกระทำเพียงแค่ครั้งเดียว แต่สามารถเรียกมาใช้ได้เรื่อยๆ ในแต่ละรอบของการทำงาน นอกจากนี้ ผู้เขียนโปรแกรมยังเขียนการทำงานของฟังก์ชันให้มีประสิทธิภาพมากยิ่งขึ้น โดยการตรวจสอบว่าเลขจำนวนเต็มนั้นเป็นเลขคู่หรือไม่ (n % 2 == 0) ถ้าใช่ n จะไม่ใช่จำนวนเฉพาะ ทำให้การวนรอบที่เหลือเกิดขึ้น เพื่อใช้ตรวจสอบเฉพาะเลขจำนวนเต็มที่เป็นเลขคี่เท่านั้น โดยเริ่มจากเลขจำนวนเต็ม 3 แล้วเพิ่มค่าทีละ 2
ให้สังเกตว่าชื่อของฟังก์ชันคือ isPrime เป็นชื่อที่ทำให้คนทั่วไปเข้าใจได้ง่ายว่าฟังก์ชันนี้จะทำการตรวจสอบว่า เลขจำนวนเต็มที่กำหนดเป็นจำนวนเฉพาะหรือไม่ เหมือนกับคนทั่วไปเข้าใจในภาษาอังกฤษว่า “if n is prime” ข้อสังเกตอีกข้อหนึ่ง คือ ผู้เขียนโปรแกรมสามารถพัฒนาฟังก์ชัน isPrime ( ) ให้มีการทำงานในฟังก์ชันน้อยลง โดยการนำเลขจำนวนเฉพาะ (prime number) เท่านั้นมาทำการหารจำนวนเต็ม แต่กระบวนการนี้จะใช้ต้องใช้การประกาศข้อมูลแบบแถวลำดับ (array) ซึ่งจะได้เรียนในบทต่อไป
ตัวอย่างที่ 5.14 ฟังก์ชันเพื่อหาปีอธิกสุรทิน (Leap Year)
ปีอธิกสุรทิน (leap year) คือ ปีที่มีวันที่ 29 กุมภาพันธ์เพิ่มขึ้นมา ส่วนใหญ่คนมักจะทราบว่าปีอธิกสุรทิน คือ ปีที่หารด้วย 4 ลงตัว ในความเป็นจริงแล้ว ยังมีกฎข้อยกเว้น คือ ปีที่หารด้วย 100 ลงตัวจะไม่ใช่ปีอธิกสุรทิน เว้นเสียแต่ว่า ปีนั้นจะหารด้วย 400 ลงตัว จึงจะเป็นปีอธิกสุรทิน เช่น ปี 2000 จึงเป็นปีอธิกสุรทิน
bool isLeapYear(int); int main() { int n; do { cout << "Enter year:"; cin >> n; if (isLeapYear(n)) { cout << n << " is a leap year.\n"; } else cout << n << " is not a leap year.\n"; } while (n > 1); return 0; } bool isLeapYear(int y) { // returns true if y is a leap year: return y % 4 == 0 && y % 100 != 0 || y% 400 == 0; }
ทดสอบโปรแกรม เมื่อป้อนค่าเป็น 2000, 2001, 1800 และ 0 ตามลำดับ
2000 2000 is a leap year. 2001 20001 is not a leap year. 1800 1800 is not a leap year. 0 0 is a leap year.
หมายเหตุ เงื่อนไขซ้อน y % 4 == 0 && y % 100 != 0 || y % 400 == 0 จะเป็นจริงเมื่อ y หารด้วย 4 ลงตัว แต่หารด้วย 100 ไม่ลงตัว หรือหารด้วย 400 ลงตัว
ประโยชน์ข้อหนึ่งของฟังก์ชัน คือ การห่อหุ้ม (encapsulation) ซึ่งเป็นการรวบรวมรายละเอียดของงานที่ไม่ต้องการจะนำเสนอในโปรแกรม ซึ่งการนำเสนออาจนำไปสู่ความสับสนต่องานหลักๆ ในโปรแกรมได้ เช่น โปรแกรมการบันทึกข้อมูลบุคคลนั้น จะต้องบันทึกค่าอายุของบุคคล ซึ่งจำเป็นต้องมีการตรวจสอบค่าอายุที่ผู้ใช้ป้อนเข้ามา ว่าเป็นค่าที่เป็นไปไม่ได้หรือไม่ ถ้าผู้เขียนโปรแกรมนำรายละเอียดการตรวจสอบทั้งหมดมาอไว้ในฟังก์ชันการบันทึกบันทึก จะทำให้โปรแกรมมีรายละเอียดปลีกย่อย และมีความสลับ ซับซ้อนมากเกินไป
ตัวอย่างฟังก์ชัน printDate ( ) เป็นฟังก์ชันที่ทำการรับข้อมูลผ่านทางพารามิเตอร์ แล้วแสดงผลการพิมพ์วันที่ของข้อมูลตามรูปแบบที่กำหนด ซึ่งจัดเป็นฟังก์ชันการส่องออกข้อมูล (output functions) โปรแกรมในตัวอย่าง 5.14 เป็นการแสดงการเขียนฟังก์ชันการนำเข้าข้อมูลผ่านทางแป้นพิมพ์ (input functions) ในฟังก์ชันมีการใช้คำสั่ง while (true) เพื่อควบคุมใหเกิดการวนรอบไม่รู้จบ (infinite loop) และใช้คำสั่ง return ในการออกจากลูป และสิ้นสุดการทำงานของฟังก์ชัน
ตัวอย่างที่ 5.14 ฟังก์ชันรับค่าอายุผู้ใช้โปรแกรม
int getAge(); int main() { //tests the age() function: int a = age(); cout << "\nYou are " << a << " years old."; } int getAge() { //prompts theuesr to input his/her age, and returns that value int n; while (true) { cout << "How old are you:"; cin >> n; if (n < 0) cout << "\a\tYour age could not be negative."; else if (n > 120) cout << "\a\tYou could not be over 120."; else return n; cout << "\n\tTry again.\n"; } }
ฟังก์ชันนี้เป็นตัวอย่างของการให้ผู้ใช้โปรแกรมป้อนอายุ โดยฟังก์ชันจะทำการตรวจสอบ และจะปฏิเสธข้อมูลอายุที่ไม่น่าจะเป็นไปได้ จนกว่าผู้ใช้โปรแกรมจะป้อนข้อมูลที่ถูกต้อง โดยฟังก์ชันจะตั้งสมมุติฐานว่า อายุของคนจะอยู่ในช่วง 0 ถึง 120 ปีเท่านั้น
หลังจากนำเข้าข้อมูลจาก cin ถ้าข้อมูลที่ผู้ใช้ป้อนเป็นที่ยอมรับ ฟังก์ชันจะคืนค่าดังกล่าวให้กับฟังก์ชัน main ( ) แต่ถ้าที่ผู้ใช้ป้อนไม่อยู่ในช่วงของค่าที่น่าเป็นไปได้ โปรแกรมจะส่งเสียงเตือน (beep) และให้ผู้ใช้ทำการป้อนข้อมูลใหม่ ให้สังเกตุว่า คำสั่ง return ในฟังก์ชัน getAge( ) ไม่ได้อยู่เป็นคำสั่งสุดท้ายของฟังก์ชัน และ ฟังก์ชัน getAge ( ) ไม่มีรับค่าพารามิเตอร์เข้าไปทำงานในฟังก์ชัน แต่ในการเขียนฟังก์ชันจำเป็นต้องระบุเครื่องหมาย ( ) เสมอ ทั้งในส่วนการประกาศฟังก์ชัน (function declaration) ส่วนการนิยามฟังก์ชัน (function definition) และ การเรียกใช้ฟังก์ชัน (function call)
ที่ผ่านมาการส่งผ่านข้อมูลเข้าไปทำงานในฟังก์ชันเป็นการส่งผ่านข้อมูลแบบการส่งผ่านโดยค่า (passing by value) กล่าวคือ เมื่อมีการเรียกใช้ฟังก์ชัน จะมีการกำหนดค่าให้กับพารามิเตอร์ของฟังก์ชันก่อนที่จะเริ่มกระบวนการทำงานตามคำสั่งในฟังก์ชัน เช่น เมื่อมีการเรียกใช้ cube(x) ถ้าตัวแปร x มีค่า เท่ากับ 4 ค่าจำนวนเต็ม 4 จะถูกกำหนดค่าให้กับพารามิเตอร์ n ซึ่งจัดว่าเป็นตัวแปรท้องถิ่น (local variable) ในฟังก์ชัน cube ( ) แล้วจึงเริ่มการทำงานในฟังก์ชัน cube ( ) การทำงานจะเกิดขึ้นที่ตัวแปร n ดังนั้น ตัวแปร x จึงไม่ได้ถูกเปลี่ยนแปลงค่าหลังจากการเรียกใช้ฟังก์ชัน กล่าวอีกนัยหนึ่งว่า x เป็นพารามิเตอร์ที่อ่านค่าเท่านั้น (read-only parameters)
กระบวนการส่งข้อมูลแบบผ่านค่าอนุญาตให้พารมิเตอร์ที่ส่งค่าเป็นนิพจน์ทั่วไป เช่น cube(3), cube(2*x-3) และ cube(2*sqrt(x)-cube(3)) ซึ่งนิพจน์ภายในวงเล็บจะถูกประมวลผลเพื่อให้ได้ค่าส่งผ่านไปยังฟังก์ชัน cube ( )
กระบวนการส่งผ่านโดยค่าเป็นวิธีการที่เรามักจะใช้ในการส่งข้อมูลไปทำงานกับฟังก์ชัน เพราะจะทำให้ค่าของพารามิเตอร์ไม่เปลี่ยนแปลงหลังจากการเรียกฟังก์ชัน อย่างไรก็ตาม ฟังก์ชันบางตัวจำเป็นจะต้องมีการเปลี่ยนแปลงค่าของพารามิเตอร์ที่ส่งให้ฟังก์ชัน กระบวนการส่งผ่านข้อมูลดังกล่าว เรียกว่า การส่งผ่านข้อมูลแบบอ้างอิง (passing by reference)
ในการส่งพารามิเตอร์โดยใช้การอ้างอิงตำแหน่ง ผู้เขียนโปรแกรมต้องระบุเครื่องหมาย & ไว้หลังชนิดข้อมูลของพารามิเตอร์แต่ละตัวในฟังก์ชัน โดยเมื่อมีคำสั่งเปลี่ยนแปลงค่าตัวแปรในฟังก์ชันที่ผ่านข้อมูลแบบอ้างอิง จะส่งผลให้อ้างอิงถึงตำแหน่งเดียวกับตัวแปรที่เป็นพารามิเตอร์ ผลที่เกิดขึ้น ตัวแปรพารามิเตอร์มีการเปลี่ยนแปลงค่า กระบวนการดังกล่าวมีผลให้ค่าของพารามิเตอร์ทั้งถูกเขียนและถูกอ่าน (read-write parameters) พารามิเตอร์ที่ส่งผ่านข้อมูลโดยค่า เรียกว่า พารามิเตอร์แบบค่า (value parameters) ส่วนพารามิเตอร์ที่ส่งผ่านข้อมูลโดยการอ้างอิง เรียกว่า พารามิเตอร์แบบอ้างอิง (reference parameters)
ตัวอย่างที่ 5.15 ฟังก์ชัน swap ( )
void swap(float&, float&); int main() { float a = 22.2, b = 44.4; cout << "a = " << a << ", b = " << b << endl; swap(a, b); cout << "a = " << a << ", b = " << b << endl; } void swap(float& x, float& y) { // exchages the values of x and y: float temp = x; x = y; y = temp; }
ทดสอบโปรแกรม
a = 22.2 b = 44.4 a = 44.4 b = 22.2
จุดประสงค์ของฟังก์ชัน swap ( ) คือ การแลกเปลี่ยนค่าของออปเจกต์ ซึ่งสามารถทำได้โดยการประกาศตัวแปร x และ y เป็นตัวแปรแบบอ้างอิง: float& x, float& y ตัวดำเนินการอ้างอิง & ทำให้ตัวแปร x และ y ตำแหน่งเดียวกับตัวแปรอาร์กิวเมนต์ที่ส่งมา คือ ตัวแปร a และ b ตามลำดับ
เมื่อฟังก์ชัน swap(a, b) ทำงาน จะสร้างการอ้างอิงท้องถิ่น (local references) สำหรับพารามิเตอร์ x และ y โดย x เป็นจะอ้างอิงตัวแปร a และ y จะอ้างอิงตัวแปร b จากการดำเนินการของทั้ง 3 คำสั่งในฟังกชั่น ในตอนแรกตัวแปรท้องถิ่น temp ถูกประกาศขึ้นและกำหนดค่าเริ่มต้นให้เป็น x (ซึ่งก็คือ a) จากนั้นจะกำหนดค่าของ x (ซึ่งก็คือ a) ด้วยค่าของ y (ซึ่งก็คือ b) สุดท้ายจะกำหนดค่า y (ซึ่งก็คือ b) ด้วยค่าของ temp (ซึ่งได้ค่าของ a ผ่านการอ้างอิง x) ทำให้ได้ค่า a เป็น 44.4 และ b เป็น 22.2 รูป 5-3 แสดงการส่งผ่านค่าระหว่างฟังก์ชัน main ( ) กับ ฟังก์ชัน swap ( )
ในภาษา C ผู้เขียนโปรแกรมสามารถเขียนตัวดำเนินการอ้างอิง & ในรูปแบบของตัววางหน้าตัวแปรในพารามิเตอร์ได้ เช่น ผู้เขียนโปรแกรมสามารถเขียนส่วนหัวของฟังก์ชัน swap เป็น
void swap(float &x, float &y)
ตัวอย่างที่ 5.16 แสดงข้อแตกต่างระหว่างการส่งผ่านพารามิเตอร์แบบโดยค่า กับ การส่งผ่านพารามิเตอร์โดยการอ้างอิง
รูป 5-3 การส่งผ่านข้อมูลแบบโดยการอ้างอิง
ตัวอย่างที่ 5.16 การส่งผ่านข้อมูลแบบโดยค่า และแบบโดยการอ้างอิง
void f(int, int&); int main() { int a = 22, b = 44; cout << "a = " << a << ", b = " << b << endl; f(a, b); cout << "a = " << a << ", b = " << b << endl; f(2*a-3, b); cout << "a = " << a << ", b = " << b << endl; } void f(int x, int& y) { // changes reference argument to 99: x = 88; y = 99; }
ทดสอบโปรแกรม
a = 22 b = 44 a = 22 b = 99 a = 22 b = 99
การเรียกฟังก์ชัน f(a, b) ส่งพารามิเตอร์ x แบบโดยค่า และส่งพารามิเตอร์ y แบบโดยการอ้างอิง ดังนั้น ตัวแปรเท้องถิ่น x จึงมีค่าเท่ากับ 22 เมื่อกำหนดค่า 88 ให้กับ x จะไม่มีผลกระทบกับค่าในตัวแปร a ดังนั้น เมื่อเราแสดงค่าของ a ในฟังก์ชัน main ( ) จึงมีค่าเท่ากับ 22 เหมือนเดิม ในขณะที่ การส่งพารามิเตอร์ y ใช้การอ้างอิงตำแหน่งตัวแปร b ดังนั้น เมื่อเราเปลี่ยนค่าของตัวแปร y ค่าที่เปลี่ยนจะปรากฎที่ตำแหน่งเดียวกับตัวแปร b ทำให้ค่า 44 ถูกเปลี่ยนเป็น 99 การส่งผ่านค่าของพารามิเตอร์ในตัวอย่าง 5.16 แสดงดังรูป 5-4
รูป 5-4 การส่งผ่านข้อมูลโปรแกรมตัวอย่าง 5.16
ข้อแตกต่างของการส่งผ่านข้อมูลแบบค่า และ แบบโดยการอ้างอิง แสดงดังตาราง 5-3
ตาราง 5-3 เปรียบเทียบการส่งผ่านข้อมูลแบบโดยค่า และ โดยอ้างอิง
ผ่านข้อมูลแบบโดยค่า | ผ่านข้อมูลแบบโดยอ้างอิง |
int x; | int& x; |
พารามิเตอร์ x เป็นตัวแปรท้องถิ่น | พารามิเตอร์ x เป็นการอ้างอิงท้องถิ่น |
เป็นการสำรองข้อมูลซ้ำซ้อนกับอาร์กิวเมนต์ | เป็นการกำหนดชื่ออีกชื่อหนึ่งให้กับอาร์กิวเมนต์ |
เปลี่ยนแปลงค่าอาร์กกิวเมนต์ไม่ได้ | สามารถเปลี่ยนแปลงค่าอาร์กกิวเมนต์ได้ |
ค่าอาร์กิวเมนต์สามารถเป็น ค่าคงที่ ตัวแปร หรือ นิพจน์ | ค่าอาร์กิวเมนต์ต้องเป็น ตัวแปร เท่านั้น |
อาร์กิวเมนต์เป็นแบบอ่านค่าอย่างเดียว | อาร์กิวเมนต์เป็นแบบอ่านและเขียนค่าได้ |
เนื่องจาก คำสั่ง return จะส่งคืนค่าได้เพียงค่าเดียวในชื่อฟังก์ชัน ในกรณีที่ผู้เขียนโปรแกรมต้องการให้มีการส่งคืนค่ามากกว่าหนึ่งค่าจากฟังก์ชัน ผู้เขียนโปรแกรมสามารถใช้พารามิเตอร์แบบอ้างอิง ทำการเก็บค่าที่เปลี่ยนแปลงในฟังก์ชันแทนการเก็บค่าผลลัพธ์ในชื่อฟังก์ชันได้
ตัวอย่างที่ 5.17 การคืนค่ามากกว่า 1 ค่า
void computeCircle(double &, double &, double); int main() { double r, a, c; cout << "Enter radius:"; cin >> r; computeCircle(a, c, r); cout << "area = " << a << ", circumference = " << c << endl; } void computeCircle(double &area,double& circumference,double r) { // returns the area and circumference of a circle with radius r const double PI = 3.141592653589793; area = PI*r*r; circumference = 2*PI*r; }
ทดสอบโปรแกรม
Enter radius: 100 area = 31415.9 circumference = 628.319
ฟังก์ชัน computeCircle ( ) จะคืนค่า 2 ค่าไปยังพารามิเตอร์แบบอ้างอิง คือ area และ circumference เมื่อกำหนดค่ารัศมี r เป็นข้อมูลนำเข้าไปเพื่อทำงานในฟังก์ชัน ให้สังเกตว่า เรามักนิยมเขียนกลุ่มพารามิเตอร์ที่มีการส่งค่าคืนกลับจากฟังก์ชัน ไว้ก่อนพารามิเตอร์ที่มีการส่งค่าเข้าไปเพื่อใช้งานในฟังก์ชัน เช่น ตัวอย่างประกาศพารามิเตอร์ area และ circumference ไว้ก่อน radius
ข้อดีของการส่งพารามิเตอร์แบบการอ้างอิงมี 2 ข้อหลักๆ คือ ผู้เขียนโปรแกรมสามารถเปลี่ยนค่าของอาร์กิวเมนต์ได้ เช่น การเปลี่ยนค่าในฟังก์ชัน swap ( ) ส่วนอีกข้อหนึ่ง คือ ถ้ามีการส่งค่าไปเก็บในฟังก์ชัน ฟังก์ชันจะต้องสำรองเนื้อที่หน่วยความจำเพื่อเก็บค่าที่ส่งเข้ามา ทำให้สิ้นเปลืองทรัพยากรหน่วยความจำ การส่งผ่านพารามิเตอร์โดยการอ้างอิงจึงเป็นวิธีที่ช่วยประหยัดการสำรองเนื้อที่ของหน่วยความจำ ในบางกรณี ผู้เขียนโปรแกรมไม่ประสงค์ให้ข้อมูลถูกเปลี่ยนแปลง เช่น การส่งข้อมูลเพื่อใช้ในการพิมพ์เท่านั้น การส่งพารามิเตอร์แบบโดยการอ้างอิงคงไม่ใช่คำตอบที่ผู้เขียนโปรแกรมเลือกใช้ แม้ว่า การส่งพารามิเตอร์แบบโดยค่าจะช่วยป้องกันการเปลี่ยนค่าตัวแปรโดยไม่ตั้งใจ แต่การสำรองเนื้อที่เพื่อเก็บข้อมูล โดยเฉพาะข้อมูลขนาดใหญ่ เช่น ข้อมูลภาพ อาจก่อให้เกิดการสิ้นเปลืองเนื้อที่หน่วยความจำ ในภาษา C++ มีทางเลือกที่ 3 ในการส่งผ่านพารามิเตอร์ คือ การส่งพารามิเตอร์โดยการอ้างอิงแบบค่าคงที่ (passing by constant reference) การส่งผ่านข้อมูลในวิธีนี้ จะเหมือนกับการส่งพารามิเตอร์โดยการอ้างอิง แต่ระบบจะป้องกันไม่ให้เกิดแก้ไขข้อมูล โดยฟังก์ชันสามารถทำได้เพียงแค่การอ่านค่าของตัวแปรเท่านั้น (read-only parameters)
ตัวอย่างที่ 5.18 การส่งผ่านค่าโดยการอ่างอิงแบบค่าคงที่
void f(int x, int &y, const int &z) { x += z; y += z; cout << "x = " << x << ", y = " << y << ", z = " << z << endl; } int main() { // tests the f() function int a = 22, b = 33, c = 44; cout << "a = " << a << ", b = " << b << ", c = " << c << endl; f(a,b,c); cout << "a = " << a << ", b = " << b << ", c = " << c << endl; f(2*a-3,b,c); cout << "a = " << a << ", b = " << b << ", c = " << c << endl; }
ทดสอบโปรแกรม
a = 22 b = 33 c = 44 x = 66 y = 77 z = 44 a = 22 b = 77 c = 44 x = 85 y = 121 z = 44 a = 22 b = 121 c = 44
ฟังก์ชันจะเปลี่ยนเฉพาะค่าของพารามิเตอร์ y แต่จะไม่สามารถเปลี่ยนค่าพารามิเตอร์ z ส่วน x เมื่อถูกเปลี่ยนค่า จะไม่มีผลต่ออาร์กิวเมนต์ a เพราะว่า x เป็นพารามิเตอร์แบบค่า การเปลี่ยนแปลงจึงเกิดที่ตัวแปรท้องถิ่น x เท่านั้น ส่วน y เป็นพารามิเตอร์แบบอ้างอิง ซึ่งจะอ้างอิงตำแหน่งเดียวกับตัวแปร b การเปลี่ยนค่าของ y จึงหมายถึงการเปลี่ยนค่าของตัวแปร b ส่วนการกำหนดพารามิเตอร์โดยอ้างอิงแบบค่าคงที่ ทำให้ผู้เขียนโปรแกรมไม่สามารถเขียนคำสั่งเปลี่ยนค่าของ z ได้ ทำได้เพียงแต่การนำค่ามาทำงานเท่านั้น ดังที่แสดงในตัวอย่าง
การเรียกฟังก์ชันในแต่ละครั้งจะเกิดการสำรองเนื้อที่ในหน่วยความจำเพื่อใช้สำหรับการประมวลผลฟังก์ชัน เช่น การสำรองหน่วยความจำเพื่อเก็บค่าตัวแปรท้องถิ่นที่ใช้ในฟังก์ชัน นอกจากนั้น การกระโดดไปทำงานในฟังก์ชัน ทำให้เสียเวลาเพิ่มขึ้นในการทำกระบวนการบางอย่างก่อนไปทำงานในฟังก์ชัน เช่น การกำหนดค่าของพารามิเตอร์ การสำรองพื้นที่ของตัวแปรท้องถิ่นในฟังก์ชัน การบันทึกค่าของตัวแปรต่าง ๆ และเรจิสเตอร์ และ การเก็บค่าตำแหน่งคำสั่งปัจจุบันที่เรียกฟังก์ชัน เพื่อใช้ในการกระโดดกลับมาที่คำสั่งเดิมเมื่อการทำงานของฟังก์ชันสิ้นสุดลง เป็นต้น บางครั้งเราสามารถหลีกเลี่ยงกระบวนการเหล่านี้ได้ โดยการกำหนดฟังก์ชันเป็นแบบฟังก์ชันแทรก (inline function) ซึ่งจะเป็นตัวแจ้งให้โปรแกรมแปลโปรแกรมทำการแทนที่คำสั่งการเรียกใช้ฟังก์ชันในโปรแกรม ด้วยรหัสคำสั่งของฟังก์ชันนั้น
ตัวอย่างที่ 5.19 ฟังก์ชันแทรก cube ( )
inline int cube(int x) { //returns cube of x: return x*x*x; } int main() { // tests the cube() function: cout << cube(4) << endl; int x, y; cout << "Enter number:"; cin >> x; y = cube(x); cout << "cube(" << x << ") is " << y << endl; }
เมื่อโปรแกรมแปลโปรแกรมทำการแปลคำสั่งโปรแกรมตัวอย่าง จะทำการแทรกรหัสคำสั่งของฟังก์ชัน cube ( ) ในทุกที่ที่มีการเรียกใช้ในฟังก์ชัน main ( ) กล่าวคือ จะแทนที่นิพจน์ cube(4) ในฟังก์ชัน main ( ) ด้วยคำสั่งจริงๆของฟังก์ชัน cube ( ) คือ 4*4*4 และ แทนที่นิพจน์ cube(x) ในฟังก์ชัน main ( ) ด้วยคำสั่งx*x*x ทำให้ผลการแปลโปรแกรมตัวอย่าง 5.19 ได้ดังนี้
int main() { // tests the cube() function: cout << 4*4*4 << endl; int x, y; cout << "Enter number:"; cin >> x; y = x*x*x ; cout << "cube(" << x << ") is " << y << endl; }
กระบวนการทำงานของโปรแกรมแปลโปรแกรมในการแทนที่คำสั่งฟังก์ชันแทรก เรียกว่า การขยาย (expand) ฟังก์ชันแทรก การใช้ฟังก์ชันแทรกอาจจะมีผลเสียเกิดขึ้น ถ้าผู้เขียนโปรแกรมไม่ระมัดระวัง เช่น ถ้าหากโปรแกรมมีการเรียกฟังก์ชันแทรก 26 ครั้ง และฟังก์ชันแทรกมีขนาด 40 คำสั่ง โปรแกรมหลังจากการแปลคำสั่ง จะปรากฏบรรทัดคำสั่งเพิ่มขึ้นอีกกว่า 1000 คำสั่ง
ในหัวข้อ 1.8 มีการอธิบายขอบเขตของการใช้งานตัวแปร โดยผู้เขียนโปรแกรมสามารถเรียกใช้ตัวแปรได้หลังจากการประกาศตัวแปร ถ้าหากว่าการประกาศตัวแปรนั้นอยู่ในฟังก์ชัน ขอบเขตของตัวแปรก็จะสิ้นสุดในฟังก์ชันนั้น แต่ถ้าตัวแปรถูกประกาศในบล็อก ขอบเขตของตัวแปรก็จะสิ้นสุดในบล็อกดังกล่าว
ในโปรแกรมหนึ่งๆ สามารถมีวัตถุที่ใช้ชื่อเดียวกันได้ ถ้าวัตถุดังกล่าวถูกประกาศไว้ในคนละขอบเขต ดังแสดงไว้ในตัวอย่าง 1.9 ตัวอย่างต่อไปจะแสดงถึงการประกาศและใช้งานตัวแปรที่ใช้ชื่อเดียวกัน แต่ถูกประกาศไว้ในขอบเขตต่างๆ กัน เช่น ประกาศไว้ในคนละฟังก์ชัน
ตัวอย่างที่ 5.20 ขอบเขตแบบซ้อนและขนาน
void f(); // f() is global void g(); // g() is global int x = 11; // this x is global int main() { int x = 22; { int x = 33; cout << "In block inside main() : x = " << x << endl; } cout << "In main(): x = " << x << endl; cout << "In main(): ::x = " << ::x << endl; f(); g(); } void f() { int x = 44; cout << "In f() : x = " << x << endl; } void g() { cout << "In g(): x = " << x << endl; }
ทดสอบโปรแกรม
Inblock inside main(): x = 33 In main(): x = 22 In main(): ::x = 11 In f(): x = 44 In g(): x = 11
ตัวแปร x ที่กำหนดให้มีค่าเริ่มต้นเป็น 11 เป็นตัวแปรส่วนรวม ดังนั้น ขอบเขตของ x (11) จึงสามารถใช้ได้ทั้งโปรแกรม ตัวแปร x ตัวที่สอง มีค่าเริ่มต้นเป็น 22 เป็นตัวแปรท้องถิ่นในฟังก์ชัน main ( ) จึงใช้ได้เฉพาะในฟังก์ชัน main ( ) เท่านั้น ตัวแปร x ถัดมา ถูกกำหนดให้มีค่าเริ่มต้นเป็น 33 เป็นตัวแปรท้องถิ่นในบล็อกในฟังก์ชัน main ( ) จึงถูกใช้ได้เฉพาะในบล็อกเท่านั้น เมื่อมีการแสดงผลค่า x ในบล็อกจึงมีค่าเป็น 33 ค่า x ในฟังก์ชัน main ( ) มีค่าเป็น 22 และ :: x ที่หมายถึงตัวแปรส่วนรวมจึงมีค่าเป็น 11
ในฟังก์ชัน f ( ) มีการประกาศ x และกำหนดค่าเริ่มต้นให้เป็น 44 ขอบเขตของ x ตัวนี้จะจำกัดอยู่ในฟังก์ชัน f ( ) เท่านั้น ซึ่งเป็นการประกาศคู่ขนานกับตัวแปร x (22) ในฟังก์ชัน main ( ) แต่ขอบเขตของ x ในฟังก์ชัน f ( ) เป็นขอบเขตที่ซ้อนอยู่ในขอบเขต x ตัวแรกที่เป็นาตัวแปรส่วนรวม ดังนั้น การเรียกใช้ค่า x ในฟังก์ชัน f ( ) จึงหมายถึง x ที่มีค่าเป็น 44 เมื่อเปรียบเทียบกับฟังก์ชัน g ( ) ที่ไม่มีการประกาศตัวแปร x การเรียกใช้ x ในฟังก์ชัน g ( ) จึงหมายถึง x ส่วนรวมที่มีค่าเป็น 11
ในภาษา C++ อนุญาตให้ผู้เขียนโปรแกรมสามารถกำหนดฟังก์ชันให้ใช้ชื่อเดียวกันได้ ตราบที่ฟังก์ชันเหล่านั้นมีการระบุพารามิเตอร์ไม่เหมือนกัน เช่น จำนวนของพารามิเตอร์ในฟังก์ชันแตกต่างกัน หรือ มีอย่างน้อยหนึ่งพารามิเตอร์ที่มีการระบุชนิดข้อมูลต่างกัน เรียกว่า การโอเวอร์โหลดฟังก์ชัน (function overloading)
ตัวอย่างที่ 5.21 การโอเวอร์โหลดฟังก์ชัน max ( )
int max(int, int); int max(int, int, int); int main() { cout << "max of 99 and 77 is " << max(99, 77) << endl; cout << "max of 55, 66, and 33 is " << max(55, 66, 33) << endl; return 0; } int max(int x, int y) { // return the maximum of the two given integer: return (x > y ? x : y); } int max(int x, int y, int z) { // return the maximum of the three given integer: int m = (x > y ? x : y); // m = max(x,y) return (z > m ? z : m); }
ทดสอบโปรแกรม
99 66
จากตัวอย่าง มีการนิยามฟังก์ชัน 2 ฟังก์ชันที่แตกต่างกัน แต่ใช้ชื่อ max เหมือนกัน โปรแกรมแปลโปรแกรมจะตรวจสอบการเรียกใช้พารามิเตอร์ เพื่อพิจารณาว่าการเรียกใช้ฟังก์ชัน max ( ) ในโปรแกรม เป็นการเรียกใช้งานฟังก์ชันใด เช่น ในการเรียกครั้งแรก max(99,77) มีการระบุพารามิเตอร์ 2 ตัว ทำให้โปรแกรมแปลโปรแกรมทราบว่า เป็นการเรียกใช้งานฟังก์ชัน max ( ) ตัวแรก ในขณะที่ การเรียก max(55,66,33) มีการระบุพารามิเตอร์ 3 ตัว จึงเป็นการเรียกใช้ฟังก์ชัน max ( ) ตัวที่สอง
โปรแกรมภาษา C++ ทุกโปรแกรมจำเป็นต้องมีฟังก์ชัน main ( ) โดย โปรแกรมที่สมบูรณ์อาจประกอบด้วยฟังก์ชัน main ( ) รวมถึง ฟังก์ชันอื่น ๆ ที่ถูกเรียกโดยตรงจากฟังก์ชัน main ( ) หรือ ถูกเรียกโดยฟังก์ชันที่ถูกเรียกโดยฟังก์ชัน main ( ) อีกทีหนึ่ง
เนื่องจาก ฟังก์ชัน main ( ) เป็นฟังก์ชันที่มีการคืนค่าเป็นจำนวนเต็ม จึงมักจะจบด้วยคำสั่ง
return 0;
โปรแกรมแปลโปรแกรมบางตัวอาจจะอนุญาตให้ละการเขียนคำสั่ง return 0; ค่าจำนวนเต็มจะถูกส่งคืนกลับไปยังระบบปฏิบัติการ (operating system) โดยค่าที่แสดงผลการทำงานที่ปกติ คือ 0 ส่วนการส่งคืนค่าใดๆ ก็ตามที่ไม่ใช่ 0 ถือว่าเกิดข้อผิดพลาดขึ้นในการประมวลผล
ถ้าในฟังก์ชัน main ( ) มีการใช้คำสั่ง return อยู่ในตำแหน่งที่ไม่ใช่คำสั่งสุดท้าย จะทำให้โปรแกรมจบการทำงานในทันทีที่พบ ดังตัวอย่างต่อไปนี้
ตัวอย่างที่ 5.22 การใช้คำสั่ง return เพื่อจบการทำงานของโปรแกรม
int main() { // prints the quotient of two input integer: int n, d; cout << "Enter two integers: " ; cin >> n >> d; if(d == 0) return 0; cout << n << "/" << d << " = " << n/d << endl; }
ทดสอบโปรแกรม เมื่อป้อนค่า 99 กับ 17
Enter two integers: 99 17 9 / 17 = 5
ทดสอบโปรแกรม เมื่อป้อนค่า 99 กับ 0
Enter two integers: 99 0
คำสั่ง return ในฟังก์ชันใด ๆ จะทำให้การทำงานของฟังก์ชันสิ้นสุดลง แล้วกลับไปยังจุดที่มีการเรียกฟังก์ชัน ดังนั้น เมื่อคำสั่ง return ในฟังก์ชัน main ( ) ถูกกระทำ การทำงานของฟังก์ชัน main ( ) จึงสิ้นสุดลง แล้วกลับไปยังจุดที่มีการเรียกฟังก์ชัน ซึ่งก็คือระบบปฏิบัติการ จึงมีผลทำให้การประมวลผลโปรแกรมสิ้นสุดลงนั่นเอง ในทางคอมพิวเตอร์ ผู้เขียนโปรแกรมสามารถจบการทำงานของโปรแกรมได้ 4 วิธี คือ
1. ใช้คำสั่ง return ในฟังก์ชัน main
2. เรียกใช้ฟังก์ชัน exit ( )
3. เรียกใช้ฟังก์ชัน abort ( )
4. เขียนคำสั่งให้ไปทำงานในส่วนข้อยกเว้น (throw an uncaught exception)
ฟังก์ชัน exit ( ) ถูกนิยามไว้ในไฟล์ส่วนหัว <cstdlib> ซึ่งสามารถใช้ในการจบโปรแกรมในส่วนคำสั่งของฟังก์ชันอื่น ๆ ที่ไม่ใช่ฟังก์ชัน main ( ) ดังตัวอย่าง 5.23
ตัวอย่างที่ 5.23 การใช้ฟังก์ชัน exit ( ) เพื่อจบโปรแกรม
#include <cstdlib> // define the exit () function #include <iostream> // define the cin and cout objects using namespace std; double reciprocal(double x); int main() { double x; cout << "Enter number:"; cin >> x; cout << "1/" << x << " = " << reciprocal(x) << endl; } double reciprocal(double x) { // return the reciprocal of x: if(x == 0) exit(1); // terminate the program return 1.0/x; }
ทดสอบโปรแกรม เมื่อป้อนค่า 2 ให้กับ x
Enter number: 2 1/2 = 0.5
ทดสอบโปรแกรม เมื่อป้อนค่า 0 ให้กับ x
Enter number: 0
เมื่อผู้ใช้ป้อน 0 ให้กับค่า x โปรแกรมจะจบการทำงานเองภายในฟังก์ชัน reciprocal ( ) โดยไม่มีการทำงานกับนิพจน์ 1.0/x
ในภาษา C++ จำนวนอาร์กิวเมนต์ที่ส่งค่าให้กับฟังก์ชันสามารถเปลี่ยนแปลงในช่วงเวลาของการประมวลผล เพียงแต่ผู้เขียนโปรแกรมกำหนดค่าโดยปริยายให้กับอาร์กิวเมนต์ (default arguments) การส่งค่าอาร์กิวเมนต์ก็จะกลายเป็นทางเลือก เมื่อมีการเรียกใช้ฟังก์ชันดังกล่าว
ตัวอย่างที่ 5.24 พารามิเตอร์โดยปริยาย
ฟังก์ชันในการหาค่าสมการพหุนามกำลังสาม $a_0+a_1x+a_2x^2+a_3x^3$ สามารถหาค่าได้โดยใช้ Horner’s Algorithm ทำการจัดกลุ่มสมการพหุนาม ให้อยู่ในรูปที่ง่ายแก่การคำนวณ คือ $a_0+ (a_1 + (a_2 + a_3x) x) x$
double p(double, double, double=0, double=0, double=0); int main() { // tests the p() function double x = 2; cout << "p(x,2) = " << p(x,2) << endl; cout << "p(x,2,3) = " << p(x,2,3) << endl; cout << "p(x,2,3,4) = " << p(x,2,3,4) << endl; cout << "p(x,2,3,4,5) = " << p(x,2,3,4,5) << endl; } double p(double x, double a0, double a1, double a2, double a3) { // returns a0 + a1*x + a2*x^2 + a3*x^3 return a0 + (a1 + (a2 + a3*x) * x) * x; }
ทดสอบโปรแกรม
p(x,7) = 7 p(x,7,6) = 19.018 p(x,7,6,5) = 39.078 p(x,7,6,5,4) = 71.2223
เมื่อฟังก์ชัน p( ) ถูกเรียกใช้ นิพจน์ $a_0 + a_1x + a_2x^2 + a_3x^3$ จะถูกพิจารณา แต่เนื่องจากตัวแปร a1, a2, และ a3 ต่างก็มีค่าโดยปริยายเป็น 0 ดังนั้น ฟังก์ชันจึงสามารถถูกเรียกโดยใช้รูปแบบ p(x,a0) หรือ p(x,a0,a1) หรือ p(x,a0,a1,a2) หรือ p(x,a0, a1, a2, a3) ก็ได้ เช่น เมื่อเราเรียก p(x,2,3) จะมีค่าเท่ากับเราเรียก p(x,2,3,0, 0) ซึ่งมีค่าเหมือนกับเราเรียก 2 + 3x = 2 + 3*2 = 8
ในฟังก์ชันที่มีการกำหนดพารามิเตอร์ที่มีค่าโดยปรยาย จะต้องระบุพารามิเตอร์ที่มีค่าโดยปริยายไว้ทางด้านขวาสุด (หรือหลังสุด) เสมอ เช่น
void f(int a, int b, int=4, int=7, int=3);
เป็นการประกาศที่ถูกต้อง แต่
void g(int a, int=2, int=4, int, int=3);
เป็นการประกาศที่ไม่ถูกต้อง