Thaidev.com

Pointer เรื่องนี้ จําเป็นต้องรู้มั้ย
(Just Pointer!!)

ตั้งแต่ที่ผมเขียนโปรแกรมภาษา C และ C++ มา เชื่อหรือไม่ว่าผมไม่เคยใช้ตัวแปร Pointer แบบเป็นเรื่องเป็นราว คือ ใช้มันอย่างเป็นทางการเพื่อให้โปรแกรมมีประสิทธิภาพเพิ่มขึ้นเลย เนื่องจากว่าไม่เคยมีกรณีศึกษาที่จําเป็นจะต้องใช้ตัวแปร Pointer แก้ไขอย่างเห็นได้ชัด

ที่ผมอธิบายเริ่มต้นอย่างนี้ก็เพราะว่า ผู้ที่ศึกษาโปรแกรมภาษา C บางคน ยังมีข้อสงสัยเกี่ยวกับ Pointer อยู่เลยว่า ประโยชน์จริง ๆ มันคืออะไร เมื่อได้พูดคุยกันจึงได้เข้าใจว่า ถ้าเรียนภาษา C/C++ แล้ว ศึกษาไปถึงเรื่องของ Pointer ก่อนจึงจะผ่านไปศึกษาเรื่องอื่น ๆ ได้ ผมก็เลยนึกถึงสมัยที่เรียนการเขียนโปรแกรมภาษา C กับ อ. และตลอดระยะเวลา ผมแทบจะไม่ได้ใช้ตัวแปร pointer เลย และไม่ทราบว่าผู้ที่ศึกษาการเขียนโปรแกรมในปัจจุบัน จะยังจําเป็นต้องใช้ตัวแปร Pointer อยู่หรือไม่ ภาษา 4Th Generation Langauge (4thGL) นี้ ยังต้องการการเข้าถึงข้อมูลในระดับต่ำโดยใช้ Pointer อยู่หรือเปล่า ... แน่นอน นักเขียนโปรแกรม บางท่านอาจจะบอกว่า

"เมื่อแปลโปรแกรมเป็น Executable แล้ว มันจะไม่มีชื่อตัวแปรมาเกี่ยวข้อง มันจะมีแต่ Pointer"

อันนี้ก็น่ารับได้ แต่สิ่งที่เรากําลังพูดถึงอยู่ก็คือ ถ้าเราจะศึกษาการเขียนโปรแกรมภาษา C แล้วล่ะก็ จําเป็นมั้ยที่จะต้องเข้าใจเรื่องของ Pointer ก่อน จึงจะเขียนโปรแกรมได้.... และนักโปรแกรมเมอร์ภาษา C ที่พยายามศึกษาหาความรู้ด้านการเขียนโปรแกรมล่ะ จําเป็นต้องศึกษา Pointer เพราะเนื่องจากพวก Tools ในการพัฒนาโปรแกรมนั้นมีมากมายหลายตัวแล้ว ซึ่งสามารถถสร้างโค้ดโปรแกรมออกมาให้เราได้เลย โดยที่ไม่ต้องเขียนเอง และอยู่ในรูปของ OOP ด้วย เพราะฉะนั้น จําเป็นมากมั้ยที่เรายังต้องศึกษาการเข้าถึงข้อมูลระดับแอดเดรสโดยใช้ Pointer และมันยังมีประโยชน์อะไรอีก

บทความนี้ เขียนขึ้นมาก็เพื่อที่จะบอกให้ทราบถึงว่า สถานการณ์บางอย่างที่เราหยิบยกมาในตัวอย่าง หรือตัวอย่างที่นํามาเป็นสื่อใช้ในการศึกษานั้น อาจจะไม่สามารถสื่อให้ผู้ที่ศึกษาเห็นคําตอบ และเห็นประโยชน์ของสิ่งที่ต้องการศึกษาไม่ดีนัก ผมกําลังจะบอกว่า ผมเคยศึกษาเรื่องของ Pointer มาพักหนึ่งในช่วงเวลาที่เรียน และสุดท้ายก็ต้องพักไป.. เพราะตัวอย่างที่ผมศึกษานั้นเป็นลักษณะนี้

typedef struct Person{
	char *ame;
	int age;
};
Person p1,*p2;
p2=&p1; 
p1.name="somsak";
p1.age=20;
p2=&p1;

ถ้าท่านผู้อ่านเข้าใจเรื่องของ Pointer ก็คงจะหัวเราะออกมาเลยว่า ผมเอาตัวอย่างง่าย ๆ มาให้ดูทําไม.. แต่ตัวอย่างลักษณะนี้ที่ไม่ทราบถึงเหตุผลของการนําเอาตัวแปร Pointer มาใช้แก้ไขปัญหา แน่นอน ถ้าท่านผู้อ่านตอนนี้ เข้าใจในเรื่องของ Pointer ก็จะเข้าใจ แต่ถ้าผมถามท่านผู้อ่านที่เป็นภาษา C และต้องการศึกษาเรื่องของ Pointer ว่า "ทราบมั้ยครับว่าเราใช้ Pointer ในตัวอย่างนี้ เพื่ออะไร?"

คําว่าเพื่ออะไร ขยายความก็คือ Pointer จะสามารถนําไปแก้ไขปัญหาอะไรได้ในการเขียนโปรแกรมภาษา C นั่นเอง.. และเมื่อใดจะต้องประกาศตัวแปรเป็น Pointer จนผมได้ศึกษาวิชาโครงสร้างข้อมูล และต่อมาก็ได้นําเอา Pointer มาประยุกต์ใช้ในการเขียนเกม จนกระทั่งได้มาเจอปัญหาของผู้ที่กําลังศึกษาเรื่องของ Pointer อยู่ จึงอยากจะอธิบายจากปัญหาซึ่งจะเห็นได้ชัดเจนมากกว่าว่า หน้าที่ของ Pointer และเหตุผลของการใช้ Pointer เข้าไปแก้ไขปัญหา

Pointer เป็นยังไง? คืออะไร? ใช้งานยังไง?

Pointer เป็นตัวแปรประเภทหนึ่งในภาษา C ที่ต่างจากตัวแปรทั่ว ๆ ไปคือ ตัวแปรทั่ว ๆ ไปเราประกาศแบบนี้

int a,b,c;
a=b=10;
c=a;

แบบนี้ c จะเก็บ 10 เอาไว้ใช่มั้ยครับ... แต่ถ้าเขียนใหม่เป็นแบบนี้

int a,b;
int *c;
a=b=10;
c=&a;

จะเห็นได้ว่า ตัวแปร c เป็น pointer เสียแล้ว.. และให้ c=&a เพราะฉะนั้นตอนนี้ c จะไม่ได้เก็บค่า 10 แล้วครับ c จะเก็บตําแหน่งของตัวแปร a ที่อยู่ในหน่วยความจํา และทําให้ c สามารถเข้าถึงข้อมูลในตัวแปร a ได้ เหมือนกับว่าตัวแปร c ได้เข้าสิงตัวแปร a ไปแล้ว ลองดูตัวอย่างต่อไปนี้นะครับ

int a,b,*c;
a=b=10; printf("\na=%d",a); c=&a; *(int *)c=21; printf("\na=%d",a);

เมื่อเรารันโปรแกรมนี้ก็จะได้ผลแบบนี้

a=10
a=21

เอาล่ะครับ จากผลการทํางานของโปรแกรมนี้ จะอธิบายถึงหน้าที่ของ Pointer ได้อย่างไร???

ลองดูที่โปรแกรมทีละบรรทัด ๆ นะครับ...

เอาล่ะครับ ... คราวนี้ คงจะเข้าใจแล้วว่า ตัวแปร pointer ก็คือ ตัวแปรที่สามารถเข้าไปยังข้อมูลที่ตัวแปรตัวที่เข้าสิงอยู่ได้ เปรียบเทียบง่าย ๆ ลองนึกถึงหนังผีนะครับ นายเศกสรร ใส่เสื้อสีแดง สมมติว่าผี ก็คือ pointer ดังรูป

ถ้านายเสกสรร โดยผี pointer เข้าสิง ถ้าเขียนเป็นโค้ดโปรแกรมก็จะได้แบบนี้

pointer = &seksan

เพราะฉะนั้น ตอนนี้ รูปก็จะเป็บแบบนี้

เอาล่ะครับ ตอนนี้นี่เอง ถ้าผี pointer ทําการเปลี่ยนเสื้อนายเศกสรรเป็นสีเขียวก็สามารถทําได้แล้วครับ แบบนี้

*(สี *) pointer = เขียว

เพราะฉะนั้น ผลออกมาก็ได้แบบนี้

เมื่อผีออกแล้ว โดยอาจจะไปชี้ตัวอื่น หรือไปสิงตัวแปรอื่น ก็กลายเป็นว่าค่าของตัวแปรที่ถูกสิงนั้นเปลี่ยนไปนั่นเอง

เอาล่ะครับ... นี้เป็นการเปรียบเทียบให้เห็นถึงว่า ตัวแปร pointer มีลักษณะอย่างไร และ pointer ไม่ได้เข้าใจยากอย่างที่คิด แต่อาจจะเป็นเพราะการใช้สถานการณ์ที่สื่อถึงหน้าที่ของตัวแปรยังไม่ชัดเจน ลองดูตัวอย่างต่อไปนะครับ

Pointer กับโครงสร้างข้อมูล

ผู้ที่เรียนสายวิทยาการคอม วิศวกรรมคอม หรือสายคอมพิวเตอร์ ที่เคยเรียนเรื่องโครงสร้างข้อมูลมาก่อน แน่นอน เราเรียนกันหลายเรื่อง Stack , Linked List , Queue , Tree แน่นอน ถ้าใช้ภาษา C ทดสอบ หรือ implement จะต้องคุ้นเคยกับการประกาศตัวแปร * นี้แน่นอน เราอาจจะสงสัยเหมือนกับที่ผมเคยสงสัยว่า เรียน Data Structure ไปเพื่ออะไร เมื่อเราเขียนโปรแกรมที่ซับซ้อน ๆ ขึ้นมาก ๆ เราจะได้นําเอาโครงสร้างข้อมูลที่เราเรียนมาไปใช้กันจริง ๆ และใช้ Pointer ช่วยแก้ไขปัญหาได้มาก ๆ

ขอยกตัวอย่างที่เห็นได้ชัด ๆ ในเรื่องของการเขียนโปรแกรม โดยนําเอาโครงสร้างข้อมูลและตัวแปร pointer เข้ามาใช้นะครับ...

เกมไงครับ...

ท่านเคยเห็นเกมแบบวางแผน หรือเกม Sim มั้ย...............???

ในเกมประเภทนี้ จะมีตัวละครหลายๆ ตัว เช่น Warcraft และแต่ละตัวจะมีพลังเป็นของมันเอง และมีตําแหน่ง x,y เป็นของมันเอง ใช่มั้ยครับ.... นี่ล่ะครับ... ถ้าเรานํา mouse click ที่ตัวนึง มันจะมีกรอบสี่เหลี่ยมบอกและแสดงบาร์พลังออกมาให้

ตรงนี้ล่ะครับ คือ คําถามว่า เราจะรับค่าเหล่านั้นมาได้อย่างไร และจะ set สถานะของบาร์พลังของตัวละครในเกมนั้นได้อย่างไร คําตอบที่ดีที่สุด ก็คือ การใช้ Pointer เข้าสิงนั่นเอง... โครงสร้างข้อมูลที่ใช้ในเกมเหล่านี้ก็คือ Linked List นั่นเอง ให้สังเกตเกมวางแผนนะครับว่า เวลาเราสร้างคน เราสร้างได้หลาย ๆ ตัว นั่นก็คือ การ add ข้อมูลเข้าไปใน Linked List นั่นเอง และการที่เรา click เลือกตัวใดตัวหนึ่งก็คือ การที่เราให้ Pointer ที่มีโครงสร้างเดียวกันกับ Linked List ชี้ไปที่แอดเดรสของตัวนั้น ถือเป็นการเข้าสิงสมาชิกตัวนั้นใน Linked List นั่นเอง

เราลองมาดูตัวอย่างง่าย ๆ ที่ใช้อะเรย์แบบนี้นะครับ

typedef struct Person
{
	int x,y;
	int power;
};
Person a[30];
a[0].x=100;
a[0].y=120;
a[0].power=98; a[1].x=30;
a[1].y=90;
a[1].power=50; a[2].x=310;
a[2].y=230;
a[2].power=100; a[3].x=60;
a[3].y=220;
a[3].power=98;

จากข้างต้น นี่คือ การเก็บข้อมูลของคนทั้ง 30 คนเอาไว้ แต่ละคนก็จะมีค่า x,y,power อยู่ โอเคนะครับ.. เมื่อเราเก็บข้อมูลเข้าไปในตัวแปร a ทั้ง 30 สมาชิกแล้ว (ตั้งแต่ 0-29) ในตอนนี้ ค่าเหล่านี้ถูกเก็บอยู่ในหน่วยความจํา ถ้าหากว่า เราต้องการที่จะเข้าไปเปลี่ยนแปลงค่าสมาชิกตัวที่ 3 (a[2]) ล่ะครับ เราจะทําอย่างไร

เราจะต้องประกาศตัวแปร pointer แบบบ Person ขึ้นมา 1 ตัว

Person *p;

จากนั้นให้

p=&a[2];

คราวนี้ ถ้าเราต้องการดูค่า x,y เราก็จะเรียกออกมาได้โดยใช้ p.x และ p.y ซึ่งเป็นค่า x,y ของสมาชิกตัวที่ 3 ซึ่งก็คือ 310,230 นั่นเอง.. สําหรับค่าพลัง (power) ก็เช่นกัน คือ p.power นั่นเอง เช่นกัน ถ้าต้องการให้ค่าในสมาชิกตัวที่ 3 นี้เปลี่ยนแปลงไป ก็เพียงแค่เปลี่ยนค่าผ่านทาง p เพียงเท่านี้

p.x++;
p.y++;
p.power-=20;

ตอนนี้ สมาชิก a[3] ค่า x,y ก็จะเป็น 309,229 แล้ว และค่าพลังก็จะเหลือ 80 เท่านั้น ทั้งนี้เป็นเพราะว่าตัวแปร Pointer ที่ชื่อว่า p ซึ่งเป็นผีพอยเตอร์ ได้เข้าสิง และเปลี่ยนแปลงค่านั่นเอง เป็นเหตุผลเดียวกับนายเศกสรรที่เปลี่ยนจากเสื้อสีแดง เป็นสีเขียวไงครับ...

บทความนี้ ผมมีตัวอย่างแถมให้ทดสอบดูครับ เป็นโปรเจ็กต์ที่ได้จากงานวิจัยเกมแบบวางแผน เป็นการใช้ mouse ควบคุมตัวละครครับ ทดลองใช้ดูนะครับ วิธีการคลิกก็จะใช้ pointer นี่ล่ะครับ

download demo cdx_tabmove.zip
download code cdx_tabmove.txt

ในโค้ดโปรแกรม .cpp ให้ดูที่ฟังก์ชั่น cdx_DoFrame( ) นะครับ สําหรับคลาสที่ใช้ก็คือ คลาส CDXActor ครับ

โค้ดที่ใช้ตรวจจับเมาส์ เวลาที่คลิกโดนตัวละคร จะใช้แบบนี้

นอกจากนี้ ให้ท่านศึกษาและทดสอบดูนะครับ

ข้อสรุปสําหรับบทความนี้

เอาล่ะครับ... พอหอมปากหอมคอสําหรับเรื่องของ Pointer ซึ่งหยิบยกมาพูดคุยกันเพื่อให้มองภาพของ Pointer กับการเขียนภาษา C และที่สําคัญคือ การนําไปใช้ เราอาจจะศึกษาภาษามาจนกระทั่งถึงยุคของการเขียนโปรแกรมที่บริษัทผู้ผลิต OS ต่างพยายามที่จะให้ผู้บริโภค อยู่ห่าง ๆ จากความซับซ้อนของการเขียนโปรแกรมในระดับต่ำ สร้าง Component , ActiveX Control หรือสร้าง Tools ขึ้นมาช่วยให้การพัฒนาโปรแกรมง่ายขึ้น และรวดเร็วขึ้น และเมื่อเข้าสู่ยุคของ Web-based Application เต็มตัว การผลักดันให้ใช้ภาษาใน Generation Language ใหม่ ๆ ก็จะทําให้หน้าที่ของผู้ออกแบบระบบสามารถทํางานในตําแหน่งผู้พัฒนาโปรแกรมได้ด้วย (ในระดับองค์กร หรือ groupware) .. อาจจะเป็นไปได้ เพราะเราอาจจะไม่ต้องยุ่งเรื่องของ Pointer แล้วนี่.. แต่อย่างไรก็ตาม ในการพัฒนาระบบใหม่ ๆ ขึ้นมา ยังต้องการความเข้าใจในแนวคิด และอัลกอริธึมเหล่านี้อยู่เหมือนเดิม วิชา Data Structure จะต้องมีการเรียนการสอนอยู่เช่นเคย เรื่องของ Pointer ก็เช่นกัน เพราะสิ่งเหล่านั้นคือ พื้นฐานของอัลกอริธึม ที่จะพาผู้ศึกษาไปสู่ความซับซ้อนของข้อมูล และการแก้ไขปัญหาใหม่ ๆ การวิจัยโครงสร้างข้อมูลแบบใหม่ ๆ ที่ดีกว่าเดิม อีกมากมาย

บทความนี้ มีคุณความดีประการใด ขอนํามอบให้แก่อาจารย์ของผมทุก ๆ ท่านตั้งแต่อดีตจนถึงปัจจุบัน ที่ได้ประสาทวิชาให้แก่เด็กโง่ ๆ ที่เขียนเรื่อง "Pointer เรื่องนี้ จําเป็นต้องรู้มั้ย" คนนี้

กลั่นกรองออกมาเขียน อย่างหมดเปลือก

Niruth Amnuaysilp
n i r u t h x p @ t h a i d e v . c o m
Thirsday , April 10, 2003 , 12:49AM