ความรู้เกี่ยวกับการเขียนโปรแกรมภาษา C ที่นักอิเล็กทรอนิกส์ไม่ค่อยรู้ ตอนที่ 3 เริ่มต้นรู้จักกับตัวแปร pointer

ความรู้เกี่ยวกับการเขียนโปรแกรมภาษา C ที่นักอิเล็กทรอนิกส์ไม่ค่อยรู้

ตอนที่ 3 เริ่มต้นรู้จักกับตัวแปร pointer

         พูดถึงคำว่า pointer อาจจะเป็นเรื่องที่ดูยุ่งยากสำหรับใครหลายๆคน ผมเองแต่ก่อนก็พยายามหลีกเลี่ยง โดยใช้เท่าที่จำเป็น และก็ใช้แบบพื้นๆ แต่เมื่องานมีความซับซ้อนขึ้นเรื่อยๆ ก็เลยถึงเวลาที่ต้องศึกษาอย่างจริงจังสักตั้งเพื่อให้โปรแกรมที่เราเขียนมีประสิทธิภาพ ตัวแปร pointer นั้นสามารถนำไปใช้งานได้หลากหลายรูปแบบ ในตอนนี้จึงขอเริ่มต้นจากง่ายๆกันก่อน แล้วตอนต่อๆไปจะค่อยๆพูดถึงการใช้งานที่ซับซ้อนมากขึ้น ใครที่เข้าใจเรื่อง pointer เป็นอย่างดีแล้ว เวลาเขียนโปรแกรมจะเขียนได้ทะลุปรุโปร่งเหมือนกับที่ Neo ในเรื่อง The Matrix เห็นทุกอย่างในโลกเป็น 0 กับ 1 เลยทีเดียว
ว่าด้วยเรื่องของตำแหน่ง (address)
         ก่อนที่จะพูดถึง pointer จำเป็นจะต้องเข้าใจก่อน ว่าตัวแปรทุกตัว และ function ทุก function จะถูกกำหนดตำแหน่งไว้ด้วย compiler ซึ่งก็คือตำแหน่งจริงที่อยู่ในหน่วยความจำ RAM หรือไม่ก็หน่วยความจำ FLASH (ที่เก็บ code) ถ้าเป็นตัวแปร global หรือตัวแปรแบบ static (เป็นตัวแปรที่อยู่ใน RAM หรือ FLASH ที่เดิมตลอดเวลา ได้รับการจัดสรรพื้นที่ให้อย่างถาวร) ก็จะมีตำแหน่งที่คงที่ตลอด (จะเปลี่ยนก็ต่อเมื่อเราประกาศตัวแปรแทรกเพิ่มเข้ามาแล้วทำการ compile ใหม่) สมมติว่าตำแหน่งของ RAM ใน microcontroller เริ่มต้นที่ 0x20000000 ตัวแปรตัวแรกที่ถูกประกาศไว้ก็จะอยู่ที่ตำแหน่ง 0x2000000 (สมมติว่าไม่มีอย่างอื่นมาแทรก) และตัวแปรตัวถัดไป ก็จะอยู่ที่ตำแหน่งที่มากขึ้นตามขนาดของตัวแปรตัวแรก ส่วน function นั้นโดยทั่วไปจะมีตำแหน่งที่อยู่ในหน่วยความจำ FLASH (สำหรับกรณีส่วนใหญ่ที่ใช้ FLASH ภายใน chip ของ MCU) 
ตัวแปร pointer กับการส่งผ่านข้อมูล
         pointer แปลตรงตัวได้ว่าตัวชี้ ตัวแปร pointer ก็หมายถึง ตัวแปรที่ทำหน้าที่เป็นตัวชี้ตำแหน่งของตัวแปรชนิดอื่นๆ ค่าของตัวแปร pointer คือตำแหน่งของตัวแปรชนิดที่ประกาศไว้ แทนที่จะเป็นค่าจริงๆเหมือนตัวแปรปกติ ถ้าเราประกาศตัวแปร char x เวลาเราเอา x มาใช้ที่ไหนก็ตาม จะเป็นการเอาค่า x ณ ขณะนั้นมาใช้ โดยไม่สนใจว่ามันจะอยู่ที่ตำแหน่งใด ซึ่งในทางเทคนิค เรียกว่าเป็นการส่งผ่านข้อมูลด้วยค่า (pass data by value)
char x = ‘5’; //เป็นการจองพื้นที่ให้กับตัวแปร x และกำหนดให้มีค่าเป็น ‘5’
printf(“%c”,x); //จะเป็นการพิมพ์เลข 5 ออกมา
         ในอีกทางหนึ่ง เรายังสามารถส่งค่าด้วยตำแหน่งของมัน แทนที่จะเป็นค่าจริงๆ แล้วการอ่านค่า จะเป็นการไปอ่านที่ตำแหน่งนั้นอีกที เรียกว่าเป็นการส่งผ่านข้อมูลด้วยตำแหน่ง (pass data by reference) ยกตัวอย่างเช่น
char str1[] = “1234”; //เป็นการประกาศ array ของ char
char *str2; //ไม่ได้เป็นการประกาศตัวแปร char แต่เป็นการประกาศตัวแปร pointer สำหรับเก็บตำแหน่งของตัวแปรประเภท char
str2 = str1; //เป็นการเอาตำแหน่งของ str1 ไปใส่ไว้ใน str2
printf(str2); //จะเป็นการพิมพ์ตัวเลข 1234 ออกมา เหมือนกับในตัวอย่างก่อนหน้า
         จากตัวอย่างนี้ ถ้า str1 อยู่ที่ตำแหน่ง 0x20000000 และ str2 อยู่ที่ตำแหน่งถัดมาก็จะอยู่ที่ 0x20000005 (เลื่อนมา 5 byte เนื่องจากเราประกาศ str1 เป็น string จึงมีค่า 0 ต่อท้ายเป็นการปิด string) ก็จะสามารถแสดงได้ดังนี้

Basic C Programming

ที่บรรทัด printf(str2); ค่าที่ส่งไปยัง function printf จะเป็น 0x20000000 ซึ่งเป็นตำแหน่งของ str1 และจะสังเกตได้ว่าขนาดของ str2 จะเป็น 4 byte (โดยทั่วไป) ซึ่งไม่ว่าจะเป็น pointer ที่เก็บตำแหน่งของตัวแปรชนิดใด ก็จะมีขนาดเท่ากัน เพราะเป็นการเก็บตำแหน่ง ขนาดของ pointer จึงขึ้นกับขนาดของ register ของ MCU ว่ามีการเก็บตำแหน่งกี่ byte
การใช้ ‘&’ เพื่อส่งตำแหน่งของตัวแปร
         ตัวอย่างที่ผ่านมาเป็นการส่งตำแหน่งของ array ไปยัง pointer ซึ่งตัวแปร array (เช่น str1 เฉยๆ ไม่มี [ ]) จะให้ค่าที่เป็นตำแหน่งอยู่แล้ว แต่ถ้าเราจะส่งข้อมูลที่ไม่ได้เป็น array จะมีวิธีการส่งอย่างไร สมมติว่าเราสร้าง function ขึ้นมาชื่อว่า send_data ใช้สำหรับส่งข้อมูลขนาดหลายๆ byte ออกทาง serial port เราจะต้องการ input เป็นตำแหน่งของ byte เริ่มต้น (unsigned char *dat) และจำนวน byte (int len)
void send_data(unsigned char *dat , int len);
ในการเรียกใช้ function ถ้าเราจะส่งข้อมูลหลายๆ byte เราก็อาจจะเขียนได้ดังนี้
unsigned char buf[3]; //ประกาศ array ของตัวแปร unsigned char มีขนาด 3 byte
buf[0] = 0x01;
buf[1] = 0x02;
buf[2] = 0x03;
send_data(buf,3); //ส่งตำแหน่งของ buf พร้อมกับความยาว 3 byte ให้กับ function send_data
และถ้าเราต้องการจะส่งข้อมูล byte เดียว ก็จะสามารถใช้ ‘&’ ในการส่งค่าตำแหน่งของตัวแปร
unsigned char c = 0x01;
send_data(&c,1); //ส่งตำแหน่งของ c พร้อมกับความยาว 1 byte ให้กับ function send_data
นอกจากนี้ถ้าเราต้องการส่งข้อมูล byte ที่ [2] ของ buf ด้านบน เพียง byte เดียว เรายังสามารถเขียนได้ว่า
send_data(buf+2,1); //ส่งข้อมูล byte ที่ 2 เพียง byte เดียว เราสามารถเขียนเป็น buf+2 ซึ่งเป็นการส่งค่าตำแหน่งของ buf บวกกับ 2
ความแตกต่างระหว่าง pointer กับ array
         ตัวแปร pointer กับ array จะมีวิธีการใช้งานคล้ายๆกัน คือสามารถใส่ [ ] เพื่ออ่านหรือเขียนค่าแต่ละตำแหน่งได้ เช่น
char str1[] = “1234”; //เป็นการประกาศ array ของ char
char *str2; //เป็นการประกาศตัวแปร pointer สำหรับเก็บตำแหน่งของตัวแปรประเภท char
str2 = str1; //เป็นการเอาตำแหน่งของ str1 ไปใส่ไว้ใน str2 (ถ้าเราเขียนชื่อของ array เฉยๆ จะเป็นการใช้ตำแหน่งของมัน)
         หลังจากนี้เราจะสามารถอ่านหรือเขียนค่าแต่ละ byte ของ str2 ได้โดยใช้ str2[0], str2[1], str2[2], … 
         สิ่งที่แตกต่างกันระหว่าง pointer กับ array ก็คือ array จะมีขนาดตามที่กำหนดไว้ตั้งแต่แรก ไม่สามารถเปลี่ยนแปลงได้ (สำหรับภาษา C ใน microcontroller) และตำแหน่งของ array ไม่สามารถเปลี่ยนแปลงได้ ขณะที่ pointer จะมีขนาดที่ตายตัวตามที่กล่าวไว้ข้างต้น คือโดยทั่วไปจะมีขนาด 4 byte แต่สามารถจะชี้ไปที่ไหนก็ได้ เปลี่ยนได้ตลอด โดยต้องมีการกำหนดว่าจะชี้ไปที่ไหน ก่อนที่จะใช้งาน ดังนั้นการใช้งาน array กับ pointer จึงต่างกัน เราจะใช้ตัวแปร pointer เป็นตัวชี้ตำแหน่งไปยัง array หรือตัวแปรอื่นๆ โดยที่ไม่ได้มีพื้นที่เก็บข้อมูลของตัวเอง ส่วน array จะเป็นพื้นที่เก็บข้อมูลจริง มีการจองหน่วยความจำไว้ ณ ที่ใดที่หนึ่งตลอดเวลา
         บทความเรื่องตัวแปร pointer ในตอนนี้ เราเพิ่งจะกำหนดตำแหน่งให้กับตัวแปร pointer ว่าให้ชี้ไปที่ไหน แต่ยังไม่ได้อ่านหรือเขียนค่าในตำแหน่งที่ชี้ ซึ่งจะได้พูดถึงในตอนต่อไป รวมไปถึงการเลื่อนตำแหน่ง pointer อีกด้วย

<< กลับไปสู่หน้าแรกสารบัญ