C: المصفوفات
تخيّل صفًا من الخزائن، كل واحدة مرقّمة بدءًا من 0، تخزن عناصر من نفس النوع — تلك هي المصفوفة. تتيح لك إدارة مجموعة من البيانات من نفس النوع تحت اسم واحد.
1. تعريف وتهيئة المصفوفات أحادية البعد
المصفوفة هي مجموعة مرتّبة من عناصر من نفس النوع، مخزّنة بشكل متجاور في الذاكرة. عند تعريف مصفوفة، تحدد نوع العناصر وعددها.
int scores[5];
هذا يعرّف مصفوفة من 5 عناصر من نوع int، لكن المحتويات غير مهيأة وقيمها غير محددة. يُنصح بتهيئة المصفوفة عند التعريف:
int scores[5] = {90, 85, 78, 92, 88};
إذا كانت قائمة التهيئة أقصر من المصفوفة، تُعيَّن العناصر المتبقية تلقائيًا إلى 0:
int data[5] = {10, 20};
الآن data[2] و data[3] و data[4] جميعها 0. باستخدام هذه الخاصية، يمكنك تصفير مصفوفة كاملة:
int zeros[100] = {0};
إذا قدمت قائمة تهيئة كاملة، يمكنك حذف طول المصفوفة ويحسبه المترجم تلقائيًا:
int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
2. الوصول بالدليل
يُوصل إلى عناصر المصفوفة بالدليل (الفهرس)، بدءًا من 0. لمصفوفة طولها N، تتراوح الأدلة الصالحة من 0 إلى N-1.
int scores[5] = {90, 85, 78, 92, 88};
int first = scores[0];
int last = scores[4];
scores[2] = 100;
يمكن أن يكون الدليل تعبيرًا عددًا صحيحًا، مما يسهّل الاجتياز بالحلقات:
int i = 3;
int val = scores[i];
3. مخاطر تجاوز الحدود
لا تتحقق C مما إذا كانت أدلة المصفوفة متجاوزة للحدود. الوصول إلى عناصر متجاوزة هو سلوك غير معرّف — قد يقرأ بيانات عشوائية، أو يعدّل متغيرات أخرى، أو ينهار البرنامج.
int arr[3] = {10, 20, 30};
printf("%d\n", arr[3]);
printf("%d\n", arr[-1]);
كلتا السطرين تصلان إلى مواقع غير مشروعة. لن يحذرك المترجم، لكن النتائج غير متوقعة.
< بدلًا من <= عند المقارنة مع الطول.
4. اجتياز المصفوفة
استخدام حلقة for لمعالجة كل عنصر بالتسلسل هو أكثر عمليات المصفوفات شيوعًا:
int scores[5] = {90, 85, 78, 92, 88};
int i;
for (i = 0; i < 5; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}
جمع جميع العناصر:
int sum = 0;
for (i = 0; i < 5; i++) {
sum += scores[i];
}
printf("Total: %d\n", sum);
إيجاد القيمة القصوى:
int max = scores[0];
for (i = 1; i < 5; i++) {
if (scores[i] > max) {
max = scores[i];
}
}
printf("Highest: %d\n", max);
▶ مثال
حساب متوسط الدرجات وعدد الرسوب:
#include <stdio.h>
int main(void) {
double scores[] = {78.5, 92.0, 55.5, 88.0, 43.0, 67.5, 90.0};
int len = sizeof(scores) / sizeof(scores[0]);
double sum = 0;
int fail = 0;
int i;
for (i = 0; i < len; i++) {
sum += scores[i];
if (scores[i] < 60.0) {
fail++;
}
}
printf("Average: %.1f\n", sum / len);
printf("Failing: %d\n", fail);
return 0;
}
Average: 73.5
Failing: 2
هنا، sizeof(scores) / sizeof(scores[0]) يحسب عدد العناصر ديناميكيًا، لذا لا تحتاج لتحديث شرط الحلقة عند تغيير محتويات المصفوفة.
5. المصفوفات كمعاملات دوال
عند تمرير مصفوفة إلى دالة، لا تُنسخ المصفوفة بالكامل — بل يُمرَّر عنوان العنصر الأول فقط. هذا يعني أن الدالة لا يمكنها تحديد طول المصفوفة باستخدام sizeof؛ يجب تمرير الطول كمعامل منفصل.
void print_array(int arr[], int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
المعامل int arr[] يكافئ int *arr، مما يعكس "الانحلال" إلى مؤشر. تعديل عناصر المصفوفة داخل الدالة يؤثر على المصفوفة الأصلية لأن كلاهما يعملان على نفس الذاكرة.
void double_values(int arr[], int len) {
int i;
for (i = 0; i < len; i++) {
arr[i] *= 2;
}
}
int main(void) {
int data[] = {1, 2, 3, 4, 5};
double_values(data, 5);
return 0;
}
بعد الاستدعاء، تحتوي data على {2, 4, 6, 8, 10}.
6. المصفوفات ثنائية البعد
يمكن اعتبار المصفوفة ثنائية البعد "مصفوفة من المصفوفات"، وتُستخدم شيوعًا لتمثيل المصفوفات الرياضية أو البيانات الجدولية.
int matrix[3][4];
هذا يعرّف مصفوفة ثنائية البعد من 3 صفوف و4 أعمدة. يمكن تجميع التهيئة حسب الصف:
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
يمكن أيضًا كتابتها مسطحة — التأثير هو نفسه:
int matrix[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
الوصول إلى العناصر بدليلين:
int val = matrix[1][2];
matrix[0][0] = 100;
اجتياز مصفوفة ثنائية البعد يتطلب حلقة مزدوجة:
int i, j;
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
printf("%4d", matrix[i][j]);
}
printf("\n");
}
عند التهيئة، يمكنك حذف عدد الصفوف ولكن لا يمكنك حذف عدد الأعمدة:
int m[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8}
};
يستنتج المترجم صفين من بيانات التهيئة.
▶ مثال
حساب متوسط الدرجات عبر 4 مواد لـ 3 طلاب:
#include <stdio.h>
void student_avg(double grades[][4], int rows) {
int i, j;
for (i = 0; i < rows; i++) {
double sum = 0;
for (j = 0; j < 4; j++) {
sum += grades[i][j];
}
printf("Student%d Average: %.1f\n", i + 1, sum / 4);
}
}
int main(void) {
double grades[3][4] = {
{85.0, 90.0, 78.0, 92.0},
{70.0, 65.0, 80.0, 75.0},
{95.0, 88.0, 92.0, 97.0}
};
student_avg(grades, 3);
return 0;
}
Student1 Average: 86.2
Student2 Average: 72.5
Student3 Average: 93.0
❓ أسئلة شائعة
sizeof(arr) داخل دالة لمعرفة عدد العناصر؟sizeof يعطي حجم المؤشر (4 أو 8 بايت)، وليس الحجم الكلي للمصفوفة.📖 ملخص
- المصفوفات هي تخزين متجاور لعناصر من نفس النوع، مع أدلة تبدأ من 0
- يمكن حذف الطول أثناء التهيئة؛ التهيئة الجزئية تملأ العناصر المتبقية بـ 0
- لا تُجري C فحصًا للحدود — يجب على المبرمج ضمان صحة الأدلة
- تنحل المصفوفات إلى مؤشرات عند تمريرها إلى الدوال؛ يجب تمرير الطول بشكل منفصل
- المصفوفات ثنائية البعد تُخزَّن صفًا بصف؛ يجب تحديد عدد الأعمدة في معاملات الدوال
📝 تمارين
- اكتب برنامجًا يقرأ 10 أعداد صحيحة في مصفوفة، ثم يطبع جميع العناصر بترتيب عكسي.
- اكتب دالة تستقبل مصفوفة أعداد صحيحة وطولها، وتُرجع دليل القيمة القصوى.
- عرّف مصفوفة أعداد صحيحة 4x4، تهيّئها، واحسب مجموع القطرين.