المؤشر مقابل القيمة
جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 9 من 61.
عندما تقوم بتمرير struct إلى دالة، تمنحك لغة C خيارين: تمريره بالقيمة (نسخ الـ struct بالكامل) أو تمريره بواسطة مؤشر (نسخ العنوان فقط). هذا الاختيار يهم بشكل كبير من أجل الأداء.
التمرير بالقيمة ينشئ نسخة كاملة من البنية (struct) على المكدس (stack). بالنسبة لبنية صغيرة تحتوي على بضعة أعداد صحيحة، فإن هذا الأمر جيد. ولكن تخيل بنية تحتوي على مئات البايتات؛ فكل استدعاء دالة سيقوم بنسخ كل تلك البيانات:
typedef struct {
char name[100];
int scores[50];
double average;
} Student;
// بالقيمة: ينسخ حوالي 308 بايت في كل استدعاء
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average);
}
// بالمؤشر: ينسخ 8 بايت فقط (العنوان)
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average);
}
تختلف الصيغة أيضاً. مع معامل القيمة، تستخدم عامل النقطة (s.name). ومع المؤشر، تستخدم عامل السهم (s->name).
تعد نسخة المؤشر أكثر كفاءة لأنها تمرر فقط عنوان ذاكرة — عادةً ما يكون 8 بايت بغض النظر عن حجم الـ struct.
هذا هو السبب في أن مبرمجي لغة C يمررون الهياكل (structs) عبر المؤشرات (pointers) دائمًا تقريبًا. استخدم const عندما لا ينبغي للدالة تعديل البيانات، ومؤشرًا عاديًا عندما ينبغي لها ذلك. ستحصل على الكفاءة والوضوح في القصد معًا.
التحدي
سهللنقم ببناء وحدة Product توضح الفرق بين تمرير الهياكل (structs) بالقيمة (by value) وبالتأشير (by pointer). ستقوم بإنشاء دالتين للعرض—واحدة تستقبل الهيكل بالكامل كنسخة، والأخرى تستقبل مجرد مؤشر إليه.
ستقوم بإنشاء ثلاثة ملفات:
product.h: قم بالتصريح عن هيكلProductبثلاثة أعضاء:char name[50]، وdouble price، وint quantity. قم أيضاً بالتصريح عن دالتين:display_by_value— تأخذProductبالقيمة وتطبع معلوماتهdisplay_by_pointer— تأخذconst Product *وتطبع نفس المعلومات
PRODUCT_H.product.c: قم بتنفيذ كلتا الدالتين. لاحظ الفرق في الصيغة: نسخة التمرير بالقيمة تستخدم عامل النقطة (p.name)، بينما نسخة المؤشر تستخدم عامل السهم (p->name). يجب أن تطبع كلتا الدالتين بنفس التنسيق.main.c: قم بإنشاء Product، ثم استدعِ كلتا دالتي العرض لتوضيح أنهما تنتجان مخرجات متطابقة على الرغم من استخدام أساليب معاملات مختلفة.
ستتلقى ثلاثة مدخلات: اسم المنتج (سلسلة نصية)، و السعر (double)، و الكمية (عدد صحيح).
في ملفك الرئيسي، قم بتهيئة Product بهذه القيم، ثم استدعِ display_by_value متبوعة بـ display_by_pointer.
يجب أن تطبع كلتا الدالتين بهذا التنسيق:
Product: {name}, Price: {price}, Qty: {quantity}استخدم %.2f للسعر لعرض منزلتين عشريتين.
على سبيل المثال، مع المدخلات Laptop، و 999.99، و 5، ستكون المخرجات:
Product: Laptop, Price: 999.99, Qty: 5
Product: Laptop, Price: 999.99, Qty: 5جرّب بنفسك
#include <stdio.h>
#include <string.h>
#include "product.h"
int main() {
char name[50];
double price;
int quantity;
// قراءة المدخلات
scanf("%s", name);
scanf("%lf", &price);
scanf("%d", &quantity);
// TODO: قم بإنشاء متغير Product وقم بتهيئته بقيم المدخلات
// تلميح: استخدم strcpy لنسخ الاسم إلى حقل name الخاص بـ struct
// TODO: قم باستدعاء display_by_value مع Product
// TODO: قم باستدعاء display_by_pointer مع مؤشر pointer إلى Product
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مشروع: رسّام الأشكال
نظرة عامة على المشروعتنفيذ الدائرةتنفيذ المستطيلاستخدام تعدد الأشكالحاوية الأشكال