Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C++

Array Decay ใน C++ (การเสื่อมสภาพของอาร์เรย์)

Array Decay คือ การที่ Array เลื่อมลดความเป็น Array ไปกลายเป็น Pointer to First Element เมื่อส่งผ่านฟังก์ชันหรือใช้ในบริบทบางอย่าง

ทำไมเกิด Array Decay?

  • C/C++ ไม่รองรับ Pass Array by Value (Copy ทั้งอาร์เรย์)
  • Array Parameter ใน Function จะถูกปรับเป็น Pointer อัตโนมัติ
  • void func(int arr[]) ≡ void func(int* arr) ≡ void func(int* arr, size_t size)

ตัวอย่าง Array Decay

#include <iostream>
using namespace std;

void byValue(int arr[10]) {        // จริงๆ เป็น int* arr
    cout << "Inside byValue: sizeof(arr) = " << sizeof(arr) << endl;  // 8 (pointer size)
}

void byPointer(int* arr) {
    cout << "Inside byPointer: sizeof(arr) = " << sizeof(arr) << endl;  // 8
}

void byReference(int (&arr)[10]) {  // Reference to Array[10]
    cout << "Inside byReference: sizeof(arr) = " << sizeof(arr) << endl;  // 40
}

int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    cout << "In main: sizeof(arr) = " << sizeof(arr) << endl;  // 40 (10 * 4)
    
    byValue(arr);      // Decay to pointer
    byPointer(arr);    // Explicit pointer
    byReference(arr);  // No decay - keeps array type
    
    return 0;
}

ผลลัพธ์

In main: sizeof(arr) = 40
Inside byValue: sizeof(arr) = 8
Inside byPointer: sizeof(arr) = 8
Inside byReference: sizeof(arr) = 40

3 วิธีป้องกัน Array Decay

1. Pass by Reference (แนะนำ)


// Fixed Size
void func(int (&arr)[10]);           // เฉพาะ int[10]
void func(int (&arr)[N]);            // Template Size

// Template Version (ยืดหยุ่น)
template<size_t N>
void func(int (&arr)[N]) {
    cout << "Size: " << N << endl;  // รู้ขนาดจริง!
}

// Usage
int a[5] = {1,2,3,4,5};
int b[10] = {1,2,3,4,5,6,7,8,9,10};
func(a);  // N=5
func(b);  // N=10

2. ใช้ std::array (C++11) - แนะนำมากที่สุด


#include <array>
#include <iostream>
using namespace std;

void func(const array<int, 10>& arr) {
    cout << "Size: " << arr.size() << endl;  // 10
    for (int x : arr) cout << x << " ";
}

int main() {
    array<int, 10> arr = {1,2,3,4,5,6,7,8,9,10};
    func(arr);  // ไม่ Decay, รู้ Size, มี Bounds Checking (at())
}

3. ใช้ std::vector (Dynamic Size)


#include <vector>
#include <iostream>
using namespace std;

void func(const vector<int>& vec) {
    cout << "Size: " << vec.size() << endl;
    for (int x : vec) cout << x << " ";
}

int main() {
    vector<int> vec = {1,2,3,4,5};
    func(vec);  // ไม่ Decay, Dynamic Size
}

สรุปวิธีส่ง Array เข้า Function

วิธี Syntax Decay? รู้ Size? ขนาด Dynamic? แนะนำ?
Array Paramvoid f(int a[])✓ Yes✗ No✗ No✗
Pointervoid f(int* a)✓ Yes✗ No✗ No✗
Pointer + Sizevoid f(int* a, size_t n)✓ YesManual✓ Yes△ (C-style)
Array Referencevoid f(int (&a)[N])✗ No✓ Yes (Template)✗ Fixed✓ (Fixed Size)
std::arrayvoid f(const array<T,N>&)✗ No✓ Yes✗ Fixed✓✓ Best
std::vectorvoid f(const vector<T>&)✗ No✓ Yes✓ Yes✓✓ Best
std::span (C++20)void f(span<T>)✗ No✓ Yes✓ Yes✓✓ Modern

std::span (C++20) - ทันสมัยที่สุด


#include <span>
#include <vector>
#include <array>
#include <iostream>
using namespace std;

void process(span<const int> s) {  // Non-owning View
    cout << "Size: " << s.size() << endl;
    for (int x : s) cout << x << " ";
    cout << endl;
}

int main() {
    int arr[5] = {1,2,3,4,5};
    vector<int> vec = {6,7,8,9,10};
    array<int, 5> arr2 = {11,12,13,14,15};
    
    process(arr);   // OK: Array → Span
    process(vec);   // OK: Vector → Span
    process(arr2);  // OK: Array → Span
    process({1,2,3}); // OK: Initializer List → Span
}

Array Decay ในบริบทอื่นๆ


int arr[10];

// 1. Function Parameter (เห็นแล้ว)
void f(int arr[]);  // decay

// 2. Assignment (ไม่ได้ - Array ไม่ Assign ได้)
// int b[10] = arr;  // ❌ Error

// 3. Conditional Expression
true ? arr : arr;  // Decay to int*

// 4. Ternary Operator
int* ptr = condition ? arr : nullptr;  // Decay

// 5. Comma Operator
(int*)0, arr;  // Decay

// 6. sizeof - ไม่ Decay!
sizeof(arr);  // 40 - รู้ขนาดจริง!

// 7. & (Address-of) - ไม่ Decay!
int (*ptrToArr)[10] = &arr;  // Type: Pointer to Array[10]

// 8. Range-based for - ไม่ Decay!
for (int x : arr) { }  // ใช้ Array Type โดยตรง

// 9. Template Type Deduction
template<typename T> void g(T x);
g(arr);  // T = int* (Decay!)

// Template Reference - ไม่ Decay
template<typename T, size_t N> void h(T (&)[N]);
h(arr);  // T=int, N=10

สรุป

  • Array Decay = Array → Pointer (สูญเสีย Size Info)
  • เกิดเมื่อ Pass to Function, ใช้ใน Expression หลายแบบ
  • ไม่เกิดกับ: sizeof, & (address-of), Range-based for, Template Reference
  • แก้ไขด้วย: std::array (Fixed), std::vector (Dynamic), std::span (C++20 View), Array Reference Template
  • Modern C++: ห้ามใช้ Raw Array Parameter ใช้ Container/Span แทน