C++: المؤشرات والمصفوفات

آخر تحديث: 2026-08-26

في الدرس 16، تعلمنا عن المصفوفات، لكننا لم نناقش ما هو اسم المصفوفة فعليًا.

في الواقع، اسم المصفوفة هو مؤشر — يخزن عنوان العنصر الأول للمصفوفة.

فهم هذا يساعد في تفسير لماذا المصفوفات "تتحول إلى مؤشرات" عند تمريرها كمعلمات.



1. اسم المصفوفة هو مؤشر

(1) 1.1 الحقيقة الأساسية

اسم المصفوفة هو مؤشر للعنصر الأول.

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "arr = " << arr << std::endl;
 std::cout << "&arr[0] = " << &arr[0] << std::endl;
 // Both output the same! Because arr is &arr[0]
 
 return 0;
}

الخرج (مثال):

TEXT 📖 للعرض فقط
arr = 0x7ffd4a3b
&arr[0] = 0x7ffd4a3b

💡 نقطة مهمة: arr و &arr[0] هما نفس العنوان.



2. الوصول إلى عناصر المصفوفة بالمؤشرات

(1) 2.1 الاستخدام الأساسي

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // p points to first element
 
 // Method 1: Subscript(same as array)
 std::cout << "arr[0] = " << p[0] << std::endl; // 10
 std::cout << "arr[1] = " << p[1] << std::endl; // 20
 
 // Method 2: Dereference
 std::cout << "*p = " << *p << std::endl; // 10
 std::cout << "*(p+1) = " << *(p+1) << std::endl; // 20
 
 return 0;
}

💡 نقطة مهمة: p[i] هي في الواقع *(p+i) (حساب المؤشرات سيُشرح لاحقًا).



3. حساب المؤشرات

(1) 3.1 الجمع والطرح مع المؤشرات

عند جمع أو طرح عدد صحيح من مؤشر، لا يُضاف/يُطرح من قيمة العنوان ببساطة — بل يُضاف/يُطرح عدد × sizeof(نوع) بايت.

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr; // Pointing to arr[0]
 
 std::cout << "p = " << p << std::endl; // Assume 0x1000
 std::cout << "p + 1 = " << p + 1 << std::endl; // 0x1004(Not 0x1001!)
 std::cout << "p + 2 = " << p + 2 << std::endl; // 0x1008
 
 return 0;
}

كيف تعمل:


(2) 3.2 طرح المؤشرات

طرح مؤشرين يعطي المسافة بينهما (عدد العناصر)، وليس عدد البايتات.

CPP
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p1 = &arr[0]; // Pointing to arr[0]
 int* p2 = &arr[3]; // Pointing to arr[3]
 
 std::cout << "p2 - p1 = " << p2 - p1 << std::endl; // 3(Number of elements)
 
 return 0;
}

💡 نقطة مهمة: طرح المؤشرات له معنى فقط عندما يشيران إلى عناصر من نفس المصفوفة.



4. اجتياز مصفوفة بالمؤشرات (ثلاث طرق)

▶ مثال 1: اجتياز مصفوفة (صعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 // Method 1: Subscript(Most common)
 std::cout << "Method 1: Subscript" << std::endl;
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 // Method 2: Pointer arithmetic
 std::cout << "Method 2: Pointer arithmetic" << std::endl;
 for (int* p = arr; p < arr + 5; p++) {
 std::cout << *p << " ";
 }
 std::cout << std::endl;
 
 // Method 3: Use standard library function (will learn later)
 // std::cout << "Method 3: std::begin/end" << std::endl;
 // for (int* p = std::begin(arr); p != std::end(arr); p++) {
 // std::cout << *p << " ";
 // }
 
 return 0;
}

الخرج:

TEXT 📖 للعرض فقط
Method 1: Subscript
10 20 30 40 50 
Method 2: Pointer arithmetic
10 20 30 40 50 

💡 توصية: الطريقة 1 (الفهرس) هي الأكثر بديهية — فضّلها. الطريقة 2 (حساب المؤشرات) شائعة في الكود منخفض المستوى.



5. تحلل المصفوفة عند تمريرها كمعلمة

(1) 5.1 المشكلة: المصفوفات تتحول إلى مؤشرات

عند تمرير مصفوفة إلى دالة، تتحول إلى مؤشر (تفقد معلومات الحجم).

CPP
#include <iostream>

void printSize(int arr) {
 // arr is actually a pointer, not an array!
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 8(64-bit system, pointer size)
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20(5 x 4 = 20 bytes)
 printSize(arr);
 
 return 0;
}

💡 نقطة مهمة: داخل دالة printSize، arr هو في الواقع مؤشر، لذا sizeof(arr) تُرجع حجم المؤشر (8 بايت)، وليس حجم المصفوفة (20 بايت).


(2) 5.2 الحل: تمرير حجم المصفوفة

TEXT 📖 للعرض فقط
#include <iostream>

// Must additionally pass array size
void printArray(int arr, int size) {
 for (int i = 0; i < size; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 printArray(arr, 5); // Pass array name (which is first element address)
 return 0;
}

💡 نصيحة: لاحقًا ستتعلم std::array و std::vector، اللذين يحافظان على معلومات الحجم وهما أكثر أمانًا.



6. تطبيق: عكس مصفوفة

▶ مثال 2: عكس مصفوفة بالمؤشرات (صعوبة ⭐⭐)

CPP
#include <iostream>

void reverseArray(int* arr, int size) {
 int* left = arr; // Pointing to first element
 int* right = arr + size - 1; // Pointing to last element
 
 while (left < right) {
 // Swap values pointed to by left and right
 int temp = *left;
 *left = *right;
 *right = temp;
 
 left++; // Move right
 right--; // Move left
 }
}

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 
 std::cout << "Before reversal: ";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 reverseArray(arr, 5);
 
 std::cout << "After reversal: ";
 for (int i = 0; i < 5; i++) {
 std::cout << arr[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
Before reversal: 10 20 30 40 50 
After reversal: 50 40 30 20 10 


7. الأخطاء الشائعة

(1) 7.1 خروج المؤشر عن الحدود

TEXT 📖 للعرض فقط
#include <iostream>

int main() {
 int arr[5] = {10, 20, 30, 40, 50};
 int* p = arr;
 
 std::cout << *(p + 10) << std::endl; // ❌ Out of bounds! p+10 points outside array
 // Outputs garbage value, or program crashes
 
 return 0;
}

💡 نصيحة: C++ لا تتحقق تلقائيًا من حدود المؤشرات! هذا أحد أسباب سرعة C++، لكنه أيضًا مصدر شائع للأخطاء.


(2) 7.2 طرح مؤشرات بلا معنى

CPP
#include <iostream>

int main() {
 int x = 5;
 int y = 10;
 int* p1 = &x;
 int* p2 = &y;
 
 std::cout << p2 - p1 << std::endl; // ❌ Undefined behavior! p1 and p2 point to different arrays
 // Only subtracting pointers to the same array is meaningful
 
 return 0;
}

❓ أسئلة شائعة

س لماذا arr و &arr لهما نفس القيمة لكن أنواع مختلفة؟
ج > - arr من نوع int* (مؤشر إلى int) > - &arr من نوع int(*)[5] (مؤشر إلى "مصفوفة من 5 أعداد صحيحة")
س هل يمكن استخدام * و / في حساب المؤشرات؟
ج لا! المؤشرات يمكنها فقط: > - جمع/طرح أعداد صحيحة (p + n، p - n) > - طرح مؤشرين (p2 - p1) > - مقارنة (p1 < p2)

▶ مثال 3: اجتياز مصفوفة بالمؤشرات (صعوبة ⭐)

CPP
#include <iostream>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int* p = arr;

    for (int i = 0; i < 5; i++) {
        std::cout << "*(p+" << i << ")=" << *(p + i) << std::endl;
    }

    return 0;
}
▶ جرّب الكود

الخرج:

TEXT 📖 للعرض فقط
*(p+0)=10
*(p+1)=20
*(p+2)=30
*(p+3)=40
*(p+4)=50
💡 نصيحة: *(p + i) مكافئ لـ p[i] أو arr[i]. جمع/طرح الأعداد الصحيحة من المؤشرات يراعي حجم النوع تلقائيًا.



📖 ملخص

📝 تمارين

  1. أساسي (صعوبة ⭐): عرّف مصفوفة int بقيم arr[5] = {1, 2, 3, 4, 5}، ثم عرّف مؤشر int* باسم p يشير إلى arr. أخرج جميع العناصر باستخدام المؤشر.

  2. متوسط (صعوبة ⭐⭐): اكتب دالة int* findElement(int* arr, int size, int target) تبحث عن قيمة هدف في مصفوفة. إذا وُجدت، أرجع مؤشرًا إلى ذلك العنصر؛ إذا لم تُوجد، أرجع nullptr.

  3. تحدي (صعوبة ⭐⭐⭐): اكتب برنامجًا ينفذ "الفرز الفقاعي" باستخدام المؤشرات:

  4. اجتز المصفوفة، قارن العناصر المتجاورة

  5. إذا كانت بترتيب خاطئ، بدّل بينها

  6. كرر هذه العملية حتى تُفرز المصفوفة


8. 🚀 الخطوة التالية

الآن بعد أن تعلمت عن المؤشرات والمصفوفات، لنتابع إلى حساب المؤشرات المتقدم (الدرس 24) — نظرة أعمق على أنواع المؤشرات، مؤشرات void*، والمؤشرات متعددة المستويات.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%