مثال كلاسيكي: التبديل
جزء من قسم Logic & Flow في رحلة C على Coddy — الدرس 28 من 63.
تُعد دالة swap واحدة من أشهر الأمثلة التي توضح سبب أهمية المؤشرات في برمجة C. الهدف بسيط: كتابة دالة تتبادل قيم متغيرين. ومع ذلك، فإن هذه المهمة التي تبدو مباشرة تكشف عن قيد أساسي في التمرير بالقيمة (pass-by-value).
فكر فيما يحدث إذا حاولت تبديل متغيرين باستخدام التمرير بالقيمة العادي (pass-by-value):
void badSwap(int a, int b) {
int temp = a;
a = b;
b = temp;
// هذا يبدل النسخ فقط، وليس المتغيرات الأصلية!
}يفشل هذا النهج لأن الدالة تستقبل نسخاً من القيم فقط. تظل المتغيرات الأصلية في الدالة المستدعية دون تغيير.
يتطلب الحل تمرير مؤشرات (pointers) إلى المتغيرات بدلاً من ذلك:
void swap(int *a, int *b) {
int temp = *a; // تخزين القيمة الموجودة في العنوان a
*a = *b; // وضع القيمة من العنوان b في العنوان a
*b = temp; // وضع القيمة المخزنة في العنوان b
}من خلال تمرير عناوين المتغيرات واستخدام عامل إلغاء المرجعية (dereference operator)، يمكن للدالة تعديل قيم المتغيرات الأصلية مباشرة. وهذا يجعل دالة swap مثالاً ممتازاً على متى ولماذا تحتاج إلى المؤشرات (pointers) لإنجاز مهام لا يمكن لأسلوب التمرير بالقيمة (pass-by-value) التعامل معها ببساطة.
التحدي
سهلاكتب برنامجاً بلغة C يقوم بتنفيذ وتوضيح دالة التبديل (swap) الكلاسيكية باستخدام المؤشرات. يجب أن يقوم برنامجك بما يلي:
- إنشاء دالة تسمى
swapتأخذ مؤشرين إلى أعداد صحيحة كمعاملات - داخل دالة
swap، قم بتبديل القيم الموجودة في عناوين الذاكرة التي يشير إليها المؤشران باستخدام متغير مؤقت - في دالة
main، اقرأ عددين صحيحين من مدخلات المستخدم - اطبع القيم الأصلية بالتنسيق التالي:
Before swap: a = [value], b = [value] - استدعِ دالة
swapومرر عناوين كلا المتغيرين باستخدام عامل العنوان (address-of operator) - بعد استدعاء الدالة، اطبع القيم المبدلة بالتنسيق التالي:
After swap: a = [value], b = [value]
يجب أن يوضح مخرج البرنامج أن الدالة نجحت في تبديل قيم المتغيرات الأصلية:
Before swap: a = [first_input], b = [second_input]
After swap: a = [second_input], b = [first_input]يوضح هذا التحدي سبب أهمية المؤشرات لعملية التبديل - حيث يجب على الدالة الوصول إلى مواقع الذاكرة الخاصة بالمتغيرات الأصلية لتبديل قيمها بشكل دائم، وهو أمر مستحيل باستخدام التمرير بالقيمة (pass-by-value).
ورقة مرجعية
توضح دالة التبديل (swap function) لماذا تعتبر المؤشرات (pointers) ضرورية في لغة C. لا يمكن لعملية التمرير بالقيمة (Pass-by-value) تبديل المتغيرات الأصلية لأن الدوال تستقبل نسخاً فقط:
void badSwap(int a, int b) {
int temp = a;
a = b;
b = temp;
// هذا يبدل النسخ فقط، وليس المتغيرات الأصلية!
}يستخدم النهج الصحيح المؤشرات للوصول إلى المتغيرات الأصلية وتعديلها:
void swap(int *a, int *b) {
int temp = *a; // تخزين القيمة الموجودة في العنوان a
*a = *b; // وضع القيمة من العنوان b في العنوان a
*b = temp; // وضع القيمة المخزنة في العنوان b
}من خلال تمرير العناوين واستخدام عامل إلغاء المرجعية (*)، يمكن للدالة تعديل قيم المتغيرات الأصلية مباشرة.
جرّب بنفسك
#include <stdio.h>
// TODO: اكتب دالة التبديل (swap) الخاصة بك هنا
int main() {
int a, b;
// قراءة المدخلات
scanf("%d", &a);
scanf("%d", &b);
// طباعة القيم الأصلية
printf("Before swap: a = %d, b = %d\n", a, b);
// TODO: استدعِ دالة التبديل الخاصة بك هنا
// طباعة القيم بعد التبديل
printf("After swap: a = %d, b = %d\n", a, b);
return 0;
}يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Logic & Flow
1أساسيات المؤشرات
ما هو المؤشر؟الإعلان عن المؤشراتعامل العنوان (&)عامل إلغاء المرجعية (*)مؤشرات NULLمراجعة: أساسيات المؤشرات4مشروع: أداة نصوص بسيطة
نظرة عامة على المشروععد الأحرف2المؤشرات والمصفوفات
أسماء المصفوفات كمؤشراتعناصر المصفوفة - المؤشراتالعمليات الحسابية على المؤشراتمقارنة المؤشراتمراجعة: التنقل في المصفوفة باستخدام المؤشرات5المؤشرات والدوال
التمرير بالقيمةتمرير المؤشرات إلى الدوالتعديل المتغيرات عبر المؤشراتمثال كلاسيكي: التبديلتمرير المصفوفات إلى الدوالملخص: وسائط المؤشرات في الدوال8الهياكل والمؤشرات
المؤشرات إلى الهياكلعامل السهم (->)تمرير الهياكل بالقيمةتمرير مؤشرات الهياكلالتخصيص الديناميكي للهياكلمراجعة: تعديل الهياكل باستخدام المؤشرات11تحديات المراجعة النهائية
مراجعة: دمج السلاسل النصية الديناميكيمراجعة: مصفوفة من الـ Structsمراجعة: عداد تكرار الكلمات3مصفوفات الأحرف والسلاسل النصية
السلاسل النصية كمصفوفات charالمُنهي الصفري (Null Terminator)إدخال السلاسل النصية باستخدام scanfاستخدام strlen()استخدام strcpy()استخدام strcat()استخدام strcmp()مراجعة: دوال السلاسل النصية الأساسية