Array ใน C++
Tags: thai, cpp
Published: 2023-02-06 14:24:52

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

```cpp
int anArray[20];
``` 

เราก็จะได้ array ขนาด 20 ช่อง 

ตรงนี้เมื่อเทียบกับภาษาสมัยใหม่ อย่างพวก C#, Java หรือแม้แต่ JavaScript ความแตกต่างอย่างหนึ่งของ array แบบ C คือ ขนาดของ array จะเป็นส่วนหนึ่งของ type ด้วย

แล้วก็ ตอนที่ initialize ค่าของ array จะต้องทำตอนที่ประกาศทันที ถ้าประกาศเสร็จแล้วเราอยากเปลี่ยนค่า ก็ต้องเปลี่ยนค่าทีละช่อง ราวกับว่าแต่ละช่องเหมือนเป็นตัวแปรตัวหนึ่ง

สาเหตุหนึ่งคือ array ใน C++ เป็น value type เมื่อคุณสร้าง array ขึ้นมาแล้ว ตัวภาษาจะกันพื้นที่ส่วนหนึ่งในหน่วยความจำไว้ให้เลย เหมือนมีตัวแปร int หลาย ๆ ตัวติด ๆ กันตรงนั้น ต่างกับภาษาอื่นที่ array มักจะเป็น reference ไปหาพื้นที่ที่อยู่ใน heap แทน

ดังนั้นเราจะเขียนแบบ (ยกตัวอย่าง java)

```java 
int[] anArray = new int[20];
anArray = new int[50];
``` 

ใน C++ ไม่ได้

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

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

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

## Dynamic Array

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

```c
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 นี้มีขนาดเท่าไหร่

```cpp
void sort(int* array, int count);
```

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

```cpp
size_t strlen ( const char * str );
```

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

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

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

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

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

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

```cpp
std::array<int, 10> anArray;
```

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

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

```cpp
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` ไว้แบบนี้

```cpp 
std::vector<int> anVector(20);
```

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