تبديل عام
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 51 من 61.
تعد عملية تبديل قيمتين عملية شائعة في البرمجة. عادةً، ستقوم بكتابة دالة تبديل (swap) منفصلة لكل نوع — واحدة لـ int، وأخرى لـ double، وهكذا. ولكن باستخدام void* و memcpy، يمكننا كتابة دالة واحدة تقوم بتبديل أي متغيرين.
الفكرة الأساسية هي أن عملية التبديل هي مجرد نقل للبايتات. إذا كنت تعرف حجم البيانات، يمكنك نسخ البايتات دون معرفة النوع الفعلي. يبدو توقيع الدالة كما يلي:
void generic_swap(void* a, void* b, size_t size);تخبر المعلمة size الدالة بعدد البايتات التي يجب نقلها. في الداخل، نستخدم مخزناً مؤقتاً و memcpy من <string.h> لتبديل البايتات:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // مخزن مؤقت
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}الآن تعمل هذه الدالة الواحدة مع أي نوع:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x الآن هي 10، و y الآن هي 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p هي الآن 2.71، q هي الآن 3.14هذا النمط — استخدام void* مع size_t — هو الطريقة التي تنفذ بها مكتبة C القياسية الدوال العامة مثل qsort و bsearch.
التحدي
سهللنقم ببناء أداة تبديل (swap) عامة يمكنها تبادل قيم أي متغيرين، بغض النظر عن نوعهما — باستخدام قوة مؤشرات void* و memcpy.
ستقوم بتنظيم الكود الخاص بك عبر ثلاثة ملفات:
swap.h: قم بالتصريح عن دالةgeneric_swapالتي تأخذ مؤشرين من نوعvoid*ومعاملsize_tيشير إلى حجم البيانات المراد تبديلها. لا تنسَ حواجز التضمين (include guards)!swap.c: قم بتنفيذ دالةgeneric_swap. استخدم مخزناً مؤقتاً (temporary buffer) وmemcpyلنقل البايتات بين موقعي الذاكرة. تذكر نمط التبديل المكون من ثلاث خطوات: نسخ القيمة الأولى إلى المؤقت، نسخ الثانية إلى الأولى، ثم نسخ المؤقت إلى الثانية.main.c: قم بإثبات عمل أداة التبديل العامة مع أنواع بيانات مختلفة. اقرأ مؤشر النوع (iللأعداد الصحيحة،dللأعداد العشرية double) متبوعاً بقيمتين. أنشئ متغيرين من النوع المناسب، وقم بتبديلهما باستخدام دالةgeneric_swap، ثم اطبع كلتا القيمتين بعد التبديل.
سيتلقى برنامجك ثلاثة مدخلات:
- مؤشر النوع:
iللعدد الصحيح أوdللعدد العشري double - القيمة الأولى
- القيمة الثانية
بعد التبديل، اطبع كلتا القيمتين في سطرين منفصلين. بالنسبة للأعداد العشرية (doubles)، استخدم منزلتين عشريتين.
مثال على المخرجات عندما تكون المدخلات i، و 5، و 10:
10
5مثال على المخرجات عندما تكون المدخلات d، و 3.14، و 2.71:
2.71
3.14مثال على المخرجات عندما تكون المدخلات i، و -7، و 42:
42
-7مثال على المخرجات عندما تكون المدخلات d، و 99.99، و 0.01:
0.01
99.99جمال هذا النهج هو أن دالة generic_swap الخاصة بك ليس لديها أدنى فكرة عن الأنواع التي تقوم بتبديلها — فهي تقوم فقط بنقل البايتات. تعمل نفس الدالة مع الأعداد الصحيحة، والأعداد العشرية، والهياكل (structs)، أو أي نوع بيانات آخر. ستحتاج إلى تضمين <string.h> من أجل memcpy و <stddef.h> أو <stdlib.h> من أجل size_t.
ورقة مرجعية
تستخدم دالة التبديل العامة (generic swap function) مؤشرات من نوع void* و memcpy لتبديل قيم من أي نوع عن طريق نقل البايتات دون معرفة النوع الفعلي.
توقيع الدالة:
void generic_swap(void* a, void* b, size_t size);التنفيذ باستخدام مخزن مؤقت (temporary buffer) و memcpy:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // مخزن مؤقت
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}الاستخدام مع أنواع مختلفة:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x الآن 10، و y الآن 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p الآن 2.71، و q الآن 3.14الترويسات المطلوبة: <string.h> من أجل memcpy و <stddef.h> أو <stdlib.h> من أجل size_t.
جرّب بنفسك
#include <stdio.h>
#include "swap.h"
int main() {
char type;
scanf("%c", &type);
if (type == 'i') {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
// TODO: استدعِ generic_swap لتبديل a و b
// تلميح: استخدم &a و &b و sizeof(int)
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: استدعِ generic_swap لتبديل a و b
// تلميح: استخدم &a و &b و sizeof(double)
printf("%.2f\n", a);
printf("%.2f\n", b);
}
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات البرمجة التركيبية
ملفات الترويسةحواجز التضمينملفات المصدرالدوال الساكنةمراجعة: آلة حاسبة تركيبية4التغليف (Encapsulation)
مفهوم الـ Opaque Pointersتعريف الـ Opaque Structsالـ Getters والـ Settersالتحقق من البيانات في الـ Settersملخص: الصندوق السري2الكائنات والأساليب
الـ Structs ككائناتمؤشر 'Self'صحة استخدام Constالمؤشر مقابل القيمةالأساليب المساعدةملخص: Point Manager5مشروع: حساب بنكي بسيط
إعداد المشروعتنفيذ الحساب3دورة حياة الكائن
نمط المنشئ (Constructor Pattern)نمط الهادم (Destructor Pattern)تهيئة الـ Stackالنسخ العميق (Deep Copy)مراجعة: String Wrapper6الوراثة عبر التركيب
تضمين الـ Structقاعدة العضو الأولالوصول إلى أعضاء الأبعملية الـ Upcastingمراجعة: هرمية الأشكال9مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة