تبديل عام
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 51 من 61.
من العمليات الشائعة في البرمجة تبديل قيمتين. عادةً، ستكتب دالة تبديل منفصلة لكل نوع: واحدة لـ 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.
التحدي
سهللنَبْنِ أداة generic_swap عامة يمكنها تبديل قيم أي متغيرين، بغضّ النظر عن نوعهما، باستخدام قوة مؤشرات void* وmemcpy.
ستنظّم شفرتك عبر ثلاثة ملفات:
swap.h: Declare دالةgeneric_swapالتي تستقبل مؤشريvoid*ومعاملsize_tيحدد حجم البيانات المراد تبديلها. لا تنسَ حواجز التضمين!swap.c: Implement دالةgeneric_swap. استخدم مخزنًا مؤقتًا وmemcpyلنقل bytes بين موقعي الذاكرة. Remember نمط swap ذي الخطوات الثلاث: انسخ القيمة الأولى إلى temp، وانسخ الثانية إلى الأولى، ثم انسخ temp إلى الثانية.main.c: اعرض عمل swap العام مع أنواع بيانات مختلفة. اقرأ مؤشر type (iللأعداد الصحيحة، وdللقيم double) متبوعًا بقيمتين. أنشئ متغيرين من النوع المناسب، وبدّلهما باستخدام دالةgeneric_swap، ثم اطبع القيمتين بعد التبديل.
سيستقبل برنامجك ثلاثة مدخلات:
- مؤشر type:
iللأعداد الصحيحة أوdللقيم double - القيمة الأولى
- القيمة الثانية
بعد التبديل، اطبع القيمتين في سطرين منفصلين. بالنسبة إلى double، استخدم منزلتين عشريتين.
مثال على المخرجات عند كون المدخلات هي 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 لا تعرف شيئًا عن الأنواع التي تجري عليها swap. فهي تنقل bytes فقط. تعمل الدالة نفسها مع الأعداد الصحيحة وdouble وstructs أو أي نوع بيانات آخر. ستحتاج إلى include <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;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكالتدرّب بنفسك: مترجم C عبر الإنترنت