ตัวแปรพอยเตอร์ (Pointers)
Pointer คือตัวแปรดัชนีที่ เก็บค่าตำแหน่งแอดเดรสของหน่วยความจำ ซึ่งตัวแปรพอยเตอร์นั้น จะมีเครื่องหมายดอกจันทร์ (*) นำหน้าเสมอ ดังตัวอย่างต่อไปนี้
int *Num;
float *GreatNum;
char *Name;
ตัวแปรพอยเตอร์มีประโยชน์ในการลดปริมาณหน่วยความจำที่ต้องใช้ในการเขียนโปรแกรม โดยการส่งข้อมูลในรูปพอยเตอร์ เข้าไปในฟังก์ชันที่โปรแกรมเรียกพร้อมกันหลายฟังก์ชัน แทนการส่งข้อมูลในรูปตัวแปรธรรมดา ซึ่งต้องใช้ตัวแปรหลายตัว
ตัวแปรพอยเตอร์มีลักษณะคล้ายตัวแปรตารางอาเรย์ แต่ที่แตกต่างกันคือ ตัวแปรตารางอาเรย์จะเก็บเฉพาะค่าต่างๆ ที่เป็นชนิดกันเดียวกับตัวแปรอาเรย์แต่ ตัวแปรพอยเตอร์จะเก็บเฉพาะค่าตำแหน่ง Address ตัวแปรเท่านั้น โดยไม่ได้มีการจัดเตรียมหน่วยความจำแบบไดนามิกส์ (Dynamic Memory Allocation) ไว้
การเข้าถึงตำแหน่งแอดเดรสทำได้โดย ใช้เครื่องหมายแอมเปอร์แซนด์ (&) ซึ่งจะแสดงให้เห็นดังตัวอย่างต่อไปนี้ ในที่นี้กำหนดให้ตัวแปร Andy อยู่ในตำแหน่ง Address ที่ 1776 ดังตังอย่างในภาพที่ 6.1

Andy = 25; // Assigning Value 25 to Andy
Fred = Andy; // Assigning Value 25 (Value of Andy) to Fred
Ted = &Andy // Assigning Value 1776 (Address of Andy) to Ted via &
อย่างไรก็ดี ถ้าต้องการให้ตัวแปร Ted สามารถ ดึงค่า จากตัวแปร Andy ได้ ให้ ใช้สัญลักษณ์ ตัวอ้างอิงโดยอ้อม (Indirect operand) ซึ่งเป็นนำเครื่องหมายดอกจันทร์ (*) นำหน้าตัวแปรที่เก็บค่าแอดเดรสตัวแปร ดังตัวอย่างในภาพที่ 6.2

Beth = *Ted // Beth มีค่าเท่ากับ ค่าที่ตัวแปร Ted ชี้ซึ่งเท่ากับ 25
ตัวพอยเตอร์จะใช้กับตัวแปรชนิดใดก็ได้โดยที่ ตัวแปรพอยเตอร์ดังกล่าวจะได้รับการกำหนดหน่วยความจำตามลักษณะชนิดตัวแปรด้วย เช่น ตัวแปรพอยเตอร์ประเภท int จะได้รับการกำหนดหน่วยความจำไว้ที่ 4 ไบต์
ตัวอย่างการกำหนด ค่า Pointer จะแสดงให้เห็นดังนี้ โดยผลการกำหนดค่าจะแสดงให้เห็นในภาพที่ 6.3 สังเกตให้ดีจะพบว่า ตำแหน่งแอดเดรสหน่วยความจำเลื่อนไป 4ไบท์ ระหว่างตัวแปร j กับ k และ ตัวแปร k กับ ptr

จากตัวแปรพอยเตอร์สู่ตัวแปรอาเรย์ (Pointer to Array)
ตัวแปรอาเรย์มีความเกี่ยวข้องสัมพันธ์กับตัวแปรพอยเตอร์คือ ชื่อตัวแปรอาเรย์ (Array identifier) จะมีค่าเทียบได้กับตำแหน่งแอดเดรสของตารางช่องแรกซึ่งเทียบได้กับตำแหน่งแรก ตัวแปรพอยเตอร์ จะมีค่าเท่ากับแอดเดรสของตัวแปรแรกที่ตัวแปรพอยเตอร์ชี้ ดังตัวอย่างต่อไปนี้
int Num[20]; int *Ptr;
การกำหนดค่าต่อไปนี้ถือว่าใช้ได้เหมือนกัน
Ptr = Num; มีลักษณะเท่าเทียมกับ Ptr = &Num[0];
ความแตกต่างจะเกิดขึ้นเมื่อ มีการกำหนดค่าตัวต่อไป โดยที่เราสามารถกำหนดค่าแอดเดรสใหม่ให้ตัวแปร Ptr ได้ทันที ขณะที่ ตัวแปร Num จะชี้ไปที่ตำแหน่งแรกของตัวแปรจำนวนเต็ม ซึ่งมี 20 ตำแหน่งเสมอ ดังนั้น ตัวแปร Ptr จึงเป็นตัวแปรพอยเตอร์ที่เปลี่ยนตำแหน่งการชี้ได้ (Variable Pointer) ขณะที่ตัวแปร Num หรือชื่อตัวแปรอาเรย์เป็นตัวแปรพอยเตอร์ที่ตำแหน่งการชี้คงที่ตายตัว (Constant pointer) ดังตัวอย่างต่อไปนี้
int Num[20]; int *y; int *Ptr;
Ptr = Num;
*(Ptr+1) จะแสดงค่า Num[1];
*(Ptr+i) จะแสดงค่า Num[i];
y = &Num[0];
y++; // ตัวแปรนี้จะชี้ไปที่ Num[1] เท่านั้น
ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการ ใช้ตัวแปรพอยเตอร์ชี้ไปที่ตัวแปรตารางอาเรย์ ซึ่งผลที่ได้จะแสดงให้เห็นในภาพที่ 6.4

ให้ผู้อ่านลองเปลี่ยนคำสั่งในบรรทัด B เป็น printf(“ptr + d = %d\n“”, i.*ptr++); ก่อนการทดสอบครั้งต่อไป เพื่อดูว่าโปรแกรมจะให้ผลออกออกมาผิดจากโปรแกรมเดิม เมื่อทดสอบแล้ว ให้ ลองแก้บรรทัด B อีกครั้งโดยแก้เป็น printf(“ptr + d = %d\n“”, i.*(++ptr));
ในการกำหนดค่าให้ตารางอาเรย์แต่ละช่องโดยใช้ตัวแปรพอยเตอร์ นั้นทำได้โดยการใช้วงเล็บครอบตัวแปรอาเรย์ที่ตามด้วยเครื่องหมายบวกและตัวเลขแสดงดัชนีพร้อมใช้เครื่องหมายดอกจันทร์ (*) นำหน้า เนื่องจากวิธีดังกล่าวจะเหมือนกับการใช้เครื่องหมาย [] และเลขดัชนี เพื่อเข้าถึงข้อมูลในตัวแปรอาเรย์ดังตัวอย่างต่อไปนี้
*(array+5) = 50 จะมีค่าเทียบเท่ากับ array[5] = 50;
ส่วนในการกำหนดและเปลี่ยนแปลงค่าโดยใช้ตัวแปรพอยเตอร์นั้นทำได้ดังโปรแกรมต่อไปนี้
void main(void)
{
int i, j=1,k=2;
int *ptr1, *ptr2; // Declare both variables as integer pointers
float value[100], result[100];
float *ptr3, *ptr4; // Declare both variables as integer pointers
prt1 = &j; // Defining ptr1 pointing to j
ptr2 = &k; // Defining ptr2 pointing to k
ptr3 = value; // ptr3 contain the address for the 1st element of value
ptr4 = &value[0]; // ptr4 contain the address for the 1st element of value
// Value manipulation
*ptr1 = *ptr1+2 /* การบวกค่าที่ตัวแปรพอยเตอร์ ptr ชี้ไป อีก 2 */
*ptr2 = *ptr1 /* กำหนดให้ค่าที่ ตัวแปร ptr2 ชี้ เท่ากับค่าที่ตัวแปร prt1 ชี้ */
k = *ptr2 /* กำหนดให้ k มีค่าเท่ากับ ค่าที่ตัวแปร ptr2 ชี้ */
}
การเปลี่ยนแปลง ตัวแปรพอยเตอร์ด้วยวิธีการทางคณิตศาสตร์ สามารถทำได้ดังตัวอย่างต่อนี้
float *ptr3, *ptr4; float table[100]; float Pi = 3.1415927;
ptr3 = &table[0]; // กำหนดให้ ptr3 เก็บค่าตำแหน่งแอดเดรส ของตาราง table ช่องแรก
ptr3++; // เปลี่ยนค่าตำแหน่งแอดเดรส ใน ptr3 เป็นแอดเดรสตาราง table ช่องที่2
*ptr3 = Pi; // ข้อมูลช่องที่ 2 ของตาราง table มีค่าเท่ากับ Pi
ptr3+=25; // กำหนดให้ ptr3 ชี้ไปที่ตำแหน่งแอดเดรสตาราง table ช่องที่ 26
*ptr3 = 2.2222; // ข้อมูล ตาราง table ช่องที่ 26 มีคาเท่ากีบ 2.2222
ptr3 = table; // กำหนดให้ ptr3 ชี้ไปที่ table ช่องแรก
for(int ii =0; ii < 100; ii++)
{
*ptr3++ = 37.0 // กำหนดให้ข้อมูลทุกช่องของ ตาราง table เท่ากับ 37
}
ptr3 = &table[0]; // ptr3 contain the address for the 1st element of value
ptr4 = &table[0]; // ptr4 contain the address for the 1st element of value
ข้อพึงระมัดระวังคือ เครื่องหมาย ++ และ -– นั้นมีลำดับความสำคัญที่สูงกว่า * ดังนั้น
*ptr++ มีค่าเท่ากับ *(ptr++) ซึ่งตัวแปรทั้ง 2 หมายถึงการเพิ่มตัวเลขตำแหน่ง แอดเดรส ไม่ใช้เพิ่มค่าที่ตัวพอยเตอร์ชี้
ส่วนกรณี *p++ = *q++ นั้น การกำหนดให้ค่าที่ตัวแปรพอยเตอร์ p มึค่าเท่ากับ ค่าที่ตัวแปรพอยเตอร์ q ชี้ จากน้นจึงมีการเลื่อนตำแหน่งแอดเดรสของตัวแปรพอยเตอร์ทั้ง 2 ไปอีก 1 ช่วง ดังตัวอย่างต่อไปนี้
*p = *q; p++; q++;
ในการเตรียมพื้นที่ตารางแบบไดนามิกให้ตัวแปรพอยเตอร์นั้นทำได้ดังนี้
กรณี C: int *ptr;
Ptr = (int *)malloc(6* sizeof(int));
กรณี C++: int *ptr;
ptr = new int[6];
ส่วนการคืนหน่วยความจำเมื่อสิ้นสุดการทำงานของโปรแกรมทำได้ดังนี้ ซึ่งต้องทำทุกครั้งเมื่อใช้คำสั่งออกจากโปรแกรม มิฉะนั้นเครื่องคอมพิวเตอร์ จะเกิดอาการค้าง (Hang) เมื่อปิดโปรแกรมเพราะไม่ได้คืนหน่วยความจำอย่างที่ควรจะทำ
กรณี C: free(ptr);
กรณี C++: delete[] ptr;
ตัวแปรพอยเตอร์และการเรียกฟังก์ชัน
การเรียกฟังก์ชันมีหลายแบบ ได้แก่
1) ฟังก์ชันที่ ไม่มีตัวแปรร่วมเป็นอาร์กิวเมนต์ และ ไม่คืนค่า (function with no parameter and no return value) ฟังกชันพวกนี้เพีบงแต่มีหน้าที่ทำงานอะไรบางอย่างตามที่ผู้เขียนโปรแกรมต้องการ เช่นกรณีฟังก์ชัน void skip3(void) เพื่อการกระโดดไปครั้งละ 3 บรรทัดเป็นต้น
2) ฟังก์ชันที่มีตัวแปรร่วมแต่ไม่คืนค่า ฟังก์ชันนี้จะทำงานตามค่าที่ตัวแปรร่วมกำหนด เช่นกรณีฟังก์ชัน void skip(int num) ซึ่งจะกระโดดครั้งละกี่บรรทัดตามจำนวนที่กำหนดลงในตัวแปร num
3) ฟังก์ชันที่ไม่มีตัวแปรร่วมแต่มีการคืนค่า ฟังก์ชันนี้มักจะให้ผู้ใช้โปรแกรมป้อนข้อมูลให้ฟังก์ชันจัดการประมวลผล ก่อนที่จะคืนค่าออกมา เช่นกรณีฟังก์ชัน float input(void) ที่จะคืนค่าหลังจากผู้ใช้โปรแกรมได้ป้อนข้อมูลให้ตามที่ฟังก์ชันสั่งให้ทำแล้ว
4) ฟังก์ชันที่มีตัวแปรร่วมและมีการ คืนค่า เช่นกรณีฟังก์ชัน float diff(float x, float y) ที่จะคืนค่าเป็นผลต่างระหว่างตัวแปร x และ ตัวแปร y
ส่วนฟังก์ชันที่มีการใช้ตัวแปรร่วม นั้น มีวิธีการเรียกอยู่ 2 แบบคือ
1) กรณี เรียกตามค่า (Call by Value) กรณีนี้การเปลี่ยนค่าตัวแปรร่วมที่เกิดขึ้นในฟังก์ชัน จะไม่มีผลต่อ ค่าตัวแปรร่วมตั้งต้นดังตัวอย่างต่อไปนี้
void skipline(int num)
{
int i ;
for(i = 0 ; i <=num-1 ; i++)
{
printf(“\n”);
}
num = 100;
}
void main(void)
{
int num1 = 60;
printf(“%d”,num1);
skipline(num1) ;
}
กรณี เรียกตามค่านี้ ค่า num มีการเปลี่ยนแปลง แต่ไม่มีผลต่อค่า num1 ใน main function
ตัวแปรร่วมในฟังก์ชันจะถือว่าเป็นกรณีเรียกตามค่า เมื่อการใช้ตัวแปรร่วมเป็นไปตามเงื่อนไขต่อไปนี้
• ตัวแปรร่วมนั้นแค่ส่งผ่านข้อมูลไปให้ฟังก์ชัน
• ตัวแปรร่วมดังกล่าวจะไม่เป้นตัวแปรที่คืนค่าหลังสิ้นสุดการทำงานของฟังก์ชัน
2) กรณี เรียกตามการอ้างอิง (Call by Reference) ซึ่งต้องมีการเรียกใช้ตัวแปรพอยเตอร์กรณีนี้การเปลี่ยนค่าตัวแปรร่วมที่เกิดขึ้นในฟังก์ชัน จะมีผลต่อ ค่าตัวแปรร่วมตั้งต้นดังตัวอย่างต่อไปนี้
void flip(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
ในการเรียกใช้ฟังก์ชัน flip นั้น จะต้องเรียกจาก แอดเดรสตัวแปร ดังนี้ flip(&a,&b)
มีบางกรณีที่มีการ เรียกตามค่าและเรียกตามการอ้างอิงในฟังก์ชันเดียวกันเช่นการแก้สมการกำลัง 2
bool quardsolve(float a, float b, float c, float *root1, float *root2)
{
float disc = (b*b) – (4*a*c);
if(disc < 0.0)
{
return false;
}
else
{
*root1 = (-b + sqrt(disc))/(2*a);
*root2 = (-b – sqrt(disc))/(2*a);
return true;
}
}
ตัวแปรดัชนี root1 และ root2 ทำหน้าที่เป็นตัวแปรร่วม Output ที่เรียกโดยการใช้อ้างอิง (Call by Reference) และ ตัวแปร a b c เป็นตัวแปรร่วม Input ที่ใช้กับการเรียกตามค่า (Call by Value) ถ้า การแก้สมการกำลัง 2 มีคำตอบให้คืนค่าเป็น true และ ค่า root1 และ root2 เป็นจำนวนจริง มิฉะนั้นให้ฟังก์ชันคืนค่า false ออกมาแทน เนื่องจาก ค่า root1 และ root2 นั้นได้รับการประกาศให้เป็น ตัวแปรร่วมอ้างอิง (Reference parameter) มีผลทำให้ฟังก์ชันต้นแบบ (Function Prototype) มีลักษณะเป็น
bool quardsolve(float, float, float, float *, float *)
เมื่อเรียกใช้งานฟังก์ชัน quardsolve ให้เรียกใช้งานฟังก์ชันดังนี้
float x1, y1; float test = quardsolve(1.0,2.0,1.0,&x,&y);
ซึ่งการเรียกใช้ฟังก์ชันดังกล้าวคล้ายคลึงกับการเรียกใช้คำสั่ง scanf
int number; scanf(“%d”, &number);
ตัวอย่างการเรียกใช้ฟังก์ชันที่มีตัวแปรพอยเตอร์จะแสดงให้เห็นดังตัวอย่างต่อไปนี้
1) ฟังก์ชันที่ มี ตัวแปรพอยเตอร์ที่แสดงในภาพที่ 6.5 นั้น จะอยู่ที่ฟังก์ชัน cal_cum โดยในฟังก์ชัน cal_sum มี ตัวแปร a และ b เป็น ตัวแปร input ขณะที่ตัวแปรพอยเตอร์ *r เป็นตัวแปร output เวลาเรียกใช้งานฟังก์ชัน cal_sum ในฟังก์ชัน main จะเรียกใช้งานดังนี้
double num1 = 2.4, num2 = 1.2,result;
cal_cum(num1,num2,&result);
แอดเดรสของตัวแปร result (&result) จะมีค่าเท่ากับชื่อตัวแปรพอยเตอร์ แม้ว่าตัวแปร result จะไม่ใช่ตัวแปรพอยเตอร์ก็ตาม
2) ฟังก์ชันที่มี ตัวแปรพอยเตอร์ที่แสดงในภาพที่ 6.6 นั้น จะอยู่ที่ฟังก์ชัน order ซึ่งใช้ตัวแปรพอยเตอร์ เป็น input และ output ทั้งคู่ ฟังก์ชัน order จะทำหน้าที่ จากเล็กไปหาใหญ่ โดยที่ ถ้า ค่าที่ตัวแปรพอยเตอร์ *small ชี้ขนาดใหญ่กว่าค่าที่ตัวแปรพอยเตอร์ *big ชี้ ให้ลงมือสับตำแหน่งการชี้ค่า

TOP |