- 1 1. บทนำ
- 2 2. อาร์เรย์สองมิติ คืออะไร
- 3 3. การประกาศและการกำหนดค่าเริ่มต้นของอาร์เรย์สองมิติ
- 3.1 วิธีการประกาศอาร์เรย์สองมิติ
- 3.2 การกำหนดค่าเริ่มต้นของอาร์เรย์สองมิติ
- 3.3 โค้ดตัวอย่าง: การประกาศและกำหนดค่าเริ่มต้นอาร์เรย์
- 3.4 ผลลัพธ์ที่ได้
- 3.5 4. วิธีการใช้อาร์เรย์สองมิติ: การเข้าถึงและแก้ไของค์ประกอบ
- 3.6 การเข้าถึงองค์ประกอบ
- 3.7 การแก้ไของค์ประกอบ
- 3.8 การทำงานซ้ำ (Loop) กับอาร์เรย์สองมิติ
- 3.9 ตัวอย่างการประยุกต์: กำหนดค่าทุกองค์ประกอบเป็นค่าเดียวกัน
- 4 5. โครงสร้างของหน่วยความจำสำหรับอาร์เรย์สองมิติ
- 5 6. ตัวอย่างการใช้งานจริง: การคำนวณเมทริกซ์และการสร้างกระดานเกม
- 6 7. ความสัมพันธ์ระหว่างอาร์เรย์สองมิติและตัวชี้ (Pointer)
- 6.1 ความสัมพันธ์พื้นฐานระหว่างอาร์เรย์สองมิติและตัวชี้
- 6.2 การอ้างอิงองค์ประกอบด้วยตัวชี้
- 6.3 การใช้อาร์เรย์สองมิติเป็นอาร์กิวเมนต์ของฟังก์ชัน
- 6.4 การใช้งานอาร์เรย์สองมิติแบบไดนามิกด้วยตัวชี้
- 6.5 8. วิธีการจองและคืนหน่วยความจำสำหรับอาร์เรย์สองมิติ
- 6.6 พื้นฐานของการจัดการหน่วยความจำแบบไดนามิก
- 6.7 วิธีการสร้างอาร์เรย์สองมิติแบบไดนามิก
- 6.8 ข้อควรระวังในการจัดการหน่วยความจำแบบไดนามิก
- 7 9. ข้อควรระวังในการใช้อาร์เรย์สองมิติ
- 8 10. สรุป
- 9 11. แบบฝึกหัด
1. บทนำ
ภาษา C เป็นหนึ่งในภาษาการเขียนโปรแกรมที่มีความสำคัญและมีประวัติศาสตร์ยาวนาน ภายในภาษา C “อาร์เรย์ (Array)” ถือเป็นฟังก์ชันพื้นฐานที่ขาดไม่ได้ในการจัดการและประมวลผลข้อมูลอย่างมีประสิทธิภาพ โดยเฉพาะ “อาร์เรย์สองมิติ (2D Array)” ที่สะดวกมากเมื่อใช้จัดการข้อมูลในรูปแบบตารางหรือเมทริกซ์
บทความนี้จะอธิบายเกี่ยวกับอาร์เรย์สองมิติในภาษา C ในรูปแบบที่เข้าใจง่ายสำหรับผู้เริ่มต้น โดยจะอธิบายตั้งแต่การใช้งานพื้นฐานไปจนถึงวิธีการประยุกต์ใช้ เพื่อให้ผู้อ่านสามารถนำไปใช้จริงในการเขียนโปรแกรมได้
ความสำคัญของอาร์เรย์ในภาษา C
อาร์เรย์ในภาษา C เป็นโครงสร้างข้อมูลที่ช่วยให้สามารถจัดเก็บข้อมูลหลายตัวที่มีชนิดเดียวกันได้ในที่เดียว ทำให้ไม่จำเป็นต้องประกาศตัวแปรทีละตัว และช่วยให้โค้ดกระชับและอ่านง่ายขึ้น ตัวอย่างเช่น หากต้องการเก็บตัวเลขหรือสตริงหลายค่า การใช้อาร์เรย์จะช่วยให้จัดการได้อย่างมีประสิทธิภาพ
โดยเฉพาะ “อาร์เรย์สองมิติ” มีประโยชน์ในสถานการณ์ต่อไปนี้:
- การคำนวณเมทริกซ์ทางคณิตศาสตร์
- การจัดการกระดานเกม (เช่น หมากรุก หรือโอเทลโล)
- การประมวลผลข้อมูลที่มีโครงสร้างแบบตารางหรือสเปรดชีต
สิ่งที่จะได้เรียนรู้จากบทความนี้
ในบทความนี้ ผู้อ่านจะได้เรียนรู้เนื้อหาต่อไปนี้แบบทีละขั้นตอน:
- โครงสร้างพื้นฐานและวิธีการประกาศอาร์เรย์สองมิติ
- การกำหนดค่าเริ่มต้นและการเข้าถึงองค์ประกอบของอาร์เรย์
- การจัดการอาร์เรย์สองมิติในหน่วยความจำ
- ตัวอย่างการประยุกต์ใช้ในโปรแกรมจริง
- วิธีการจองและคืนหน่วยความจำของอาร์เรย์สองมิติแบบไดนามิก
- ข้อควรระวังและข้อผิดพลาดที่พบบ่อยเมื่อใช้อาร์เรย์
แม้จะเป็นผู้เริ่มต้น หากอ่านบทความนี้ก็จะสามารถทำความเข้าใจพื้นฐานของอาร์เรย์สองมิติ และนำไปประยุกต์ใช้ได้จริงในการเขียนโปรแกรม
บทความนี้เหมาะกับใคร
บทความนี้เหมาะสำหรับทั้งผู้ที่เพิ่งเริ่มเรียนภาษา C และผู้ที่ต้องการขยายความสามารถในการเขียนโปรแกรม โดยเฉพาะผู้ที่:
- มีความเข้าใจพื้นฐานเกี่ยวกับอาร์เรย์ แต่ยังไม่เคยใช้อาร์เรย์สองมิติ
- ต้องการจัดการข้อมูลในรูปแบบตารางภายในโปรแกรม
- ต้องการทบทวนพื้นฐานการเขียนภาษา C
2. อาร์เรย์สองมิติ คืออะไร
“อาร์เรย์สองมิติ” ในภาษา C คือโครงสร้างข้อมูลที่สามารถจัดเก็บข้อมูลในรูปแบบ “แถวและคอลัมน์” ได้ หรือกล่าวอีกอย่างคือ “อาร์เรย์ของอาร์เรย์” ซึ่งมีประโยชน์อย่างยิ่งในการคำนวณเมทริกซ์และการจัดการข้อมูลแบบตาราง และถูกใช้งานอย่างแพร่หลายในโปรแกรมจริง
ในส่วนนี้ เราจะอธิบายแนวคิดพื้นฐานและโครงสร้างของอาร์เรย์สองมิติ
โครงสร้างพื้นฐานของอาร์เรย์สองมิติ
อาร์เรย์สองมิติประกอบด้วยหลายแถวและหลายคอลัมน์ โดยในความเป็นจริงแล้วแต่ละแถวคืออาร์เรย์หนึ่งมิติ และเมื่อนำมารวมกันจะกลายเป็นอาร์เรย์สองมิติ
ตัวอย่าง: ภาพรวมของอาร์เรย์สองมิติ
ข้อมูลจะถูกจัดเก็บโดยใช้แถวและคอลัมน์ ดังตัวอย่าง:
array[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
ในกรณีนี้ array
คืออาร์เรย์สองมิติที่มี 3 แถว 4 คอลัมน์ และสามารถเข้าถึงองค์ประกอบได้ด้วยการใช้ดัชนี
array[0][0]
= “1”array[2][3]
= “12”
การใช้งานของอาร์เรย์สองมิติ
อาร์เรย์สองมิติสามารถนำมาใช้ในกรณีต่าง ๆ เช่น:
- การคำนวณเมทริกซ์
เช่น การบวกและคูณเมทริกซ์ - การจัดการข้อมูลแบบตาราง
เช่น สเปรดชีตหรือฐานข้อมูล - การพัฒนาเกม
เช่น จัดการกระดานหมากรุกหรือโอเทลโล - การประมวลผลภาพ
เช่น เก็บค่าพิกเซลของสีภาพ
ดังนั้นอาร์เรย์สองมิติถือเป็นโครงสร้างพื้นฐานที่สำคัญมากในการจัดการข้อมูลซับซ้อนอย่างมีประสิทธิภาพ
ความแตกต่างระหว่างอาร์เรย์หนึ่งมิติและสองมิติ
ลักษณะของอาร์เรย์หนึ่งมิติ
อาร์เรย์หนึ่งมิติจะเก็บข้อมูลในรูปแบบเชิงเส้น (linear) เพียงแกนเดียว
int array[5] = {1, 2, 3, 4, 5};
ในกรณีนี้สามารถเข้าถึงข้อมูลตามลำดับด้วยดัชนี
array[0]
= “1”array[4]
= “5”
ลักษณะของอาร์เรย์สองมิติ
อาร์เรย์สองมิติจะเก็บข้อมูลโดยใช้ทั้ง “แถว” และ “คอลัมน์”
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
ในกรณีนี้จะเข้าถึงข้อมูลด้วยการระบุแถวและคอลัมน์
array[0][2]
= “3”array[1][0]
= “4”
ดังนั้น อาร์เรย์สองมิติจึงมีประโยชน์มากเมื่อจัดการโครงสร้างข้อมูลที่ซับซ้อน
3. การประกาศและการกำหนดค่าเริ่มต้นของอาร์เรย์สองมิติ
ในภาษา C การใช้อาร์เรย์สองมิติต้องเริ่มจากการประกาศ และเมื่อจำเป็นสามารถกำหนดค่าเริ่มต้นได้ ส่วนนี้จะอธิบายวิธีการประกาศและการกำหนดค่าเริ่มต้นของอาร์เรย์สองมิติ
วิธีการประกาศอาร์เรย์สองมิติ
การประกาศอาร์เรย์สองมิติทำได้ดังนี้:
ชนิดข้อมูล ชื่ออาร์เรย์[จำนวนแถว][จำนวนคอลัมน์];
- ชนิดข้อมูล: ชนิดของข้อมูลที่จะจัดเก็บในอาร์เรย์ (เช่น
int
,float
,char
) - จำนวนแถวและจำนวนคอลัมน์: ขนาดของอาร์เรย์ที่กำหนดเป็นจำนวนเต็ม
ตัวอย่างการประกาศ
ตัวอย่างด้านล่างนี้คือการประกาศอาร์เรย์สองมิติชนิด int
ขนาด 3 แถว 4 คอลัมน์:
int array[3][4];
กรณีนี้ หน่วยความจำที่สามารถเก็บข้อมูล 3 แถว 4 คอลัมน์ จะถูกจองไว้
การกำหนดค่าเริ่มต้นของอาร์เรย์สองมิติ
เมื่อประกาศอาร์เรย์สองมิติ สามารถกำหนดค่าเริ่มต้นไปพร้อมกันได้ โดยมีหลายวิธีในการกำหนดค่าเริ่มต้น
วิธีที่ 1: กำหนดค่าเริ่มต้นทุกองค์ประกอบ
สามารถกำหนดค่าเริ่มต้นให้ทุกตำแหน่งได้ดังนี้:
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
ในกรณีนี้ array
จะถูกจัดเก็บในหน่วยความจำดังนี้:
1 2 3
4 5 6
array[0][0]
→ 1array[1][2]
→ 6
วิธีที่ 2: กำหนดค่าเริ่มต้นบางส่วน
สามารถกำหนดค่าเฉพาะบางตำแหน่งได้ ส่วนที่เหลือจะถูกกำหนดเป็น 0 อัตโนมัติ:
int array[2][3] = {
{1, 2},
{4}
};
อาร์เรย์นี้จะถูกจัดเก็บดังนี้:
1 2 0
4 0 0
วิธีที่ 3: ใช้ {0}
เพื่อกำหนดค่าเริ่มต้นทั้งหมดเป็นศูนย์
หากต้องการให้ทุกตำแหน่งเริ่มต้นเป็น 0 สามารถใช้ {0}
ได้:
int array[3][4] = {0};
ในกรณีนี้ทุกองค์ประกอบของอาร์เรย์จะถูกกำหนดค่าเป็น 0
ข้อควรระวังในการกำหนดค่าเริ่มต้น
- จำนวนแถวไม่สามารถละเว้นได้
ต้องระบุจำนวนแถว (มิติแรก) เสมอ
int array[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8}
};
ในกรณีนี้ แม้ไม่ได้ระบุจำนวนแถว แต่เนื่องจากกำหนดจำนวนคอลัมน์ไว้ โปรแกรมจะคำนวณจำนวนแถวจากรายการค่าเริ่มต้นที่กำหนดให้
- จำนวนคอลัมน์ต้องระบุ
สำหรับอาร์เรย์สองมิติ ต้องระบุอย่างน้อยจำนวนคอลัมน์ (มิติที่สอง)
โค้ดตัวอย่าง: การประกาศและกำหนดค่าเริ่มต้นอาร์เรย์
ตัวอย่างต่อไปนี้เป็นการประกาศอาร์เรย์สองมิติ กำหนดค่าเริ่มต้น และแสดงผลแต่ละองค์ประกอบ:
#include <stdio.h>
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}
}
return 0;
}
ผลลัพธ์ที่ได้
array[0][0] = 1
array[0][1] = 2
array[0][2] = 3
array[1][0] = 4
array[1][1] = 5
array[1][2] = 6
4. วิธีการใช้อาร์เรย์สองมิติ: การเข้าถึงและแก้ไของค์ประกอบ
ในภาษา C สามารถเข้าถึงและแก้ไขค่าในอาร์เรย์สองมิติได้ ส่วนนี้จะอธิบายวิธีเข้าถึงและการใช้งานจริง
การเข้าถึงองค์ประกอบ
การเข้าถึงค่าในอาร์เรย์สองมิติต้องระบุหมายเลขแถวและคอลัมน์
วิธีการเข้าถึงพื้นฐาน
array[แถว][คอลัมน์]
ตัวอย่างเช่น มีอาร์เรย์ดังนี้:
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
array[0][0]
→ 1 (แถวที่ 1 คอลัมน์ที่ 1)array[1][2]
→ 6 (แถวที่ 2 คอลัมน์ที่ 3)
ตัวอย่าง: โปรแกรมที่แสดงผลองค์ประกอบ
#include <stdio.h>
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("array[0][0] = %d\n", array[0][0]);
printf("array[1][2] = %d\n", array[1][2]);
return 0;
}
ผลลัพธ์ที่ได้
array[0][0] = 1
array[1][2] = 6
การแก้ไของค์ประกอบ
สามารถเปลี่ยนค่าขององค์ประกอบในอาร์เรย์ได้โดยการกำหนดค่าใหม่
วิธีการเปลี่ยนค่า
array[แถว][คอลัมน์] = ค่าใหม่;
ตัวอย่าง: โปรแกรมที่เปลี่ยนค่า
#include <stdio.h>
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// เปลี่ยนค่า
array[0][0] = 10;
array[1][2] = 20;
// แสดงผลลัพธ์
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}
}
return 0;
}
ผลลัพธ์ที่ได้
array[0][0] = 10
array[0][1] = 2
array[0][2] = 3
array[1][0] = 4
array[1][1] = 5
array[1][2] = 20
การทำงานซ้ำ (Loop) กับอาร์เรย์สองมิติ
ในการจัดการอาร์เรย์สองมิติ มักใช้ลูปซ้อนกัน (nested loop) เพื่อเข้าถึงหรือแก้ไขข้อมูลทั้งหมด
ตัวอย่าง: การวนลูปตามแถวและคอลัมน์
#include <stdio.h>
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// วนลูปเพื่อแสดงผลอาร์เรย์
for (int i = 0; i < 2; i++) { // ลูปตามแถว
for (int j = 0; j < 3; j++) { // ลูปตามคอลัมน์
printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}
}
return 0;
}
ผลลัพธ์ที่ได้
array[0][0] = 1
array[0][1] = 2
array[0][2] = 3
array[1][0] = 4
array[1][1] = 5
array[1][2] = 6
ตัวอย่างการประยุกต์: กำหนดค่าทุกองค์ประกอบเป็นค่าเดียวกัน
สามารถใช้ลูปเพื่อกำหนดค่าทุกตำแหน่งของอาร์เรย์เป็นค่าที่ต้องการได้
ตัวอย่าง: กำหนดทุกองค์ประกอบเป็น 5
#include <stdio.h>
int main() {
int array[3][3];
// กำหนดค่าเป็น 5 ทุกตำแหน่ง
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
array[i][j] = 5;
}
}
// แสดงผล
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("array[%d][%d] = %d\n", i, j, array[i][j]);
}
}
return 0;
}
ผลลัพธ์ที่ได้
array[0][0] = 5
array[0][1] = 5
array[0][2] = 5
array[1][0] = 5
array[1][1] = 5
array[1][2] = 5
array[2][0] = 5
array[2][1] = 5
array[2][2] = 5
5. โครงสร้างของหน่วยความจำสำหรับอาร์เรย์สองมิติ
ในการเขียนภาษา C การเข้าใจว่าอาร์เรย์สองมิติถูกจัดเก็บในหน่วยความจำอย่างไรมีความสำคัญมาก ความรู้ตรงนี้จะช่วยให้เขียนโปรแกรมได้อย่างมีประสิทธิภาพมากขึ้น และสามารถใช้งานตัวชี้ (pointer) ได้อย่างคล่องแคล่ว
การจัดเก็บข้อมูลของอาร์เรย์สองมิติในหน่วยความจำ
อาร์เรย์สองมิติในภาษา C แท้จริงแล้วถูกจัดเก็บต่อเนื่องในหน่วยความจำแบบหนึ่งมิติ โดยใช้รูปแบบที่เรียกว่า row-major order หรือการจัดเรียงตามแถว
row-major คืออะไร
row-major คือวิธีจัดเก็บที่ข้อมูลในแต่ละแถวจะถูกจัดเก็บต่อเนื่องกันในหน่วยความจำ
ตัวอย่าง: การจัดเก็บในหน่วยความจำ
พิจารณาอาร์เรย์สองมิติด้านล่าง:
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
อาร์เรย์นี้จะถูกจัดเก็บในหน่วยความจำดังนี้:
1 2 3 4 5 6
array[0][0]
→ ตำแหน่งแรกของหน่วยความจำarray[0][1]
→ ตำแหน่งที่สองarray[1][0]
→ ตำแหน่งที่สี่
การอ้างอิงองค์ประกอบด้วยดัชนี
เมื่อใช้อาร์เรย์ในภาษา C การอ้างอิงองค์ประกอบด้วยดัชนีจะถูกคำนวณตามสูตรดังนี้:
array[i][j] = *(array + (i * จำนวนคอลัมน์) + j)
ตัวอย่าง: การคำนวณตำแหน่งหน่วยความจำ
สำหรับอาร์เรย์ array[2][3]
:
array[1][2]
สามารถคำนวณได้ดังนี้:
*(array + (1 * 3) + 2) = *(array + 5)
ซึ่งหมายความว่า เนื่องจากอาร์เรย์ถูกเก็บแบบ row-major จึงข้ามตำแหน่งของ 3 องค์ประกอบจากแถวแรก (i = 1) แล้วบวกเพิ่มด้วย 2 (j = 2) เพื่อเข้าถึงตำแหน่งที่ถูกต้อง
การใช้งานตัวชี้ (Pointer) กับอาร์เรย์สองมิติ
อาร์เรย์สองมิติในภาษา C สามารถเข้าถึงได้โดยใช้ตัวชี้ ทำให้มีความยืดหยุ่นมากขึ้น
ความสัมพันธ์ระหว่างตัวชี้และอาร์เรย์สองมิติ
อาร์เรย์สองมิติจริง ๆ แล้วคือ “อาร์เรย์ของอาร์เรย์” ดังนั้นสามารถใช้ตัวชี้เข้าถึงได้ เช่น:
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int *ptr = &array[0][0];
printf("%d\n", *(ptr + 4)); // แสดงผล: 5
ในกรณีนี้ ptr
จะชี้ไปยังองค์ประกอบแรกของอาร์เรย์ และสามารถเข้าถึงองค์ประกอบอื่น ๆ ได้ด้วยการบวก offset
การอธิบายการจัดเก็บในหน่วยความจำแบบภาพ
ตัวอย่างการจัดเก็บข้อมูลของ array[2][3]
ในหน่วยความจำ:
หน่วยความจำ: 1 2 3 4 5 6
ดัชนี:
[0][0] [0][1] [0][2] [1][0] [1][1] [1][2]
จะเห็นได้ว่าอาร์เรย์สองมิติถูกจัดเก็บต่อเนื่องกันในหน่วยความจำเสมือนเป็นอาร์เรย์หนึ่งมิติ
ข้อควรระวังเพื่อการทำงานที่มีประสิทธิภาพ
เพื่อใช้งานอาร์เรย์สองมิติอย่างมีประสิทธิภาพ ควรใส่ใจประเด็นต่อไปนี้:
- เข้าถึงข้อมูลแบบ row-major
เวลาวนลูปเข้าถึงข้อมูล ควรยึดตามแถวเป็นหลักแล้วค่อยวนคอลัมน์ จะทำให้ทำงานได้รวดเร็วกว่า
for (int i = 0; i < จำนวนแถว; i++) {
for (int j = 0; j < จำนวนคอลัมน์; j++) {
// เข้าถึงแบบ row-major
}
}
การเข้าถึงแบบนี้จะช่วยให้ใช้ cache ของหน่วยความจำได้อย่างมีประสิทธิภาพ
- ใช้ pointer อย่างเหมาะสม
การใช้ pointer สามารถลดการคำนวณของดัชนี และเพิ่มประสิทธิภาพของโปรแกรม
6. ตัวอย่างการใช้งานจริง: การคำนวณเมทริกซ์และการสร้างกระดานเกม
อาร์เรย์สองมิติถูกนำไปใช้จริงอย่างกว้างขวาง เช่น การคำนวณเมทริกซ์ทางคณิตศาสตร์ หรือการจัดการสถานะของกระดานเกม ในที่นี้จะยกตัวอย่าง 2 กรณีคือ “การคำนวณเมทริกซ์” และ “การสร้างกระดานเกม”
ตัวอย่างการคำนวณเมทริกซ์
การคำนวณเมทริกซ์เป็นสิ่งที่ใช้บ่อยทั้งในคณิตศาสตร์และวิศวกรรม โดยการใช้อาร์เรย์สองมิติสามารถทำการบวกและคูณเมทริกซ์ได้อย่างง่ายดาย
ตัวอย่างที่ 1: การบวกเมทริกซ์
โปรแกรมตัวอย่างด้านล่างเป็นการบวกเมทริกซ์:
#include <stdio.h>
int main() {
// เมทริกซ์ 3x3 จำนวน 2 ชุด
int matrix1[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int matrix2[3][3] = {
{9, 8, 7},
{6, 5, 4},
{3, 2, 1}
};
int result[3][3];
// การบวกเมทริกซ์
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
result[i][j] = matrix1[i][j] + matrix2[i][j];
}
}
// แสดงผลลัพธ์
printf("ผลลัพธ์การบวกเมทริกซ์:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", result[i][j]);
}
printf("\n");
}
return 0;
}
ผลลัพธ์ที่ได้
ผลลัพธ์การบวกเมทริกซ์:
10 10 10
10 10 10
10 10 10
ในตัวอย่างนี้ เมทริกซ์สองชุดขนาด 3×3 ถูกบวกกันทีละตำแหน่ง และเก็บผลลัพธ์ไว้ในเมทริกซ์ใหม่
ตัวอย่างที่ 2: การคูณเมทริกซ์
โปรแกรมด้านล่างแสดงการคูณเมทริกซ์:
#include <stdio.h>
int main() {
// เมทริกซ์ 3x3 จำนวน 2 ชุด
int matrix1[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int matrix2[3][3] = {
{9, 8, 7},
{6, 5, 4},
{3, 2, 1}
};
int result[3][3] = {0};
// การคูณเมทริกซ์
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
result[i][j] += matrix1[i][k] * matrix2[k][j];
}
}
}
// แสดงผลลัพธ์
printf("ผลลัพธ์การคูณเมทริกซ์:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", result[i][j]);
}
printf("\n");
}
return 0;
}
ผลลัพธ์ที่ได้
ผลลัพธ์การคูณเมทริกซ์:
30 24 18
84 69 54
138 114 90
ตัวอย่างการสร้างกระดานเกม
อาร์เรย์สองมิติยังถูกใช้บ่อยในการจัดการกระดานเกม เช่น เกมโอเทลโล ด้านล่างเป็นตัวอย่างการสร้างกระดานเริ่มต้น:
ตัวอย่าง: การกำหนดค่าเริ่มต้นและการแสดงผลกระดานโอเทลโล
#include <stdio.h>
int main() {
// กระดานโอเทลโลขนาด 8x8
int board[8][8] = {0};
// กำหนดค่าตำแหน่งเริ่มต้น
board[3][3] = 1; // ขาว
board[3][4] = 2; // ดำ
board[4][3] = 2; // ดำ
board[4][4] = 1; // ขาว
// แสดงผลกระดาน
printf("สถานะเริ่มต้นของกระดานโอเทลโล:\n");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
printf("%d ", board[i][j]);
}
printf("\n");
}
return 0;
}
ผลลัพธ์ที่ได้
สถานะเริ่มต้นของกระดานโอเทลโล:
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 1 2 0 0 0
0 0 0 2 1 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
ในโปรแกรมนี้ 0 แทนค่าว่าง 1 แทนหมากขาว และ 2 แทนหมากดำ
7. ความสัมพันธ์ระหว่างอาร์เรย์สองมิติและตัวชี้ (Pointer)
ในภาษา C อาร์เรย์สองมิติและตัวชี้มีความสัมพันธ์กันอย่างใกล้ชิด การใช้ตัวชี้ช่วยให้จัดการอาร์เรย์ได้อย่างมีประสิทธิภาพมากขึ้น ส่วนนี้จะอธิบายตั้งแต่พื้นฐานจนถึงตัวอย่างการใช้งานจริง
ความสัมพันธ์พื้นฐานระหว่างอาร์เรย์สองมิติและตัวชี้
อาร์เรย์สองมิติจริง ๆ แล้วคือ “อาร์เรย์ของอาร์เรย์” ดังนั้นแถวแต่ละแถวสามารถมองเป็นตัวชี้ได้
ตัวอย่าง: โครงสร้างพื้นฐานของอาร์เรย์สองมิติ
การประกาศอาร์เรย์สองมิติ:
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
การจัดเก็บในหน่วยความจำ:
[1] [2] [3] [4] [5] [6]
array
ชี้ไปยังตำแหน่งแรกของอาร์เรย์array[i]
ชี้ไปยังแถวที่ iarray[i][j]
คือค่าในตำแหน่งนั้น
การอ้างอิงองค์ประกอบด้วยตัวชี้
สามารถใช้การคำนวณด้วย pointer เพื่อเข้าถึงค่าได้ดังนี้:
*(array[0] + 1) // เท่ากับ array[0][1]
*(*(array + 1) + 2) // เท่ากับ array[1][2]
การใช้อาร์เรย์สองมิติเป็นอาร์กิวเมนต์ของฟังก์ชัน
เมื่อส่งอาร์เรย์สองมิติให้กับฟังก์ชัน มักจะใช้ pointer เพื่อจัดการ เช่น:
ตัวอย่าง: การจัดการอาร์เรย์สองมิติภายในฟังก์ชัน
#include <stdio.h>
// ฟังก์ชันสำหรับพิมพ์อาร์เรย์
void printArray(int (*array)[3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", array[i][j]);
}
printf("\n");
}
}
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// ส่งอาร์เรย์ไปยังฟังก์ชัน
printArray(array, 2);
return 0;
}
ผลลัพธ์ที่ได้
1 2 3
4 5 6
จุดสำคัญ
int (*array)[3]
หมายถึง pointer ที่ชี้ไปยังอาร์เรย์ที่มี 3 คอลัมน์- ภายในฟังก์ชันสามารถเข้าถึงข้อมูลด้วยแถวและคอลัมน์ได้
การใช้งานอาร์เรย์สองมิติแบบไดนามิกด้วยตัวชี้
โดยใช้ตัวชี้ (pointer) สามารถสร้างอาร์เรย์สองมิติแบบไดนามิกได้ ทำให้จัดการขนาดของอาร์เรย์ได้ยืดหยุ่นมากขึ้น
ตัวอย่าง: การสร้างอาร์เรย์สองมิติแบบไดนามิก
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 2, cols = 3;
// จองหน่วยความจำแบบไดนามิก
int** array = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
array[i] = malloc(cols * sizeof(int));
}
// กำหนดค่า
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = i * cols + j + 1;
}
}
// แสดงผล
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", array[i][j]);
}
printf("\n");
}
// คืนหน่วยความจำ
for (int i = 0; i < rows; i++) {
free(array[i]);
}
free(array);
return 0;
}
ผลลัพธ์ที่ได้
1 2 3
4 5 6
8. วิธีการจองและคืนหน่วยความจำสำหรับอาร์เรย์สองมิติ
ในภาษา C สามารถใช้การจัดการหน่วยความจำแบบไดนามิกเพื่อสร้างอาร์เรย์สองมิติที่มีขนาดยืดหยุ่นตามต้องการในขณะรันโปรแกรม วิธีนี้ช่วยให้จัดการกรณีที่อาร์เรย์ขนาดคงที่ไม่เพียงพอ
พื้นฐานของการจัดการหน่วยความจำแบบไดนามิก
การจัดการหน่วยความจำแบบไดนามิกทำได้โดยใช้ฟังก์ชัน malloc
หรือ calloc
ซึ่งสามารถกำหนดขนาดของอาร์เรย์ได้ในระหว่างที่โปรแกรมทำงาน
วิธีการสร้างอาร์เรย์สองมิติแบบไดนามิก
มีสองวิธีหลัก ๆ สำหรับสร้างอาร์เรย์สองมิติแบบไดนามิก:
- ใช้ตัวชี้ของแถว (array of pointers)
- ใช้การจองหน่วยความจำแบบอาร์เรย์หนึ่งมิติแล้วเข้าถึงแบบสองมิติ
วิธีที่ 1: ใช้อาร์เรย์ของตัวชี้
วิธีนี้คือการจองหน่วยความจำสำหรับแต่ละแถวแยกกัน
ขั้นตอน
- จองหน่วยความจำสำหรับตัวชี้ตามจำนวนแถว
- จองหน่วยความจำให้แต่ละแถวตามจำนวนคอลัมน์
ตัวอย่าง: การจองและใช้อาร์เรย์สองมิติแบบไดนามิก
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
// จองตัวชี้สำหรับแต่ละแถว
int** array = malloc(rows * sizeof(int*));
// จองหน่วยความจำสำหรับแต่ละแถว
for (int i = 0; i < rows; i++) {
array[i] = malloc(cols * sizeof(int));
}
// กำหนดค่า
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = i * cols + j + 1;
}
}
// แสดงผล
printf("อาร์เรย์สองมิติที่จองแบบไดนามิก:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", array[i][j]);
}
printf("\n");
}
// คืนหน่วยความจำ
for (int i = 0; i < rows; i++) {
free(array[i]);
}
free(array);
return 0;
}
ผลลัพธ์ที่ได้
อาร์เรย์สองมิติที่จองแบบไดนามิก:
1 2 3 4
5 6 7 8
9 10 11 12
วิธีที่ 2: ใช้อาร์เรย์หนึ่งมิติแล้วเข้าถึงแบบสองมิติ
วิธีนี้คือการจองหน่วยความจำทั้งหมดสำหรับอาร์เรย์สองมิติในครั้งเดียว แล้วใช้การคำนวณดัชนีเพื่อเข้าถึงแบบสองมิติ
ขั้นตอน
- จองหน่วยความจำสำหรับ (จำนวนแถว × จำนวนคอลัมน์)
- คำนวณตำแหน่งด้วยสูตร
i * cols + j
ตัวอย่าง: การสร้างอาร์เรย์สองมิติจากอาร์เรย์หนึ่งมิติ
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
// จองหน่วยความจำทั้งหมด
int* array = malloc(rows * cols * sizeof(int));
// กำหนดค่า
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i * cols + j] = i * cols + j + 1;
}
}
// แสดงผล
printf("อาร์เรย์สองมิติที่สร้างจากอาร์เรย์หนึ่งมิติ:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", array[i * cols + j]);
}
printf("\n");
}
// คืนหน่วยความจำ
free(array);
return 0;
}
ผลลัพธ์ที่ได้
อาร์เรย์สองมิติที่สร้างจากอาร์เรย์หนึ่งมิติ:
1 2 3 4
5 6 7 8
9 10 11 12
ข้อควรระวังในการจัดการหน่วยความจำแบบไดนามิก
- หลีกเลี่ยง memory leak
หากไม่คืนหน่วยความจำหลังใช้งาน จะทำให้เกิด memory leak ต้องใช้free
ทุกครั้ง - ตรวจสอบข้อผิดพลาดในการจองหน่วยความจำ
เมื่อใช้malloc
หรือcalloc
ต้องตรวจสอบว่าได้หน่วยความจำสำเร็จหรือไม่ หากไม่สำเร็จจะคืนค่าNULL
if (array == NULL) {
printf("การจองหน่วยความจำล้มเหลว\n");
return 1;
}
- คำนวณขนาดหน่วยความจำให้ถูกต้อง
ต้องคำนวณขนาดที่ต้องการอย่างแม่นยำก่อนจองหน่วยความจำ
9. ข้อควรระวังในการใช้อาร์เรย์สองมิติ
แม้อาร์เรย์สองมิติจะเป็นโครงสร้างข้อมูลที่ทรงพลัง แต่หากใช้งานผิดวิธีอาจก่อให้เกิดบั๊กหรือ memory leak ได้ ส่วนนี้จะอธิบายข้อควรระวังและข้อผิดพลาดที่พบบ่อย
การป้องกันการเข้าถึงนอกขอบเขต (Out of bounds)
หากเข้าถึงอาร์เรย์นอกขอบเขต จะทำให้โปรแกรมทำงานผิดพลาดหรือเกิดการล่มได้
ตัวอย่าง: การเข้าถึงนอกขอบเขต
#include <stdio.h>
int main() {
int array[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// การเข้าถึงนอกขอบเขต (ผิดพลาด)
printf("%d\n", array[2][0]); // ไม่มีแถวที่ 3
return 0;
}
ในโค้ดนี้ array[2][0]
พยายามเข้าถึงแถวที่ไม่มีอยู่จริง ส่งผลให้เกิด undefined behavior
วิธีป้องกัน
- เขียนเงื่อนไขลูปไม่ให้เกินจำนวนแถวและคอลัมน์
- เพิ่มการตรวจสอบขอบเขตก่อนเข้าถึง
โค้ดที่แก้ไขแล้ว
for (int i = 0; i < 2; i++) { // ใช้จำนวนแถวจริง
for (int j = 0; j < 3; j++) {
printf("%d ", array[i][j]);
}
}
การป้องกัน memory leak
ถ้าไม่คืนหน่วยความจำที่จองแบบไดนามิก จะทำให้เกิด memory leak
ตัวอย่าง: memory leak
#include <stdlib.h>
int main() {
int rows = 2, cols = 3;
int** array = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
array[i] = malloc(cols * sizeof(int));
}
// คืนหน่วยความจำไม่ครบ
free(array);
return 0;
}
โค้ดนี้ไม่คืนหน่วยความจำของแต่ละแถว ทำให้เกิด memory leak
วิธีป้องกัน
ต้องคืนหน่วยความจำทุกครั้งตามลำดับ:
- คืนหน่วยความจำแต่ละแถว
- คืนหน่วยความจำตัวชี้หลัก
วิธีที่ถูกต้อง
for (int i = 0; i < rows; i++) {
free(array[i]);
}
free(array);
ข้อจำกัดของการเปลี่ยนขนาด
อาร์เรย์ที่ประกาศแบบคงที่ ไม่สามารถเปลี่ยนขนาดได้ หากต้องการขนาดยืดหยุ่นต้องใช้อาร์เรย์แบบไดนามิก
วิธีแก้
- ใช้การจัดการหน่วยความจำแบบไดนามิก
- หากต้องการเปลี่ยนขนาด ใช้
realloc
การลืมกำหนดค่าเริ่มต้น
หากไม่กำหนดค่าเริ่มต้นให้ตัวแปรอาร์เรย์ ค่าในหน่วยความจำอาจเป็น “ค่าขยะ”
ตัวอย่าง: ลืมกำหนดค่าเริ่มต้น
int array[2][3];
printf("%d\n", array[0][0]); // อาจได้ค่าที่ไม่คาดคิด
วิธีป้องกัน
- กำหนดค่าเริ่มต้นตอนประกาศ
int array[2][3] = {0}; // กำหนดค่าเป็น 0 ทุกตำแหน่ง
- หากใช้อาร์เรย์ไดนามิก ใช้
calloc
int* array = calloc(rows * cols, sizeof(int));
ประสิทธิภาพและ cache
เพื่อให้การเข้าถึงข้อมูลมีประสิทธิภาพ ควรเข้าถึงแบบ row-major
ตัวอย่าง: การเข้าถึงแบบ row-major
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", array[i][j]);
}
}
การเข้าถึงแบบ column-major (ที่ไม่แนะนำ)
ถ้าเข้าถึงแบบคอลัมน์ก่อน (column-major) จะทำให้ cache ทำงานไม่มีประสิทธิภาพ และช้าลง
เช็กลิสต์ข้อผิดพลาดที่พบบ่อย
ก่อนใช้งานอาร์เรย์สองมิติ ควรตรวจสอบประเด็นเหล่านี้:
- มีการเข้าถึงนอกขอบเขตหรือไม่
- มีการคืนหน่วยความจำครบถ้วนหรือไม่ (ถ้าเป็นแบบไดนามิก)
- มีการกำหนดค่าเริ่มต้นก่อนใช้งานหรือไม่
- หากต้องการเปลี่ยนขนาด มีการใช้
realloc
อย่างถูกต้องหรือไม่ - เข้าถึงข้อมูลแบบ row-major หรือไม่
10. สรุป
ในบทความนี้ เราได้อธิบายพื้นฐานของการใช้อาร์เรย์สองมิติในภาษา C ตั้งแต่การประกาศ การเข้าถึงองค์ประกอบ การวนลูป การใช้งานจริง ไปจนถึงข้อควรระวัง
- อาร์เรย์สองมิติ = ใช้ในการเก็บข้อมูลแบบตาราง
- การเข้าถึงองค์ประกอบ = ใช้อินเด็กซ์ [แถว][คอลัมน์]
- การประมวลผลข้อมูล = ใช้ลูปซ้อน
- การประยุกต์ใช้ = ตารางคูณ เกมเขาวงกต เมทริกซ์
- ข้อควรระวัง = หลีกเลี่ยงการเข้าถึงนอกขอบเขตและ memory leak
การเข้าใจและฝึกฝนเนื้อหาเหล่านี้ จะทำให้การเขียนโปรแกรม C ของคุณแข็งแกร่งยิ่งขึ้น
11. แบบฝึกหัด
สุดท้ายนี้ ลองทำแบบฝึกหัดเพื่อทดสอบความเข้าใจ:
- ประกาศอาร์เรย์สองมิติขนาด 3×3 และกำหนดค่าเริ่มต้นเป็น 1 ถึง 9 แล้วพิมพ์ออกมาในรูปแบบตาราง
- เขียนโปรแกรมคำนวณผลรวมของแต่ละแถวและพิมพ์ออกมา
- เขียนโปรแกรมที่ตรวจสอบว่าอาร์เรย์ 3×3 ที่ป้อนเข้ามาเป็น magic square หรือไม่
- เขียนโปรแกรมเกมเขาวงกตง่าย ๆ โดยใช้อาร์เรย์สองมิติ
หากทำแบบฝึกหัดเหล่านี้ได้ จะเข้าใจการใช้งานอาร์เรย์สองมิติในเชิงลึก และสามารถประยุกต์ใช้กับโปรแกรมจริงได้