المؤشر مقابل القيمة
جزء من قسم البرمجة كائنية التوجه في رحلة C على Coddy. الدرس 9 من 61.
عندما تمرّر struct إلى function، تمنحك C خيارين: تمريرها بالقيمة (نسخ struct بالكامل) أو تمريرها بواسطة pointer (نسخ address فقط). يؤثر هذا الاختيار بشكل كبير في الأداء.
التمرير بالقيمة ينشئ نسخة كاملة من البنية على المكدس. بالنسبة إلى بنية صغيرة تحتوي على بضعة أعداد صحيحة، فهذا مناسب. لكن تخيّل بنية تحتوي على مئات البايتات: every استدعاء function ينسخ كل تلك البيانات:
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).
إصدار pointer أكثر كفاءة لأنه يمرر only عنوان address في الذاكرة، وعادةً ما يكون حجمه 8 bytes بغض النظر عن حجم البنية.
لهذا السبب يمرر مبرمجو C الهياكل دائمًا تقريبًا باستخدام pointer. استخدم const عندما لا ينبغي لـ function تعديل البيانات، واستخدم pointer عاديًا عندما ينبغي لها ذلك. ستحصل على الكفاءة والنية الواضحة معًا.
التحدي
سهللنبنِ وحدة Product توضّح الفرق بين تمرير البُنى بالقيمة وبواسطة pointer. ستنشئ اثنتين من function باسم display: واحدة تستقبل البنية كاملةً كنسخة، وأخرى تستقبل pointer إليها فقط.
ستنشئ ثلاثة ملفات:
product.h: Declare بنيةProductتحتوي على ثلاثةmembers:char name[50]وdouble priceوint quantity. Declare أيضًا اثنتين منfunction:display_by_value: تستقبلProductبالقيمة وتطبع معلوماتهdisplay_by_pointer: تستقبلconst Product *وتطبع المعلومات نفسها
PRODUCT_H.product.c: Implement كلتاfunction. لاحظ اختلاف الصياغة: إصدار التمرير بالقيمة يستخدمdotoperator (p.name)، بينما يستخدم إصدارpointerarrowoperator (p->name). يجب أن تطبع كلتاfunctionبالتنسيق نفسه.main.c: أنشئ Product، ثم استدعِ كلتاdisplayfunction لإظهار أنهما تنتجان مخرجات متطابقة رغم استخدام أسلوبي معاملات مختلفين.
ستتلقى ثلاث inputs: اسم product (سلسلة نصية)، وprice (قيمة من النوع double)، وquantity (عدد صحيح).
في ملفك الرئيسي، قم بتهيئة Product بهذه القيم، ثم استدعِ display_by_value متبوعًا بـ display_by_pointer.
يجب أن تطبع كلتا function بهذا التنسيق:
Product: {name}, Price: {price}, Qty: {quantity}استخدم %.2f من أجل price لعرض منزلتين عشريتين.
على سبيل المثال، عند استخدام 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 إلى حقل name في الـ struct
// TODO: استدعِ display_by_value مع الـ Product
// TODO: استدعِ display_by_pointer مع مؤشر إلى الـ Product
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 عبر الإنترنت