DEV Community

Wutipong Wongsakuldej
Wutipong Wongsakuldej

Posted on

Array ใน C++

ในยุคเริ่มต้น เรื่อง array ของ C++ เนี่ยจริง ๆ เป็นฟีเจอร์ที่ยกมาจากภาษา C ซึ่งเวลาเราประกาศตัวแปรเป็น array เราก็จะเขียนแบบนี้

int anArray[20];
Enter fullscreen mode Exit fullscreen mode

เราก็จะได้ 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];
Enter fullscreen mode Exit fullscreen mode

ใน C++ ไม่ได้

จุดที่ยากอีกอย่างคือ ตัวขนาดของ array จะต้องรู้ตั้งแต่ตอนที่คอมไพล์โค๊ดเลย ไม่สามารถที่จะระบุภายหลังได้ (เพราะคอมไพล์เลอร์จะกันพื้นที่ให้ตอนนั้นเลย)

ตัวอย่างข้างล่างนี้คอมไพล์ไม่ผ่าน

const int size = 20;
int array[size]; // error: ตอนคอมไพล์โค๊ดไม่รู้ว่า size มีค่าเท่าไหร่
Enter fullscreen mode Exit fullscreen mode

Dynamic Array

ในภาษา C เนี่ย เราสามารถใช้ pointer ต่าง array ได้ คือเราเห็นตัวแปรไหนเป็น pointer เนี่ย สามารถใส่ subscript ตามหลังได้

int* anArray = (int*) malloc(sizeof(int) * 10); // หรือใช้ new int[10] แทนในภาษา C++
anArray[30] = 100;
Enter fullscreen mode Exit fullscreen mode

แน่นอนว่าถ้าเราไปเข้าถึงพื้นที่ที่เราไม่ได้กันไว้ให้ตัวแปรนั้น มันก็จะพัง เท่านั้นเอง

line 110:    12 Segmentation fault
Enter fullscreen mode Exit fullscreen mode

ซึ่งการใช้งานในลักษณะนี้จะเหมือนกับ 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);
Enter fullscreen mode Exit fullscreen mode

หรืออีกวิธีคือ บังคับว่า ต้องปิดท้ายด้วยค่า 0 เท่านั้น ซึ่งพวกฟังก์ชันที่เป็นชุดของ string ในภาษา C จะใช้วิธีนี้ทั้งหมด (เรียกว่า null-terminated string)

size_t strlen ( const char * str );
Enter fullscreen mode Exit fullscreen mode

ซึ่ง มันกลายเป็นว่า วิธีการใช้ array หรือ dynamic array มันก็เลยวุ่นวายแบบสุด ๆ ไปเลย

Array ใน C++ ยุคใหม่

ผ่านมาถึงปี 2007 เราคงคิดว่า เฮ้ย ... มันต้องเปลี่ยนแล้วมั้ย ภาษาอื่นเค้า array ใช้ง่ายมาก ทำไม C++ มันใช้ยากอย่างงี้

ก็เลยเกิด type ที่ชื่อ std::array ขึ้นมา

ผู้ใช้ก็ เฮ ... มีไทป์ใหม่แล้ว มันต้องใช้ง่ายเหมือนภาษาอื่นแน่เลย คำตอบคือ ไม่ใช่ครับ ไอ้ std::array เนี่ย จริง ๆ แล้วมันคือ wrapper ของไอ้ array แบบ C ธรรมดานี่ล่ะ

ดังนั้นข้อจำกัดที่ว่า ขนาดของ array เป็นส่วนหนึ่งของ type ก็ยังคงจริงอยู่เช่นเดิม

std::array<int, 10> anArray;
Enter fullscreen mode Exit fullscreen mode

สิ่งที่ง่ายขึ้นคือ เราสามารถบอกขนาดของ array ได้จากตัวแปรนั้นละ และตัว std::array นี่ก็มีวิธีการใช้งานเหมือนกับ STL container อื่น ๆ ของ standard library ดังนั้นมันก็จะใช้ง่ายขึ้น มีเคสพิเศษน้อยลง

ทำ re-assignment ก็ยังได้เลย

std::array<int, 12> a = {0};
a = {20, 20};
Enter fullscreen mode Exit fullscreen mode

แต่ไม่ได้หมายความว่า 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);
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)