Table of Contents

บทที่ 5 ฟังก์ชัน (Functions)

โปรแกรมที่มีประโยชน์และนำไปงานได้จริงส่วนใหญ่จะมีขนาดใหญ่กว่าตัวอย่างโปรแกรมที่พบเห็นในบทเรียนที่ผ่านมา เพื่อให้การจัดการโปรแกรมสามารถทำได้ง่าย ผู้เขียนโปรแกรมควรแบ่งโปรแกรมเป็นโปรแกรมย่อย (subprograms) ในภาษา C++ เรียกโปรแกรมย่อยเหล่านี้ว่า ฟังก์ชัน (functions) ผู้เขียนโปรแกรมสามารถพัฒนาและทดสอบโปรแกรมย่อยแต่ละโปรแกรมแยกจากกัน แล้วนำโปรแกรมย่อยที่ผ่านการทดสอบแล้วไปใช้ในโปรแกรมอื่น ๆ การจัดการโปรแกรมโดยแบ่งเป็นส่วนๆ (modularization) เป็นคุณลักษณะของการพัฒนาโปรแกรมเชิงวัตถุ (object-oriented software)

5.1 ฟังก์ชันในคลังโปรแกรมภาษา C++ มาตรฐาน

คลังโปรแกรมภาษา 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)ค่า exexp(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)ค่า log10log10(2) returns 0.30103
pow (x,p)ค่า xppow(2,3) returns 8.0
sin (x)ค่าซายน์ของ x (หน่วยเรเดียน)sin(2) returns 0.909297
sqrt (x)ค่ารากที่สองของ xsqrt(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>

5.2 ฟังก์ชันที่ผู้ใช้นิยาม

แม้ว่า คลังโปรแกรมภาษา 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 จะเป็นนิพจน์ใดๆ ที่มีค่าชนิดเดียวกับชนิดข้อมูลที่ส่งออกจากฟังก์ชัน

5.3 การทดสอบฟังก์ชัน

เมื่อใดก็ตาม ที่ผู้เขียนโปรแกรมนิยามฟังก์ชันขึ้นมาเพื่อใช้งาน ผู้เขียนโปรแกรมต้องทำการทดสอบฟังก์ชันก่อน ด้วยการเขียนส่วนโปรแกรมที่ไม่ซับซ้อนให้ทำการเรียกใช้ฟังก์ชันที่พัฒนาขึ้น ส่วนของโปรแกรมดังกล่าว เรียกว่า โปรแกรมตัวขับการทดสอบ (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 ไว้ก่อนหน้า เพื่อให้การประมวลผลฟังก์ชันจบลงก่อนกำหนด โดยไม่จำเป็นต้องทำงานในส่วนคำสั่งที่เหลือในฟังก์ชันนั้น

5.4 การประกาศฟังก์ชันและนิยามฟังก์ชัน

ในตัวอย่างผ่านมา จะเห็นว่าการนิยามฟังก์ชัน (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 เข้ากับฟังก์ชันตัวใหม่ ก็จะได้โปรแกรมที่มีประสิทธิภาพมากขึ้นไปใช้งาน

5.5 ตัวแปรท้องถิ่น และ ฟังก์ชัน

ตัวแปรท้องถิ่น (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

5.6 ฟังก์ชัน void

การเรียกใช้ฟังก์ชัน ไม่จำเป็นต้องให้ฟังก์ชันทำการคำนวณเพื่อคืนค่ากลับออกมาก็ได้ ในภาษา 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

5.7 ฟังก์ชัน bool

ในในหลายๆ กรณี ผู้เขียนโปรแกรมมักออกแบบฟังก์ชันเพื่อใช้ประเมินเงื่อนไข เพื่อใช้ในคำสั่งเลือกกระทำ เช่น คำสั่ง 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 ลงตัว

5.8 ฟังก์ชันการนำเข้าและส่งออกข้อมูล

ประโยชน์ข้อหนึ่งของฟังก์ชัน คือ การห่อหุ้ม (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)

5.9 การส่งผ่านพารามิเตอร์แบบอ้างอิง

ที่ผ่านมาการส่งผ่านข้อมูลเข้าไปทำงานในฟังก์ชันเป็นการส่งผ่านข้อมูลแบบการส่งผ่านโดยค่า (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

5.10 การส่งพารามิเตอร์โดยการอ้างอิงแบบค่าคงที่

ข้อดีของการส่งพารามิเตอร์แบบการอ้างอิงมี 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 ได้ ทำได้เพียงแต่การนำค่ามาทำงานเท่านั้น ดังที่แสดงในตัวอย่าง

5.11 ฟังก์ชันแทรก

การเรียกฟังก์ชันในแต่ละครั้งจะเกิดการสำรองเนื้อที่ในหน่วยความจำเพื่อใช้สำหรับการประมวลผลฟังก์ชัน เช่น การสำรองหน่วยความจำเพื่อเก็บค่าตัวแปรท้องถิ่นที่ใช้ในฟังก์ชัน นอกจากนั้น การกระโดดไปทำงานในฟังก์ชัน ทำให้เสียเวลาเพิ่มขึ้นในการทำกระบวนการบางอย่างก่อนไปทำงานในฟังก์ชัน เช่น การกำหนดค่าของพารามิเตอร์ การสำรองพื้นที่ของตัวแปรท้องถิ่นในฟังก์ชัน การบันทึกค่าของตัวแปรต่าง ๆ และเรจิสเตอร์ และ การเก็บค่าตำแหน่งคำสั่งปัจจุบันที่เรียกฟังก์ชัน เพื่อใช้ในการกระโดดกลับมาที่คำสั่งเดิมเมื่อการทำงานของฟังก์ชันสิ้นสุดลง เป็นต้น บางครั้งเราสามารถหลีกเลี่ยงกระบวนการเหล่านี้ได้ โดยการกำหนดฟังก์ชันเป็นแบบฟังก์ชันแทรก (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 คำสั่ง

5.12 ขอบเขต

ในหัวข้อ 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

5.13 โอเวอร์โหลด

ในภาษา 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 ( ) ตัวที่สอง

5.14 ฟังก์ชัน main( )

โปรแกรมภาษา 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

5.15 ค่าอาร์กิวเมนต์โดยปริยาย

ในภาษา 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); 

เป็นการประกาศที่ไม่ถูกต้อง