Wutipong Wongsakuldej
Posted on February 6, 2023
ในยุคเริ่มต้น เรื่อง array ของ C++ เนี่ยจริง ๆ เป็นฟีเจอร์ที่ยกมาจากภาษา C ซึ่งเวลาเราประกาศตัวแปรเป็น array เราก็จะเขียนแบบนี้
int anArray[20];
เราก็จะได้ array ขนาด 20 ช่อง
ตรงนี้เมื่อเทียบกับภาษาสมัยใหม่ อย่างพวก C#, Java หรือแม้แต่ JavaScript ความแตกต่างอย่างหนึ่งของ array แบบ C คือ ขนาดของ array จะเป็นส่วนหนึ่งของ type ด้วย
แล้วก็ ตอนที่ initialize ค่าของ array จะต้องทำตอนที่ประกาศทันที ถ้าประกาศเสร็จแล้วเราอยากเปลี่ยนค่า ก็ต้องเปลี่ยนค่าทีละช่อง ราวกับว่าแต่ละช่องเหมือนเป็นตัวแปรตัวหนึ่ง
สาเหตุหนึ่งคือ array ใน C++ เป็น value type เมื่อคุณสร้าง array ขึ้นมาแล้ว ตัวภาษาจะกันพื้นที่ส่วนหนึ่งในหน่วยความจำไว้ให้เลย เหมือนมีตัวแปร int หลาย ๆ ตัวติด ๆ กันตรงนั้น ต่างกับภาษาอื่นที่ array มักจะเป็น reference ไปหาพื้นที่ที่อยู่ใน heap แทน
ดังนั้นเราจะเขียนแบบ (ยกตัวอย่าง java)
int[] anArray = new int[20];
anArray = new int[50];
ใน C++ ไม่ได้
จุดที่ยากอีกอย่างคือ ตัวขนาดของ array จะต้องรู้ตั้งแต่ตอนที่คอมไพล์โค๊ดเลย ไม่สามารถที่จะระบุภายหลังได้ (เพราะคอมไพล์เลอร์จะกันพื้นที่ให้ตอนนั้นเลย)
ตัวอย่างข้างล่างนี้คอมไพล์ไม่ผ่าน
const int size = 20;
int array[size]; // error: ตอนคอมไพล์โค๊ดไม่รู้ว่า size มีค่าเท่าไหร่
Dynamic Array
ในภาษา C เนี่ย เราสามารถใช้ pointer ต่าง array ได้ คือเราเห็นตัวแปรไหนเป็น pointer เนี่ย สามารถใส่ subscript ตามหลังได้
int* anArray = (int*) malloc(sizeof(int) * 10); // หรือใช้ new int[10] แทนในภาษา C++
anArray[30] = 100;
แน่นอนว่าถ้าเราไปเข้าถึงพื้นที่ที่เราไม่ได้กันไว้ให้ตัวแปรนั้น มันก็จะพัง เท่านั้นเอง
line 110: 12 Segmentation fault
ซึ่งการใช้งานในลักษณะนี้จะเหมือนกับ array ในภาษาสมัยใหม่อย่างที่ว่าข้างบนครับ คือเราใช้ pointer ที่ชี้ไปหาพื้นที่หน่วยความจำที่เป็น array อีกทีนึง
ดังนั้น สำหรับคนที่เขียนภาษาอื่นมา ถ้าจะเขียน array บางทีเราอาจจะต้องใช้รูป dynamic array แบบนี้ครับ ไม่ใช่รูป array ปรกติ
อ้อ อีกอย่างครับ ลืม ... array สามารถแปลงร่างเป็น pointer ได้ครับ (แต่แปลงกลับไม่ได้นะ)
ปัญหาของการใช้ array แบบภาษา C
อย่างที่เล่าข้างบน Array ในภาษา C นั้น ขนาดของ array จะเป็นส่วนหนึ่งของ type ด้วย แต่ ... เราไม่สามารถบอกจากตัวแปรนั้น ๆ ได้ว่า array นี้มีขนาดเท่าไหร่
อันนี้รวมทั้ง array และ dynamic array ที่มาในรูป pointer ด้วย
คือคนเขียนจะต้องรู้เองว่า array เนี่ยมีขนาดเท่าไหร่ หรือ จะต้องส่งตัวแปรอีกตัวมาบอกว่า array นี้มีขนาดเท่าไหร่
void sort(int* array, int count);
หรืออีกวิธีคือ บังคับว่า ต้องปิดท้ายด้วยค่า 0 เท่านั้น ซึ่งพวกฟังก์ชันที่เป็นชุดของ string ในภาษา C จะใช้วิธีนี้ทั้งหมด (เรียกว่า null-terminated string)
size_t strlen ( const char * str );
ซึ่ง มันกลายเป็นว่า วิธีการใช้ array หรือ dynamic array มันก็เลยวุ่นวายแบบสุด ๆ ไปเลย
Array ใน C++ ยุคใหม่
ผ่านมาถึงปี 2007 เราคงคิดว่า เฮ้ย ... มันต้องเปลี่ยนแล้วมั้ย ภาษาอื่นเค้า array ใช้ง่ายมาก ทำไม C++ มันใช้ยากอย่างงี้
ก็เลยเกิด type ที่ชื่อ std::array
ขึ้นมา
ผู้ใช้ก็ เฮ ... มีไทป์ใหม่แล้ว มันต้องใช้ง่ายเหมือนภาษาอื่นแน่เลย คำตอบคือ ไม่ใช่ครับ ไอ้ std::array
เนี่ย จริง ๆ แล้วมันคือ wrapper ของไอ้ array แบบ C ธรรมดานี่ล่ะ
ดังนั้นข้อจำกัดที่ว่า ขนาดของ array เป็นส่วนหนึ่งของ type ก็ยังคงจริงอยู่เช่นเดิม
std::array<int, 10> anArray;
สิ่งที่ง่ายขึ้นคือ เราสามารถบอกขนาดของ array ได้จากตัวแปรนั้นละ และตัว std::array
นี่ก็มีวิธีการใช้งานเหมือนกับ STL container อื่น ๆ ของ standard library ดังนั้นมันก็จะใช้ง่ายขึ้น มีเคสพิเศษน้อยลง
ทำ re-assignment ก็ยังได้เลย
std::array<int, 12> a = {0};
a = {20, 20};
แต่ไม่ได้หมายความว่า array ที่มี element type เดียวกันแต่ละคนละขนาด จะ assign กันได้นะครับ
อ้าวแล้ว Dynamic Array ล่ะ ?
จริง ๆ Dynamic Array ของ C++ นี่เกิดก่อน std::array
อีก นั่นคือ std::vector
ที่อยู่ในชุด STL นั่นเอง
หลายคนที่ไม่ได้ศึกษาจริงจัง จะไม่รู้ว่ามันใช้แทนกันได้ คือ std::vector
เนี่ย ไส้ในมันคือ dynamic array และมันการันตีว่า ค่าต่าง ๆ ที่อยู่ข้างในนั้นจะวางอยู่ชิดติดกันเสมอ
คือที่เราเห็นว่า เฮ้ยเราสามารถแทรกค่าได้ สามารถนำค่าออกได้ สามารถขยายขนาดได้ ฯลฯ เนี่ย มันเกิดจากฟังก์ชันต่าง ๆ เค้า implement ข้างในให้หมดเลย เราเลยไม่ต้องแบบ realloc()
หรือย้ายค่าไปมา ทั้งหมด คลาสนี้ทำให้หมดเลย
ในทางกลับกัน ถ้าเราใช้ std::vector
เหมือนพวก container อื่น ๆ เช่นพวก linked list ก็อาจจะทำให้ performance หดได้เหมือนกัน
เวลาใช้ vector ให้คิดว่ามันคือ dynamic array เข้าไว้ครับ
แน่นอนว่ามันมีความแตกต่างอยู่บ้างครับ อย่าง สมมติเราระบุขนาด std::vector
ไว้แบบนี้
std::vector<int> anVector(20);
ไอ้ตัว vector เนี่ย แทนที่มันจะกันที่ไว้แค่ 20 มันจะกันเผื่อไว้ประมาณนึง คือเค้าออกแบบมาเพื่อว่าเราอาจจะมีการเพิ่มหรือลด element ใน vector ไว้ได้วย ซึ่งถ้าเราซีเรียสเรื่องการใช้งานหน่วยความจำมาก ๆ การใช้ vector ก็อาจจะไม่เหมาะครับ
Posted on February 6, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.