CodeQuiz EP.35 - Pointer คืออะไร ทำไมเขียน C ต้องเจอดาวเต็มหน้าจอ
ทีมงาน Leagues of Code TH
เผยแพร่เมื่อ

สาระสำคัญ
เคยงงไหมว่าทำไม code ภาษา C ต้องมีดาว * กับ & เต็มไปหมด แล้วทำไม Python กับ JS ไม่ต้องเขียนเลย
EP นี้พาไปรู้จัก pointer แบบบ้าน ๆ ผ่านภาพล็อกเกอร์ที่โรงเรียน อ่านจบรับรองว่ามอง pointer เป็นของเล่น ไม่ใช่ของน่ากลัว
สาย C สาย C++ น่าจะเคยเปิดหนังสือมาแล้วเจอดาว * เต็มไปหมด แล้วก็พ่วงด้วยเครื่องหมาย & เป็นแพ็คคู่ จนงงไปหมดว่ามันคืออะไรกันแน่
ในขณะที่สาย Python สาย JavaScript กลับไม่ค่อยเห็นเครื่องหมายพวกนี้เลย แต่ก็ยังเขียนโปรแกรมได้อยู่ดี เอ๊ะ มันต่างกันยังไงเนาะ คำตอบสั้น ๆ คือทุกภาษามี pointer หมดอะ แค่บางภาษาซ่อนไว้ลับหลัง บางภาษาจับมือบังคับให้น้องเล่นกับมันตรง ๆ
TLDR: Pointer คือ เลขที่อยู่ ที่ชี้ไปที่ข้อมูลใน memory (หน่วยความจำของคอม) ไม่ใช่ตัวข้อมูลเอง พูดง่าย ๆ คือป้ายบอกทาง ไม่ใช่ของจริง
เปรียบแบบบ้าน ๆ ตู้ล็อกเกอร์ที่โรงเรียน

สมมุติว่าน้องเก็บกล่องข้าวไว้ในล็อกเกอร์เบอร์ 042 ที่โรงเรียน ตอนเที่ยงน้องเดินไปบอกเพื่อนว่า กล่องข้าวอยู่ในล็อกเกอร์เบอร์ 042 นะ ไปหยิบให้หน่อยดิ สิ่งที่น้องส่งให้เพื่อนคือ เลขล็อกเกอร์ ไม่ใช่ กล่องข้าว ตัวจริง
นี่แหละ pointer ในภาษาคน เลขบอกที่อยู่ของของ ไม่ใช่ของเอง
จับคู่กันแบบนี้ : เลข 042 = address (ที่อยู่ใน memory) , กล่องข้าวข้างใน = value (ข้อมูลจริง) , เพื่อนเดินไปเปิดล็อกเกอร์ = dereference (การเอา pointer มาเปิดดูของข้างใน) ถ้าน้องส่งกล่องข้าวให้เพื่อนตรง ๆ เพื่อนจะได้กล่องคนละใบกับน้อง แต่ถ้าน้องส่งเลขล็อกเกอร์ไป น้องกับเพื่อนเข้าถึงกล่องเดียวกันได้ เด็ดอะ
ทำไมต้องมี pointer ตั้งแต่แรก

เหตุผลแรกคือ ความเร็ว สมมุติน้องมี array (รายการช่องเรียงต่อกันใน memory) ขนาด 10 ล้านช่อง ถ้าจะส่งให้ function อื่นใช้แล้วต้อง copy ทั้งก้อน คงเปลือง RAM สุด ๆ ส่งแค่เลข address ของช่องแรกไปให้แทน function ไหนอยากใช้ก็เปิดล็อกเกอร์เอง เซฟทั้ง RAM เซฟทั้งเวลา
เหตุผลที่สองคือ การแชร์ของ สมมุติเพื่อน 3 คนต้องอัปเดตคะแนนชุดเดียวกัน ถ้าทุกคนถือสำเนาคนละใบ แก้ไปก็ไม่ตรงกันสักใบ ส่ง pointer ให้ทุกคนชี้ไปที่คะแนนชุดเดียว แก้ที่ใครก็เห็นเหมือนกันหมด เนียนใช่ป่ะ
เหตุผลที่สามคือ โครงสร้างข้อมูลแบบยืดหยุ่น เช่น linked list (รายการที่แต่ละกล่องโยงกันด้วย pointer) หรือ tree (โครงสร้างต้นไม้ของข้อมูล) สร้างไม่ได้เลยถ้าไม่มี pointer เพราะแต่ละ node (กล่องข้อมูลหนึ่งจุดในโครงสร้าง) ต้องชี้ไปหากันได้
แล้วทำไม Python กับ JS เหมือนไม่มี pointer

จริง ๆ มีอะ แค่ภาษาบังคับให้เล่นแบบไม่ต้องเห็นเอง ใน Python ทุกตัวแปรที่เก็บ object พวก list, dict, instance หลังบ้านคือ reference (ตัวชี้ไปที่ object) ดี ๆ นี่เอง เลยเป็นที่มาของอาการคลาสสิก ส่ง list เข้า function แล้ว function ไปแก้ list นั้น ตัวข้างนอกก็โดนแก้ตามเพราะมันชี้ไปที่ก้อนเดียวกัน เซ็งใช่ป่ะ
ภาษาแบบนี้เรียก managed memory คือมี Garbage Collector (โปรแกรมที่คอยตามเก็บ memory ที่ไม่มีใครใช้แล้ว) คอยลบของให้อัตโนมัติ คนเขียน code เลยไม่ต้องจ้องเลข address กับ * และ & ให้เวียนหัว
ภาษาแบบ C/C++ ตรงข้ามเลย ให้น้องจัดการ memory เองทั้งหมด เลยเร็วและประหยัด แต่ถ้าลืมคืนของก็เกิด memory leak (หน่วยความจำรั่วเพราะลืมปล่อย) เผลอเปิดล็อกเกอร์ที่ไม่ใช่ของเราก็เกิด segmentation fault (โปรแกรมเด้งเพราะไปแตะที่ที่ไม่มีสิทธิ์) ได้ง่ายมาก
ดู code จริงสักนิด

123456789#include <stdio.h>int main() {int score = 100;int *ptr = &score; // & = ขอเลขล็อกเกอร์ของ scoreprintf("%d\n", *ptr); // * = เปิดล็อกเกอร์ตามเลขนี้ ได้ 100*ptr = 200; // เข้าไปแก้ของในล็อกเกอร์printf("%d\n", score); // ได้ 200 เพราะมันคือกล่องเดียวกันreturn 0;}
เกร็ดเด็ด ๆ ที่หลายคนไม่รู้

- pointer มีขนาดคงที่ตามเครื่อง บนเครื่อง 64-bit คือ 8 byte (1 byte = 8 bit) ไม่ว่าจะชี้ไปที่ int ที่ struct หรือที่ array ใหญ่แค่ไหนก็เท่าเดิม เพราะมันเก็บแค่ เลขล็อกเกอร์ ไม่ได้เก็บของ
- pointer ที่ชี้ไปที่ ที่ว่าง คือ NULL ใน C คือ None ใน Python ถ้าเผลอ dereference NULL ก็คือเปิดล็อกเกอร์ที่ไม่มีอยู่ โปรแกรมเด้งทันที
- pointer สามารถชี้ไปที่ pointer ตัวอื่นได้อีก เป็นชั้น ๆ เลยเกิด int **ptr int ***ptr ที่หลายคนเห็นแล้วเวียนหัว แต่จริง ๆ ก็แค่ เลขล็อกเกอร์ของล็อกเกอร์ที่เก็บเลขล็อกเกอร์อีกที
สรุปแบบไม่ต้องจำ
- pointer = เลขที่อยู่ของของใน memory ไม่ใช่ของจริง
- & คือ ขอเลขล็อกเกอร์ , * คือ เปิดล็อกเกอร์
- Python กับ JS ก็ใช้ pointer ลับหลังตลอด แค่ไม่บังคับให้คนเขียนรู้
- ส่ง pointer = แชร์ของ , copy ค่า = ทำสำเนา
- เห็นดาวเต็มจอใน C ไม่ต้องกลัว มันแค่บอกว่าตอนนี้เรากำลังเล่นกับเลขล็อกเกอร์อยู่
น้องไม่ต้องจำกฎทุกข้อ จำแค่ภาพล็อกเกอร์ก็พอ เมื่อไหร่งงให้ถามตัวเองว่า ตอนนี้ฉันถือเลขล็อกเกอร์ หรือฉันถือกล่องข้าวอยู่ แค่นี้ก็เริ่ดละ