อธิบาย Pointers ภาษาคนธรรมดา

อ่านแล้วไม่ตาลอย หวังว่านะ
Pointers in simple cover

มือให้หัดโค้ด เวลาไป search ใน internet เกี่ยวกับ Pointers ในภาษาโปรแกรมมิ่ง เราจะเจอแต่ความ abstract ดูแล้วตาลายไปหมดเลย เลื่อน ๆ ไปแล้วแล้วก็ไม่ได้เข้าใจมากขึ้นเท่าไร แต่ไม่เป็นไร วันนี้ผมจะมาเล่าให้ฟังแบบคนธรรมดาคุยกันครับ

Memory Address

ใน memory ของคอมพิวเตอร์เรา ประกอบด้วย block ซึ่งเราจะแทนมันด้วยถัง ถังเยอะ ๆ เลย แต่ละถังจะแปะเลข address เอาไว้อยู่ เอาไว้ใช้ระบุความแตกต่าง และแต่ละถังเก็บ value ได้ก้อนเดียว

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

ตัวอย่างเช่น เวลาประกาศ i = 1 มันก็จะไปหยิบถังมาใบนึง ใส่เลข 1 เข้าไป แล้วก็จดใส่กระดาษไว้ว่า ค่าของตัวแปรชื่อ i จะเก็บไว้กับถังที่มี address นี้นะ

Memory address attribute in simple

เวลาเราอยากได้ค่าจากตัวแปร iเราก็จะหยิบกระดาษมาดู ว่าเราจะต้องไปหาถังเลขไหน แล้วหยิบ value ในถังมาดูเลย เช่น ถ้าตั้งไว้ว่า i = 1เวลา print i ออกมา ก็ได้ค่าเป็น 1 อันนี้ปกติ

เหมือนกันแต่ไม่ใช่

ถ้าสมมติเราเขียนว่า j = iแล้วเอามาเทียบ value กัน มันจะบอกว่าค่าเท่ากัน แต่ถ้าถามว่ามันคือตัวเดียวกันไหม อันนี้ไม่ใช่

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

ทำให้ตอนนี้เรามี 2 ถัง ที่ของข้างในหน้าตาเหมือนกัน ถามว่า i “เท่ากับ” j ไหม ใช่

How memory address working in simple

แต่ว่าใน 2 ถังนี้ มันกำลังหมายถึง ของก้อนเดียวกันไหม ไม่ ถ้าเราแก้ค่าของในถัง i ค่าในถัง j จะไม่เปลี่ยนตาม

ถ้าอยากให้เรียกถัง j แล้วสามารถพาไปดูของในถัง i ได้ ต้องทำยังไงดี งั้นเราต้องใช้ address เพื่อให้มันนำทางไปหาถังที่เก็บค่าของ i ให้ได้

สิ่งนี้แหละ คือ pointer

Pointers

Pointers เป็นตัวแปรที่จะเก็บ address ใน memory ในที่นี้คือ เลขบนถัง แทนที่ปกติจะเก็บ value ทั่วไป

พอเรารู้ address แล้ว มันก็พาเราไปหาถังที่เราต้องการ และหยิบของข้างในดูได้ ว่ามันเก็บ value อะไรไว้

ที่เราต้องทำก็แค่ไปหา address ของตัวแปร i ออกมาใส่ใน j ให้ได้ แล้วทีนี้ทำยังไง

ถ้าในภาษา C หรือ Golang เราใส่ & เติมไปข้างหน้าตัวแปรได้เลย จะได้ค่า address บน memory ของตัวแปรนั้น ๆ มา อย่างเช่น j = &i

ทีนี้ตัวแปร j จะแปลงร่างเป็น pointer เรียบร้อย พร้อมเอาไปใช้งาน ถ้า print ออกมา จะได้ค่าเป็นเลข address เลย

How Pointers works

ตอนนี้เราหาถังเจอแล้ว แล้วทำยังไงถึงจะหยิบของในถังออกมาดูได้ เราสามารถทำได้โดยใส่ * ด้านหน้า pointer จะได้เป็น *j พอเอาไป print เราจะได้ของที่เก็บไว้ใน i แล้ว เย่

ด้วยสิ่งนี้ เวลาเราแก้ไข value ผ่าน pointer j ค่าในตัวแปร i จะเปลี่ยนไปโดยปริยาย เพราะทุกคนกำลังมองมาที่ถังเดียวกัน

ทำทำไม?

แล้วทำไมต้องทำขนาดนี้ด้วยล่ะ ถ้าอยากเรียกใช้ หรือแก้ไขค่าใน i เราไม่เรียกตรง ๆ เลยไม่ดีกว่าเหรอ

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

เพราะเราไม่ได้ไปแก้ถังนั้น มันสร้างถังใหม่ตั้งแต่ตอนเราโยนตัวแปรเข้าไปใน function แล้ว

เราเลยต้องโยน address ของตัวแปรข้างนอกเข้าไปแทน เพื่อบอกให้ function รู้ ว่านายต้องไปแก้ไขค่าที่ถังนี้นะ

เอาแบบเห็นเป็นโค้ดเลย จะได้แบบนี้ (ผมดัดแปลงมาจากโค้ดของ BornToDev นะครับ)

void addFiveByValue(int number) 
{ 
     number += 5;
} 

void addFiveByPointer(int * number) 
{ 
     * number += 5;
} 

int main() 
{ 
   int myNum = 1; 
   int * myPointer = &myNum; // หยิบ address ของ myNum มาเก็บไว้ที่ myPointer

   addFiveByValue(myNum); // เรียก function ที่ใช้ตัวแปรปกติ
   printf("myNum (not using pointer): %d\n", myNum); 
   // myNum (not using pointer): 1

   addFiveByPointer(myPointer); // เรียก function ที่ใช้ pointer
   printf("myNum (using pointer): %d\n", myNum); 
   // myNum (using pointer): 6

   return 0; 
}

อย่าสับสน

สิ่งหนึ่งที่ทำให้ผมสับสนเรื่อง pointer โดยไม่จำเป็น คือ หน้าตาของ type มันนี่แหละ เพราะถ้าเราจะประกาศ pointer ที่จะพาเราไปหาค่า integer ถ้าเป็นภาษา C จะเขียนว่า int * ดูแค่นี้จะไม่ค่อยสับสน แต่ถ้าเป็นหน้าตาเต็ม ๆ มันจะเป็นแบบนี้ เขียนได้ 3 แบบ

int * j;
int* j;
int *j;

ตัวที่พาผมงง คือ int *j เพราะหน้าตามันเหมือนเวลาเรากำลังหยิบ value ออกมาจาก address แต่เอ๊ะตอนนี้เราเพิ่งประกาศตัวแปรนะ มันใช้ยังไงกันแน่เนี่ย

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

จบแล้ว

Pointer จะเป็นเรื่องแรก ๆ เลย ที่เราจะได้เรียนเวลาศึกษาภาษา C และด้วยความที่มันลงไประดับ low level มาก ๆ ไปถึง memory เลย จึงอาจจะยาก และ mind blow ไปมาก ๆ สำหรับมือใหม่ รวมถึงผมตอนที่กำลังหัดด้วย

คิดว่าน่าจะดี ถ้าเราสามารถเล่าสิ่งนี้เป็นด้วยภาษามนุษย์คุยกัน เลยอยากลองเอามาเล่าให้ทุกคนฟังครับ ไม่รู้ว่าจะเข้าใจขึ้น หรืองงกว่าเดิม ฮ่าาาา

Share the Post:

Leave a Reply

Your email address will not be published. Required fields are marked *

Continue Reading

คนที่คุยเก่ง อาจไม่ต้องพูดเก่งเสมอไป

เทคนิคในการพูดคุยที่น่าสนใจ จากหนังสือ 9 ใน 10 ของ ชีวิต พิชิตด้วยการพูด

The Pomodoro Technique

เคลียร์งานไว ใน 25 นาที

สมการ Value

คำว่า value ที่เราได้ยินกัน มันประกอบด้วยอะไรบ้าง

Lower Your Time Preference

ลงทุนแรงและเวลาในวันนี้ เพื่อสิ่งที่ดีกว่าในอนาคต