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

تمرير مؤشرات الهياكل

جزء من قسم Logic & Flow في رحلة C على Coddy — الدرس 47 من 63.

بينما يعمل تمرير الهياكل (structs) بالقيمة بشكل جيد لقراءة البيانات، فإنه يصبح غير فعال مع الهياكل الكبيرة لأن الهيكل بأكمله يتم نسخه. والأهم من ذلك، لا يمكنك تعديل الهيكل الأصلي من داخل الدالة. هنا يصبح تمرير المؤشرات إلى الهياكل (pointers to structs) أمراً ضرورياً.

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

صيغة الدالة التي تقبل مؤشراً إلى هيكل (struct) هي:

void functionName(struct StructureName *ptr) {
    // جسم الدالة باستخدام ptr->member
}

داخل الدالة، تستخدم عامل السهم (arrow operator) للوصول إلى أعضاء الـ struct وتعديلها. أي تغييرات يتم إجراؤها من خلال المؤشر (pointer) ستؤثر بشكل مباشر على الـ struct الأصلي في الدالة المستدعية، مما يجعل هذا النهج مثاليًا للدوال التي تحتاج إلى تحديث أو تحويل بيانات الـ struct.

تعد هذه التقنية أساسية لبناء برامج C فعالة تتعامل مع هياكل بيانات معقدة، حيث تجمع بين قوة المؤشرات (pointers) وتنظيم الـ structs.

challenge icon

التحدي

سهل

قم بإنشاء برنامج بلغة C يوضح كيفية تمرير مؤشرات (pointers) للهياكل (structs) إلى الدوال لتعديل البيانات بكفاءة. يجب أن يقوم برنامجك بما يلي:

  1. تعريف struct باسم BankAccount بالأعضاء التالية:
    • عدد صحيح accountNumber لتخزين رقم الحساب
    • مصفوفة أحرف ownerName بحجم 30 لتخزين اسم صاحب الحساب
    • رقم عشري balance لتخزين رصيد الحساب
    • عدد صحيح transactionCount لتخزين عدد المعاملات
  2. كتابة دالة باسم depositMoney تقوم بما يلي:
    • تستقبل مؤشراً إلى هيكل BankAccount ومبلغاً عشرياً كمعاملات
    • تضيف المبلغ إلى رصيد الحساب باستخدام عامل السهم (arrow operator)
    • تزيد عدد المعاملات بمقدار 1
    • تطبع "Deposit successful. New balance: %.2f\n"
  3. كتابة دالة باسم withdrawMoney تقوم بما يلي:
    • تستقبل مؤشراً إلى هيكل BankAccount ومبلغاً عشرياً كمعاملات
    • تتحقق مما إذا كان مبلغ السحب أكبر من الرصيد الحالي
    • في حالة عدم كفاية الأموال، تطبع "Insufficient funds. Current balance: %.2f\n"
    • في حالة كفاية الأموال، تطرح المبلغ من الرصيد، وتزيد عدد المعاملات، وتطبع "Withdrawal successful. New balance: %.2f\n"
  4. كتابة دالة باسم displayAccount تقوم بما يلي:
    • تستقبل مؤشراً إلى هيكل BankAccount كمعامل
    • تطبع معلومات الحساب بهذا التنسيق الدقيق:
      • "Account Information:\n"
      • "Account Number: %d\n"
      • "Owner: %s\n"
      • "Balance: %.2f\n"
      • "Transactions: %d\n"
  5. في الدالة الرئيسية (main function):
    • إنشاء متغير من نوع BankAccount باسم account
    • قراءة رقم الحساب، واسم المالك، والرصيد الأولي من المدخلات
    • تعيين عدد المعاملات إلى 0
    • استدعاء displayAccount لإظهار حالة الحساب الأولية
    • قراءة مبلغ الإيداع واستدعاء depositMoney
    • قراءة مبلغ السحب واستدعاء withdrawMoney
    • استدعاء displayAccount لإظهار حالة الحساب النهائية

يوضح هذا التحدي قوة تمرير المؤشرات إلى الهياكل (structs) في الدوال. على عكس التمرير بالقيمة (pass-by-value)، تستقبل الدوال عنوان الذاكرة للهيكل الأصلي، مما يسمح لها بتعديل البيانات الأصلية مباشرة. هذا النهج أكثر كفاءة (لا يوجد نسخ للبيانات) ويمكّن الدوال من تغيير قيم الهيكل بشكل دائم في الدالة المستدعية.

ورقة مرجعية

عند تمرير الهياكل (structs) بالقيمة، يتم نسخ الهيكل بالكامل، مما يجعله غير فعال للهياكل الكبيرة ويمنع تعديل البيانات الأصلية. تمرير المؤشرات إلى الهياكل (pointers to structs) يحل كلتا المشكلتين عن طريق تمرير عنوان الذاكرة بدلاً من ذلك.

صيغة الدالة لقبول مؤشر إلى هيكل:

void functionName(struct StructureName *ptr) {
    // جسم الدالة باستخدام ptr->member
}

داخل الدالة، استخدم عامل السهم (->) للوصول إلى أعضاء الهيكل وتعديلها من خلال المؤشر. أي تغييرات يتم إجراؤها ستؤثر بشكل مباشر على الهيكل الأصلي في الدالة المستدعية.

يوفر هذا النهج ميزتين رئيسيتين:

  • أكثر كفاءة (لا يتم نسخ الهيكل بالكامل)
  • يسمح للدوال بتعديل بيانات الهيكل الأصلية

جرّب بنفسك

#include <stdio.h>
#include <string.h>

// TODO: قم بتعريف هيكل BankAccount هنا

// TODO: اكتب دالة depositMoney هنا

// TODO: اكتب دالة withdrawMoney هنا

// TODO: اكتب دالة displayAccount هنا

int main() {
    // قراءة المدخلات
    int accountNum;
    char ownerName[30];
    float initialBalance;
    float depositAmount;
    float withdrawAmount;
    
    scanf("%d", &accountNum);
    scanf("%s", ownerName);
    scanf("%f", &initialBalance);
    scanf("%f", &depositAmount);
    scanf("%f", &withdrawAmount);
    
    // TODO: قم بإنشاء متغير BankAccount وتهيئته
    
    // TODO: استدعِ displayAccount لعرض الحالة الأولية
    
    // TODO: استدعِ depositMoney مع مبلغ الإيداع
    
    // TODO: استدعِ withdrawMoney مع مبلغ السحب
    
    // TODO: استدعِ displayAccount لعرض الحالة النهائية
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Logic & Flow