مراجعة مؤشرات Void
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 49 من 61.
إن void* هي طريقة لغة C لقول "مؤشر لشيء ما، لكنني لن أخبرك ما هو". يمكنه حمل عنوان أي نوع بيانات — int أو float أو struct أو أي شيء آخر. وهذا يجعله الأساس لكتابة كود عام (generic) في لغة C.
المشكلة هي أنه لا يمكنك إلغاء مرجعية (dereference) لـ void* مباشرة. فالمترجم (compiler) لا يعرف عدد البايتات التي يجب قراءتها أو كيفية تفسيرها. لاستخدام البيانات، يجب عليك تحويلها (cast) مرة أخرى إلى النوع الصحيح:
void print_int(void* data) {
int* p = (int*)data;
printf("%d\n", *p);
}
int main() {
int x = 42;
print_int(&x);
return 0;
}هذه المرونة تأتي مع المسؤولية. إذا قمت بالتحويل إلى نوع خاطئ، فلن يمنعك المترجم — ولكن برنامجك سينتج بيانات غير صحيحة أو سيتوقف عن العمل (crash). لهذا السبب، غالباً ما تقرن الدوال العامة void* بمؤشر نوع (مثل enum أو flag) حتى تعرف الدالة كيفية تفسير البيانات بشكل صحيح.
في الدروس القادمة، ستستخدم void* لبناء حاويات يمكنها تخزين أي نوع من البيانات، مما يتيح إنشاء هياكل بيانات قابلة لإعادة الاستخدام حقاً.
التحدي
سهلاكتب دالة تسمى print_value تقبل معاملين: مؤشر void* للبيانات وعلم نوع (type flag) من نوع int. يجب أن تقوم الدالة بتفسير وطباعة البيانات بناءً على علم النوع:
- إذا كان علم النوع هو
0، فقم بتحويل المؤشر إلىint*واطبع القيمة الصحيحة - إذا كان علم النوع هو
1، فقم بتحويل المؤشر إلىfloat*واطبع قيمة الفاصلة العائمة مع منزلتين عشريتين
سيتلقى برنامجك مدخلين:
- علم النوع (
0للعدد الصحيح،1للعدد العشري) - قيمة عددية
بناءً على علم النوع، قم بتخزين القيمة في نوع المتغير المناسب، ثم استدعِ print_value مع مؤشر لهذا المتغير وعلم النوع.
مثال للمخرجات عندما تكون المدخلات 0 و 42:
42مثال للمخرجات عندما تكون المدخلات 1 و 3.14:
3.14مثال للمخرجات عندما تكون المدخلات 0 و -17:
-17مثال للمخرجات عندما تكون المدخلات 1 و 99.5:
99.50ورقة مرجعية
إن void* هو مؤشر عام يمكنه الاحتفاظ بعنوان أي نوع من أنواع البيانات. وهو يوفر المرونة لكتابة كود عام في لغة C.
لا يمكنك إلغاء مرجعية void* مباشرة — يجب عليك تحويله إلى النوع الصحيح أولاً:
void print_int(void* data) {
int* p = (int*)data;
printf("%d\n", *p);
}
int main() {
int x = 42;
print_int(&x);
return 0;
}غالباً ما تقرن الدوال العامة الـ void* بمؤشر نوع (مثل enum أو flag) لمعرفة كيفية تفسير البيانات بشكل صحيح. قد يؤدي التحويل إلى نوع خاطئ إلى سلوك غير محدد أو توقف البرنامج بشكل مفاجئ.
جرّب بنفسك
#include <stdio.h>
// TODO: اكتب دالة print_value الخاصة بك هنا
int main() {
int type_flag;
scanf("%d", &type_flag);
if (type_flag == 0) {
int int_value;
scanf("%d", &int_value);
// TODO: قم باستدعاء print_value مع مؤشر لـ int_value وعلامة النوع (type flag)
} else if (type_flag == 1) {
float float_value;
scanf("%f", &float_value);
// TODO: قم باستدعاء print_value مع مؤشر لـ float_value وعلامة النوع (type flag)
}
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرة