Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

المؤشر مقابل القيمة

جزء من قسم Object Oriented Programming في رحلة C على Coddy — الدرس 9 من 61.

عندما تقوم بتمرير struct إلى دالة، تمنحك لغة C خيارين: تمريره بالقيمة (نسخ الـ struct بالكامل) أو تمريره بواسطة مؤشر (نسخ العنوان فقط). هذا الاختيار يهم بشكل كبير من أجل الأداء.

يؤدي التمرير بالقيمة إلى إنشاء نسخة كاملة من الـ struct على المكدس (stack). بالنسبة للـ struct الصغيرة التي تحتوي على عدد قليل من الأعداد الصحيحة (integers)، يعد هذا أمراً جيداً. لكن تخيل وجود struct بمئات البايتات — فكل استدعاء دالة سيقوم بنسخ كل تلك البيانات:

typedef struct {
    char name[100];
    int scores[50];
    double average;
} Student;

// بالقيمة: ينسخ حوالي 608 بايت في كل استدعاء
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 عندما لا ينبغي للدالة تعديل البيانات، ومؤشرًا عاديًا عندما ينبغي لها ذلك. ستحصل على الكفاءة والوضوح في الغرض معًا.

challenge icon

التحدي

سهل

لنقم ببناء وحدة 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 * وتطبع نفس المعلومات
    استخدم حراس التضمين (include guards) مع الرمز PRODUCT_H.
  • product.c: قم بتنفيذ كلتا الدالتين. لاحظ الفرق في بناء الجملة: إصدار القيمة يستخدم عامل النقطة (p.name)، بينما إصدار المؤشر يستخدم عامل السهم (p->name). يجب أن تطبع كلتا الدالتين بنفس التنسيق.
  • main.c: قم بإنشاء Product، ثم استدعِ كلتا دالتي العرض لإظهار أنهما تنتجان مخرجات متطابقة على الرغم من استخدام أنماط معاملات مختلفة.

ستتلقى ثلاثة مدخلات: اسم المنتج (سلسلة نصية)، و السعر (عدد عشري مزدوج)، و الكمية (عدد صحيح).

في ملفك الرئيسي، قم بتهيئة 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

ورقة مرجعية

عند تمرير الهياكل (structs) إلى الدوال، يمكنك الاختيار بين التمرير بالقيمة (نسخ الهيكل بالكامل) أو التمرير بالمؤشر (نسخ العنوان فقط).

التمرير بالقيمة ينشئ نسخة كاملة من الهيكل:

void print_by_value(Student s) {
    printf("%s: %.2f\n", s.name, s.average);  // Use dot operator
}

التمرير بالمؤشر يمرر عنوان الذاكرة فقط:

void print_by_pointer(const Student *s) {
    printf("%s: %.2f\n", s->name, s->average);  // Use arrow operator
}

الاختلافات الرئيسية:

  • التمرير بالقيمة يستخدم عامل النقطة (s.member)
  • التمرير بالمؤشر يستخدم عامل السهم (s->member)
  • المؤشرات أكثر كفاءة للهياكل الكبيرة (8 بايت فقط مقابل حجم الهيكل بالكامل)
  • استخدم const مع المؤشرات عندما لا ينبغي للدالة تعديل البيانات

يفضل مبرمجو C عادةً تمرير الهياكل بالمؤشر للحصول على أداء أفضل.

جرّب بنفسك

#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 لنسخ الاسم إلى حقل الاسم في الهيكل (struct)
    
    // TODO: قم باستدعاء display_by_value مع الـ Product
    
    // TODO: قم باستدعاء display_by_pointer مع مؤشر (pointer) إلى الـ Product
    
    return 0;
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming