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

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

ตอนที่ 6 การใช้งาน Structure เบื้องต้น

The C Programming Language logo

           สำหรับคนที่อาจจะแค่คุ้นๆกับคำนี้ structure หรือในการใช้งานภาษา c จะใช้คำว่า struct คือการสร้างตัวแปรของเราขึ้นมาเอง โดยเป็นโครงสร้างที่ประกอบไปด้วยตัวแปรชนิดต่างๆ เรียกว่า field สำหรับคนเขียนโปรแกรมมือใหม่struct อาจจะไม่ใช่สิ่งจำเป็นทำให้อาจจะไม่ค่อยได้ใช้ แต่ถ้าได้ฝึกใช้จนคล่องแล้วจะพบว่าการใช้ struct ช่วยให้การเขียนโปรแกรมง่ายขึ้นมาก เราจะมาดูกันว่า struct มีประโยชน์อย่างไรบ้าง
การใช้ struct ในการเก็บค่า setting ต่างๆ
           ในงานทั่วๆไปที่ต้องมีการเก็บค่า setting ต่างๆ ลงในหน่วยความจำภายนอก เช่น EEPROM (เพื่อให้ค่า setting เหล่านั้นยังคงอยู่แม้จะมีการปิดแหล่งจ่ายไฟ) เรามักจะมีการเก็บค่า setting หลายอย่าง เช่น ถ้าเราทำ TV เราก็ต้องเก็บค่าระดับความดังเสียงล่าสุด, ช่องสุดท้ายที่เปิดดู, ความสว่างของแสง เป็นต้น ถ้าเราไม่ใช้ struct เราก็จะต้องกำหนดตัวแปรเหล่านี้ขึ้นมา ดังนี้
int volume; //เก็บระดับความดังของเสียง
int channel; //เก็บช่องที่เราดู
int light; //เก็บความสว่างของแสง
           จากนั้น เราก็ต้องกำหนดตำแหน่งของค่าแต่ละค่า ว่าอยู่ตรงไหนของ EEPROM เช่น
– volume เป็น integer ขนาด 4 byte อยู่ตำแหน่ง 0-3
– channel เป็น integer ขนาด 4 byte อยู่ตำแหน่ง 4-7
– light เป็น integer ขนาด 4 byte อยู่ตำแหน่ง 8-11

           เวลาเราเขียน เราก็มักจะนำค่าเหล่านี้มาเรียงใส่ buffer ที่เป็นตัวแปร array ก่อน แล้วจึงเขียนลงไปใน EEPROM ทีเดียว ดังนี้

unsigned char buffer[12]; //ประกาศ array สำหรับอ่านเขียนค่า setting

memcpy(buffer+0,&volume,4); //copy ค่า volume ลงไปใน buffer ตั้งแต่ตำแหน่ง 0 ด้วยความยาว 4 byte
memcpy(buffer+4,&channel,4); //copy ค่า buffer ลงไปใน buffer ตั้งแต่ตำแหน่ง 4 ด้วยความยาว 4 byte
memcpy(buffer+8,&light,4); //copy ค่า light ลงไปใน buffer ตั้งแต่ตำแหน่ง 8 ด้วยความยาว 4 byte
eeprom_write(buffer,0,12); //function ตัวอย่าง ใช้ในการเขียนข้อมูลจากตำแหน่งของ buffer ลงใน EEPROM โดยเริ่มตั้งแต่ตำแหน่งที่ 0 ของ EEPROM และใช้ความยาว 12 byte

หมายเหตุ คำสั่ง memcpy จะใช้สำหรับ copy ค่าในระดับ byte ทีละ byte โดย parameter แรกจะใช้ตำแหน่งปลายทาง parameter ตัวที่ 2 จะเป็นตำแหน่งต้นทาง และ parameter ตัวที่ 3 จะเป็นความยาวในหน่วย byte หากยังไม่เข้าใจเครื่องหมาย ‘&’ ให้ย้อนไปดูบทความตอนที่ 4
           ทีนี้เราลองมาเขียนโปรแกรมเดียวกันด้วย struct ดูบ้าง ก่อนอื่น เราต้องกำหนดรูปแบบของ struct ขึ้นมาก่อน

typedef struct { //เป็นการกำหนดชนิดของตัวแปร โดยเป็นตัวแปร structure
           int volume; //เก็บระดับความดังของเสียง
           int channel; //เก็บช่องที่เราดู
           int light; //เก็บความสว่างของแสง
} setting_struct; //เป็นการกำหนดให้ชนิดตัวแปร structure แบบนี้เรียกว่า setting_struct 
setting_struct setting; //เป็นการประกาศตัวแปรชนิด setting_struct ชื่อว่า setting

           สำหรับคนที่ยังไม่คุ้นเคยกับ struct เราจะอ่านหรือเขียนค่า field แต่ละ field ของ struct ด้วยการใส่ . ตามด้วยชื่อ field เช่น ถ้าเราจะอ่านหรือเขียนค่า volume เราต้องใช้ setting.volume เป็นต้น
           จากนั้นเวลาเราจะเขียนค่าลง EEPROM เราสามารถจะเขียนค่าจาก setting ลงไปใน EEPROM ได้โดยตรง ดังนี้

eeprom_write(&setting,0,sizeof(setting_struct)); //function ตัวอย่าง ใช้ในการเขียนข้อมูลจากตำแหน่งของ setting ลงใน EEPROM โดยเริ่มตั้งแต่ตำแหน่งที่ 0 ของ EEPROM และใช้ความยาวตามขนาดของ setting_struct

           ลองเปรียบเทียบกันระหว่าง 2 วิธี สิ่งที่ต่างกันก็คือ ถ้าเราไม่ใช้ struct ตัวแปรของเราจะกระจัดกระจายเป็นหลายๆตัว ในการอ่านหรือเขียนก็จะต้องเขียนเยอะขึ้น เพราะต้องเอาค่าตัวแปรแต่ละตัวมารวมกันก่อน นอกจากนี้ เราต้องมาคำนึงถึงตำแหน่งและขนาดของตัวแปรแต่ละตัว ซึ่งก็มีโอกาสผิดพลาดได้ง่าย
           ขณะที่การใช้ struct จะเห็นว่าตัวแปรของเราถูกรวมเอาไว้ต่อๆกันเป็น structure ตัวเดียว ทำให้การจัดการทำได้ง่ายกว่า เราสามารถจะเขียนหรืออ่าน struct ทั้งก้อนได้ด้วยคำสั่งเดียว

ขนาดและการจัดเรียงข้อมูลของ struct
           สมมติว่าเราใช้ MCU ขนาด 32 bit (ซึ่ง int มีขนาด 4 byte) ส่วน char มีขนาด 1 byte, short มีขนาด 2 byte และ long มีขนาด 4 byte เราลองมาหาขนาดของ struct ตัวอย่างด้านล่างกัน

typedef struct {
           char a; //ขนาด 1 byte
           short b; //ขนาด 2 byte
           int c; //ขนาด 4 byte
           int d[3]; //ขนาด 12 byte
           char e; //ขนาด 1 byte
           long f; //ขนาด 4 byte
} test_struct;

           ถ้าเราลองรวมแบบง่ายๆ เราจะคิดว่าขนาดของ structure ทั้งหมดคือ 1+2+4+12+1+4 = 24 byte แต่จริงๆแล้วขนาดของ test_struct คือ28 byte โดยเราจะหาได้จากการใช้คำสั่ง sizeof() (เป็นคำสั่งที่อยู่ใน library stdio.h) ซึ่งสามารถใช้ได้กับตัวแปรทุกๆชนิด สาเหตุที่ test_struct มีขนาด 28 byte เนื่องจากในจัดเรียง memory ของ MCU นั้นตัวแปรขนาด 2 byte จะต้องอยู่ในตำแหน่ง (address) ที่หาร 2 ลงตัว ส่วนตัวแปรขนาด 4 byte ก็จะต้องอยู่ในตำแหน่งที่หาร 4 ลงตัว การจัดเรียง struct ดังกล่าว จึงสามารถแสดงเป็นภาพได้ดังนี้

C Programming CH6

           และถ้าเราเพิ่มตัวแปรเข้าไปอีก 1 ตัวต่อท้าย เช่น เพิ่ม char g เข้าไปเป็น field สุดท้ายต่อจาก long f คิดว่าขนาดของ test จะเพิ่มเป็นเท่าไหร่ จะเป็น 29, 30 หรือว่า 32 ลองหาคำตอบได้ด้วยการเขียนโปรแกรมบน MCU จริง (ในที่นี้ต้องใช้ MCU 32 bit) แล้วลอง debug ดูค่าของ sizeof(test)
           การจัดเรียงข้อมูลใน MCU อาจไม่ใช่สิ่งที่เราจำเป็นต้องรู้ แต่ที่ต้องรู้ก็คือขนาดของ struct อาจจะไม่ได้รวมได้ง่ายๆอย่างที่เราคิด เราจึงควรใช้ sizeof() ในการหาขนาดที่แท้จริง เพื่อการคำนวณที่ถูกต้อง อย่างไรก็ตาม ความเข้าใจในการทำงานของ MCU จะช่วยให้เราเขียนโปรแกรมโดยป้องกันการเกิดความผิดพลาดเอาไว้ก่อนได้ อย่างเช่น ถ้าเราจะใช้ pointer หรือ array สำหรับตัวแปรขนาด 4 byte เราจะต้องระวังตำแหน่งที่เราจะใช้ ให้เป็นตำแหน่งที่หารด้วย 4 ลงตัวเสมอ นอกจากนี้เรายังได้เห็นว่า ถ้าเราต้องการสร้าง struct ให้มีขนาดที่เล็กที่สุด เราควรจะเรียง field ใน struct อย่างไร
           การใช้ structure ยังมีประโยชน์มากกว่านี้ เราลองมาดูกันต่อในบทความตอนต่อไปครับ

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