Kodax

المؤشرات والمصفوفات — المؤشرات والذاكرة

المؤشرات والمصفوفات

اسم المصفوفة يتحول إلى مؤشر

int nums[4] = {10, 20, 30, 40};
int* p = nums;        // مثل &nums[0]
cout << *p;           // 10

في معظم التعابير يتحول اسم المصفوفة إلى مؤشر لأول عنصر. لهذا عند تمرير مصفوفة لدالة تستلم مؤشراً وتفقد معرفة حجمها.

حساب المؤشرات (Pointer Arithmetic)

p + 1 لا تعني العنوان التالي بايتاً، بل العنصر التالي: يتقدم بمقدار sizeof(int).

int arr[] = {10, 20, 30};
int* p = arr;
cout << *(p + 2) << '\n';   // 30
cout << p[2] << '\n';       // 30 — الصيغتان متطابقتان حرفياً
p++;                         // يشير الآن لـ 20

arr[i] هي في الحقيقة اختصار لـ *(arr + i).

المرور بالمؤشرات

for (int* it = nums; it != nums + 4; ++it) {
    cout << *it;
}

nums + 4 يشير "بعد آخر عنصر": مسموح أن تحسبه وتقارن به، غير مسموح أن تقرأ منه. هذا هو أساس فكرة begin() و end() في المكتبة القياسية.

sizeof والمصفوفات

داخل نفس النطاق sizeof(nums) / sizeof(nums[0]) يعطي عدد العناصر. لكن داخل دالة استلمت int* arr فإن sizeof(arr) حجم المؤشر فقط (8 بايت عادة)! لذلك يُمرَّر الحجم دائماً مع المصفوفة.

النصوص بأسلوب C

const char* name = "Ali"; مصفوفة أحرف تنتهي بالحرف '\0'. ستراها في الكود القديم ومكتبات C. في الكود الجديد استخدم std::string.

⚠️ تجاوز الحدود

حساب مؤشر يخرج من المصفوفة ثم القراءة منه سلوك غير معرّف، تماماً كفهرس خارج الحدود.

البديل الحديث

std::vector و std::span (C++20) يحملان المؤشر والحجم معاً، فيحلان مشكلة فقدان الحجم.

تمرين

اكتب int sumArray(const int* arr, int size) تمر على العناصر بمؤشر متحرك (بدون أقواس [ ])، و void reverse(int* arr, int size) تعكس المصفوفة بمؤشرين من الطرفين.

#include <iostream>
using namespace std;

// دالة تستقبل مصفوفة (تتحول لمؤشر)
void printArray(int* arr, int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// المرور باستخدام مؤشر
void printWithPointer(int* start, int* end) {
    while (start != end) {
        cout << *start << " ";
        start++;
    }
    cout << endl;
}

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = 5;
    
    // اسم المصفوفة = مؤشر للعنصر الأول
    cout << "arr = " << arr << endl;
    cout << "&arr[0] = " << &arr[0] << endl;
    cout << "نفس العنوان! ✓" << endl;
    
    // الوصول بعدة طرق
    cout << "\n=== طرق الوصول ===" << endl;
    cout << "arr[0] = " << arr[0] << endl;
    cout << "*arr = " << *arr << endl;
    cout << "*(arr+0) = " << *(arr+0) << endl;
    
    cout << "arr[2] = " << arr[2] << endl;
    cout << "*(arr+2) = " << *(arr+2) << endl;
    
    // Pointer Arithmetic
    cout << "\n=== Pointer Arithmetic ===" << endl;
    int* ptr = arr;
    
    cout << "*ptr = " << *ptr << endl;        // 10
    cout << "*(ptr+1) = " << *(ptr+1) << endl; // 20
    cout << "*(ptr+2) = " << *(ptr+2) << endl; // 30
    
    ptr++;  // يتحرك بحجم int (4 bytes)
    cout << "بعد ptr++: *ptr = " << *ptr << endl;  // 20
    
    ptr += 2;
    cout << "بعد ptr+=2: *ptr = " << *ptr << endl; // 40
    
    // المسافة بين مؤشرين
    int* start = arr;
    int* end = arr + 4;
    cout << "\nالمسافة: " << (end - start) << " عناصر" << endl;
    
    // المرور على المصفوفة
    cout << "\n=== المرور ===" << endl;
    
    cout << "بالفهرس: ";
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    cout << "بالمؤشر: ";
    for (int* p = arr; p < arr + size; p++) {
        cout << *p << " ";
    }
    cout << endl;
    
    // استخدام الدوال
    cout << "\nباستخدام دالة: ";
    printArray(arr, size);
    
    cout << "بمؤشري البداية والنهاية: ";
    printWithPointer(arr, arr + size);
    
    return 0;
}
اختبر فهمك