บทที่ 6
ค่าคงที่ (Constant) และการอ้างอิง (Reference)
วัตถุประสงค์เชิงพฤติกรรม (Behavioral Objectives)
หลังจากศึกษาจบบทเรียนนี้แล้ว นักศึกษาจะมีความสามารถดังนี้
1.ศึกษาค่าคงที่ (Constant)
2.ยกตัวอย่างการกำหนดค่าคงที่(#define)
3.อธิบายและยกตัวอย่าง”ค่าหลัก”const
4.เข้าใจตัวดำเนินการแอดเดรส
5.อธิบายและแสดงตัวอย่าง .การอ้างอิง (Refernce)”
6.ยกตัวอย่างตัวบ่งชี้(Pointer)
7.อธิบายและยกตัวอย่างอาร์เรย์
8.ยกตัวอย่างการส่งผ่านค่า
9.จัดบอร์ดเชิงปฏิบัติการ”ค่าคงที่และการอ้างอิง”
10.สนทนาเชิงปฏิบัติการ”การส่งผ่านค่า”
11.อธิบายคำศัพท์ได้ 12 คำ
ในการเขียนโปรแกรมต่าง ๆ ย่อมมีการส่งค่าไปมา เพื่อแสดงผล เพื่อการคำนวณ ฯลฯ โดยเฉพาะกับการทำงานของฟังก์ชันต่าง ๆ ที่เราสร้างขึ้น ซึ่งค่าที่ส่งผ่านไปมานั้น อาจจะเป็นได้ทั้งค่าที่ระบุได้แน่นอนหรือค่าคงที่ กับค่าที่อ้างอิงถึงแหล่งที่อยู่ของวัตถุนั้น ๆ
ในบทนี้จะกล่าวถึงความหมายของค่าคงที่และค่าอ้างอิง โดยนำไปใช้ในการส่งผ่านค่าที่เรียกกันว่า Pass by Value และ Pass by Reference ซึ่งใช้กันมากในการเขียนโปรแกรมที่มีการทำงานในส่วนของการเรียกผ่านฟังก์ชัน
ค่าคงที่ (Constant)
เรื่องชื่อสัญลักษณ์ (Symbolic Name) สำหรับค่าคงที่ ชื่อสัญลักษณ์จะช่วยให้รู้ว่าค่าคงที่ตัวนั้น ๆ มีความหมายเป็นอะไร และช่วยอำนวยความสะดวก ในกรณีที่โปรแกรมมีการใช้ค่าคงที่ตัวใดตัวหนึ่งซ้ำ ๆ กันในหลาย ๆ ที่ และเมื่อไรที่ต้องการเปลี่ยนค่าของค่าคงที่นั้นก็สามารถทำได้ง่าย ๆ โดยทำการแก้ไขในจุดเดียว
ค่าคงที่ (Constant) คือ ทุกนิพจน์ที่มีค่าแน่นอน (Fixed Value) อาจเป็นจำนวนเต็ม ทศนิยม ตัวอักขระหรือสายอักขระ
จำนวนเต็ม
1776
707
-273
ค่าคงที่ที่เป็นตัวเลขนี้เป็นเลขฐานสิบ จำไว้ว่าในการกำหนดไม่จำเป็นต้องใช้ (“ ”) หรืออักขระพิเศษอื่นช่วย เช่น เมื่อไรที่เขียน 1776 ในโปรแกรม ก็จะรู้ได้ว่านั่นคือค่า 1776
ข้อมูลเพิ่มเติมเกี่ยวกับเลขฐาน 10 C++ ยังมีการใช้เลขฐาน 8 และฐาน 16 ด้วย ถ้าจะแสดงเลขฐาน 8 จะต้องเติมเลข 0 (เลขศูนย์) หน้าจำนวนนั้น และเมื่อจะใช้เลขฐาน 16 ต้องเติม 0x (เลขศูนย์ ,x) โดยแสดงตัวอย่างต่อไปนี้
75 // decimal
0113 // octal
0x4b // hexadecimal
ซึ่งทั้งหมดต่างมีค่าเหมือนกันคือ 75
ทศนิยม
ประกอบด้วยจำนวนเต็มและจุดทศนิยม ค่าทางวิทยาศาสตร์ (e) ซึ่งมีค่าเป็น 10 ยกกำลัง
3.14159 // 3.14159
6.02e23 // 6.02 x 10
1.6e-19 // 1.6 x 10
3.0 // 3.0
ตัวอักขระ และสายอักขระ
ก็คือ ที่ไม่ใช่ตัวเลข เช่น
‘z’
‘p’
“Hello world”
“How do you do?”
2 ประโยคแรก เป็นตัวอักษรตัวเดียว และ 2 ประโยคหลังเป็นค่า String ที่รวมตัวอักษรหลายตัวเข้าด้วยกัน ที่แตกต่าง คือ ตัวอักขระ (Character) ใช้เครื่องหมาย Single Quotes (‘ ’) สายอักขระ (String)ใช้เครื่องหมาย Double Quotes (“ ”)
เมื่อกำหนดค่าแบบอักขระและสายอักขระ จำเป็นต้องมีการใช้เครื่องหมายทุกครั้ง เช่น
x
‘x’
เมื่อ x คือ ตัวแปรตัวหนึ่ง ส่วน ‘x’ คือ ตัวอักขระที่มีค่าคือตัว ‘x’
การกำหนดค่าคงที่ (#define)
เราสามารถกำหนดชื่อค่าคงที่ของเรา ซึ่งใช้อยู่ประจำและไม่มีการเปลี่ยนแปลงค่า โดยการใช้ #define ด้วยรูปแบบต่อไปนี้
#define identifier value
ตัวอย่าง การประกาศ
#define PI 3.14159265
#define NEWLINE ‘\n’
#define WIDTH 100
จากตัวอย่างมีการกำหนดค่าคงที่ 3 ตัว โดยจะสังเกตว่าไม่จำเป็นต้องจบด้วยเครื่องหมาย (;) ส่วนมากจะกำหนดไว้ส่วนต้นของโปรแกรมเพื่อป้องกันการสับสน ซึ่งจะยกตัวอย่างการนำค่าคงที่ PI และการขึ้นบรรทัดใหม่มาใช้ ดังนี้
circle= 2 * PI * r;
cout << NEWLINE;
คำหลัก const
เกี่ยวกับคำหลัก const สามารถใช้กำหนดค่าให้กับตัวแปรที่ไม่สมควรทำการเปลี่ยนค่าคำสั่ง const มีข้อดีที่เหนือกว่า คือ ประการแรกในการสร้างชื่อสัญลักษณ์สามารถกำหนดชนิดข้อมูลของค่าคงที่ได้นั้นเองตามต้องการ (คำสั่ง #define ไม่สามารถทำได้) ประการที่ 2 สามารถใช้กฎของกรอบ (Scoping Rule) เพื่อกำจัดอาณาบริเวณของชื่อสัญลักษณ์ให้อยู่แต่เฉพาะในฟังก์ชันใดฟังก์ชันหนึ่ง หรือในไฟล์ใดไฟล์หนึ่งเท่านั้น ประการที่ 3 สามารถใช้ const กับชนิดข้อมูลสืบทอดอย่างเช่นอาร์เรย์ได้ เป็นต้น โดยจะยกตัวอย่างการใช้งาน const ดังนี้
const int width = 100;
const char tab = ‘\t’;
const zip (float a[], cont int size)
ในกรณีที่ยกมานั้นเป็นตัวแปรและตัวพารามิเตอร์ของฟังก์ชัน วัตถุอาจได้รับการประกาศเป็นค่าคงที่ได้ด้วย เช่น
const Rational pi (22,7);
อย่างไรก็ดี เมื่อมีการทำเช่นนี้ คอมไพเลอร์ C++ จะจำกัดการเข้าถึงฟังก์ชันสมาชิกของวัตถุ เช่น คลาส Rational ที่ได้รับนิยามไว้ก่อนหน้านี้ ดังนั้น ฟังก์ชัน print() ไม่อาจได้รับการเรียกใช้ได้สำหรับวัตถุนี้
pi.print(); // error: call not allower
ในความเป็นจริงถ้าไม่แก้ไขการนิยามคลาสของเรา ฟังก์ชันสมาชิกที่สามารถถูกเรียกสำหรับวัตถุ const เท่านั้นที่จะเป็นคอนสตักเตอร์และดีสตรักเตอร์ เพื่อเป็นการเอาชนะข้อจำกัดนี้ เราต้องประกาศฟังก์ชันสมาชิกที่ต้องการให้สามารถใช้ร่วมกับวัตถุ const เหล่านั้นเป็นค่าคงที่ด้วย
ฟังก์ชันจะได้รับการประกาศเป็นค่าคงที่ด้วยการแทรกคำหลัก const ไว้ระหว่างรายการพารามิเตอร์กับตัวฟังก์ชัน เช่น
void print() const { cout << num << ‘/’ << den << endl; }
การแก้ไขการนิยามฟังก์ชันนี้จะยอมให้ทำการเรียกใช้สำหรับวัตถุคงที่ได้
const Rational pi (22,7);
pi.print(); // o.k.now
ตัวดำเนินการแอดเดรส
เมื่อมีการประกาศตัวแปรเกิดขึ้นจะมีคุณสมบัติพื้นฐานที่เกี่ยวข้องกับตัวแปรเกิดขึ้น 3 ประการ ได้แก่ ชื่อของตัวแปร ชนิดของตัวแปร และตำแหน่งที่อยู่ในหน่วยความจำ เช่น การประกาศประโยคนี้
int n;
สิ่งที่เกิดขึ้น ได้แก่ ชื่อของตัวแปร คือ n ชนิดของตัวแปร คือ int และตำแหน่งเลขที่อยู่ คือ ตำแหน่งในหน่วยความจำที่ค่าของ n จะถูกนำไปเก็บไว้ สมมติว่าตรงกับเลขที่ 0x3fffd14 (สัญลักษณ์นี้คือเลขฐาน 16) จะทำให้สามารถมองเห็นภาพของ n ได้
การดึงค่าข้อมูลมาใช้ทำได้โดยการอ้างถึงชื่อตัวแปร เช่น สามารถพิมพ์ค่าของ n ด้วยประโยค cont << n;
การเข้าถึงเลขที่อยู่ของตัวแปรทำได้โดยอาศัยตัวดำเนินการแอดเดรส (Address Operator) ซึ่งเขียนแทนด้วยเครื่องหมาย & เช่น เราสามารถพิมพ์เลขที่อยู่ของตัวแปร n ได้ด้วยประโยค cont << &n;
ตัวดำเนินการแอดเดรส & จะดำเนินการบนชื่อของตัวแปรเพื่อกำหนดตำแหน่งเลขที่อยู่ให้ตัวดำเนินการนี้มีลำดับความสำคัญ ระดับ 15 ซึ่งเป็นระดับเดียวกับตัวดำเนินการ NOT (!) และตัวดำเนินการเพิ่มก่อน (++)
ตัวอย่างที่ 6-1 การพิมพ์ค่าตัวชี้
ตัวอย่างนี้แสดงวิธีการพิมพ์ค่าของตัวแปรและเลขที่อยู่ของตัวแปร
การแสดงเลขที่อยู่ของตัวแปรด้วยวิธีนี้ไม่มีประโยชน์มากนัก ตัวดำเนินการแอดเดรส & มีการนำไปใช้งานที่สำคัญมากกว่านี้
การอ้างอิง (Reference)
การอ้างอิง คือ ชื่อที่ใช้อ้างถึงตัวแปรอีกตัวหนึ่ง จะได้รับการประกาศด้วยการใช้ตัวดำเนินการ & ต่อท้าย แบบชนิดข้อมูล (Type) ของตัวอ้างอิงนั้น ๆ
ตัวอย่างที่ 6-2 การใช้การอ้างอิง
โปรแกรมนี้ตัวแปร r ได้รับการประกาศเป็นตัวอ้างอิงกับตัวแปร n
ตัวระบุทั้งสอง คือ n และ r มีชื่อต่างกันแต่ใช้แทนตัวแปรตัวเดียวกัน ทั้งคู่จะมีค่าเหมือนกันตลอด การลดค่า n ทำให้ทั้ง n และ r มีค่าเป็น 32 การเพิ่มค่า r สองเท่าก็ส่งผลให้ทั้ง n และ r มีค่าเป็น 64 เหมือนกัน
ตัวชี้ (Pointer)
นอกจากตัวดำเนินการอ้างอิง & ที่ทำหน้าที่ให้เลขที่อยู่ในหน่วยความจำของตัวแปร (Address) ที่มีเครื่องหมายนี้กำกับไว้ เรายังสามารถใช้เก็บเลขที่อยู่ของตัวแปรอื่นได้ด้วยตัวแปรที่ใช้เก็บเลขที่อยู่นี้เรียกว่า ตัวชี้ (Pointer) เช่น ถ้าตัวแปรเป็นชนิด int ตัวแปรที่ทำหน้าที่เป็นตัวชี้ต้องเป็นชนิดจำนวนเต็ม และเขียนกำกับไว้ด้วย int*
ตัวอย่างที่ 6-3 ค่าของตัวชี้ คือ เลขที่อยู่ในหน่วยความจำ
ตัวแปรตัวชี้ p และนิพจน์ &n เป็นชนิดเดียวกัน (ตัวชี้ไปที่ int) และมีค่าเหมือนกัน คือ 0x0012FF7C เพราะว่าทั้งคู่ทำหน้าที่เก็บเลขที่อยู่ของตัวแปร เลขที่อยู่นั้นได้เก็บอยู่ที่ตำแหน่ง 0x001FF78 ของหน่วยความจำ
บ่อยครั้งที่เราใช้ตัวชี้ p ตามลำพังเพื่อดึงค่าข้อมูลที่ตำแหน่ง p ชี้อยู่มาใช้งาน การทำเช่นนี้เรียกว่า Derefence
ตัวอย่างที่ 6-4 การใช้เครื่องหมายดอกจัน (*) เป็นตัวดำเนินการตัวชี้
อาร์เรย์ (Array)
อาร์เรย์ คือ ลำดับของวัตถุที่เป็นชนิดเดียวกันจำนวนหนึ่ง โดยที่วัตถุเหล่านั้นจะได้รับการเรียกว่า สมาชิก (Elements) ของอาร์เรย์ และมีหมายเลขกำกับให้ตามลำดับดังนี้ 0,1,2,3 … หมายเลขเหล่านี้ได้รับการเรียกว่า ค่าดรรชนี (Index Values) หรือดรรชนีกำกับ (Subscript) ของอาร์เรย์ คำว่าดรรชนีกำกับถูกนำมาใช้เพราะมีการอ้างถึงลำดับทางคณิตศาสตร์ ดังนั้น อาร์เรย์จึงได้เขียนด้วยดรรชนีกำกับดังนี้ a0,a1,a2,… ตัวเลขเหล่านี้ หมายถึง ตำแหน่งของสมาชิกในอาร์เรย์ ด้วยเหตุนี้ จึงทำให้เข้าถึงข้อมูลในอาร์เรย์ได้โดยตรง
โปรแกรมที่นำไปใช้ส่วนใหญ่จะใช้ข้อมูลอาร์เรย์ เหตุผลหนึ่งที่ทำให้ข้อมูลอาร์เรย์มีประโยชน์อย่างมาก ก็คือ สามารถใช้ชื่อข้อมูลเพียงชื่อเดียวร่วมกับตัวแปรที่แสดงค่าดรรชนีสมาชิกต่าง ๆ ในอาร์เรย์ได้ แทนการร้องขอข้อมูลเหล่านั้นด้วยชื่อตัวแปรที่แตกต่างกันหลายชื่อซึ่งทำให้การเขียนโปรแกรมง่ายและสะดวกอย่างยิ่ง
รูปแบบการประกาศอาร์เรย์
type array-name [array-size]
ตัวอย่างที่ 6-5 การพิมพ์ตามลำดับในแถวคำสั่ง
โปรแกรมนี้อ่านข้อมูลเข้ามาจำนวน 4 จำนวน แล้วสั่งพิมพ์แบบกลับข้อมูล (Reverse)
การประกาศ double a[4] หมายถึง ประกาศให้ a เป็นข้อมูลอาร์เรย์ มีสมาชิก 4 ตัว เป็นชนิด double ประโยคคำสั่งลูป for แรกใช้สำหรับให้ผู้ใช้ป้อนข้อมูลทศนิยมให้กับสมาชิกทั้ง 4 ตัวของอาร์เรย์ด้วย cin ซึ่งหมายถึง นำข้อมูลเข้า ประโยคคำสั่งลูป for ที่สองใช้สำหรับพิมพ์หรือแสดงรายการข้อมูลของอาร์เรย์ที่กลับข้อมูลแล้ว
นอกจากนี้ ยังสามารถกำหนดค่าเริ่มต้นให้กับอาร์เรย์ได้ดังตัวอย่างต่อไปนี้
float a[4] = {22.2, 44.4, 66.6, 88.8} ;
หรือการควบคุมขนาด array-size ด้วยการกำหนดค่าคงที่ เช่น
const int size = 100;
…
double a[size];
หมายถึง a เป็นอาร์เรย์ที่ประกอบด้วยสมาชิกจำนวน 100 ตัว ซึ่งการกำหนดแบบนี้จะช่วยให้โปรแกรมยืดหยุ่นยิ่งขึ้น
การส่งผ่านค่า
ฟังก์ชันที่เคยกล่าวมาแล้วนั้น พารามิเตอร์ที่ใช้ฟังก์ชันเป็นแบบ ส่งผ่านโดยค่า (Pass by Value) หมายถึง นิพจน์ที่ใช้ตรงจุดเรียกฟังก์ชันจะได้รับการคำนวณให้ได้ผลลัพธ์ก่อน จึงส่งฟังก์ชันจะเกิดขึ้น เช่น การเรียกใช้ฟังก์ชัน cube(x) ถ้า x มีค่าเป็น 4 ค่า 4 จะถูกส่งไปยังตัวแปรเฉพาะที่ n ก่อนที่คำสั่งในฟังก์ชันจะทำการปฏิบัติ เนื่องจากค่า 4 ใช้เฉพาะภายในฟังก์ชันเท่านั้น ตัวแปร x ไม่มีผลกระทบที่เกิดขึ้นจากฟังก์ชัน ดังนั้น ตัวแปร x จึงเป็นพารามิเตอร์ที่ใช้อ่านเท่านั้น
กระบวนการการส่งผ่านโดยค่ายอมให้ตัวพารามิเตอร์จริงในฟังก์ชันอยู่ในลักษณะของนิพจน์ที่มีความซับซ้อนโดยไม่จำเป็นว่าจะต้องเป็นนิพจน์ง่าย ๆ เช่น ฟังก์ชัน cube อาจถูกเรียกในลักษณะนี้ cube(3) หรือ cube(2*sqrt(x)-cube(3)) ซึ่งในแต่ละกรณี นิพจน์ภายในวงเล็บจะต้องได้รับการคำนวณให้ได้ผลลัพธ์สุดท้ายออกมาก่อน จึงจะส่งค่านั้นไปยังฟังก์ชัน
การส่งผ่านโดยค่านั้นเป็นวิธีการสื่อสารที่ปกติจะใช้กับฟังก์ชัน ซึ่งจะทำให้ฟังก์ชันมีความเป็นอิสระในตัวเองมากขึ้น เพราะเป็นการป้องกันผลกระทบรอบข้าง อย่างไรก็ตาม มีบางสถานการณ์ซึ่งฟังก์ชันต้องการเปลี่ยนค่าของพารามิเตอร์ที่ส่งมายังฟังก์ชัน วิธีการนี้สามารถทำได้ โดยการส่งผ่านค่าโดยการอ้างอิง (Pass by Reference)
การส่งค่าโดยการอ้างอิงนั้นทำได้ง่าย ๆ ด้วยการเติมเครื่องหมาย & (Ampersand) ต่อท้ายที่ชนิดของข้อมูลในส่วนของรายการพารามิเตอร์ของฟังก์ชัน การทำเช่นนี้จะทำให้ตัวแปรเฉพาะที่อ้างอิงกับพารามิเตอร์จริงที่ส่งค่ามาให้ และเมื่อมีการเปลี่ยนแปลงใด ๆ เกิดขึ้นกับตัวแปรเฉพาะที่ในฟังก์ชัน ก็จะส่งผลให้พารามิเตอร์จริงมีการเปลี่ยนแปลงตามไปด้วย
ฟังก์ชัน swap() นี้มักใช้ในการเรียงลำดับข้อมูล
//Swap x and y so that each ends up
//with the other’s value:
void swap (float& x, float& y)
{
float temp = x;
x = y;
y = temp;
}
จุดประสงค์หลักของฟังก์ชันนี้ ก็คือ การแลกเปลี่ยนค่าของวัตถุ 2 จำนวนที่ผ่านมายังฟังก์ชันการทำสิ่งนี้ให้สำเร็จจะต้องอาศัยการประกาศพารามิเตอร์แบบแผน x และ y ให้เป็นตัวแปรแบบตัวแปรอ้างอิง ซึ่งทำให้ด้วยการใช้เครื่องหมาย & ดังนี้ float& x, float& y ตัวดำเนินการอ้างอิง & ทำให้ x และ y มีความหมายตรงกันสำหรับพารามิเตอร์จริงที่ส่งมาที่ฟังก์ชัน
ตัวอย่างที่ 6-6 โปรแกรมสำหรับทดสอบฟังก์ชันและแสดงผลลัพธ์จากตัวอย่างการ run
หมายเหตุ : การประกาศฟังก์ชัน
void swap (float, float&);
ต้องรวมตัวดำเนินการอ้างอิง & สำหรับพารามิเตอร์อ้างอิงแต่ละตัว ถึงแม้ว่าไม่มีการระบุตัวพารามิเตอร์
โปรแกรมเมอร์ C++ บางคนเขียนตัวดำเนินการอ้างอิง & เป็นแบบนำหน้าตัวพารามิเตอร์ เช่น void swap (float &x float &y) แทนที่จะเขียนต่อจากชนิดของข้อมูลเหมือนในตัวอย่าง ซึ่งก็ไม่ผิด เพราะคอมไพเลอร์จะรับ float& x, float &x, float & x หรือ float&x
ตัวอย่างที่ 6-7 การส่งผ่านโดยค่าและการส่งผ่านโดยการอ้างอิง
ตัวอย่างนี้แสดงความแตกต่างระหว่างการส่งผ่านโดยค่าและการส่งผ่านค่าโดยการอ้างอิง
เมื่อเรียกฟังก์ชัน f(a,b) จะส่งค่า a ไปให้ตัวแปร x แบบส่งผ่านโดยค่า และส่งค่า b โดยอ้างอิงกับตัวแปร y ดังนั้น x เป็นตัวแปรเฉพาะที่ ซึ่งได้รับค่าจาก a คือ 22 ในขณะที่ y เปรียบเสมือนกับตัวแปร b ซึ่งมีเท่ากับ 33 การทำงานของฟังก์ชัน คือให้ x มีค่าเท่ากับ 88 แต่ค่าของ x นี้จะไม่ส่งผลกระทบกลับมาที่ตัวแปร a เมื่อ y มีค่าเท่ากับ 99 ซึ่งหมายถึง b มีค่าเท่ากับ 99 ด้วย ดังนั้น เมื่อการทำงานของฟังก์ชันสิ้นสุดลง ตัวแปร a ยังคงมีค่าเท่าเดิม คือ 22 ในขณะที่ b มีค่าเปลี่ยนไปตาม y คือ 99
ไม่มีความคิดเห็น:
แสดงความคิดเห็น