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

النسخ العميق (Deep Copy)

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

عندما تقوم بتعيين struct إلى آخر في لغة C، فإنك تحصل على shallow copy—يتم نسخ جميع الحقول بايت مقابل بايت. يعمل هذا بشكل جيد مع القيم البسيطة مثل integers، ولكنه يسبب مشكلة مع pointers: حيث ينتهي الأمر بكلا الـ structs بالإشارة إلى نفس الذاكرة.

typedef struct {
    char *name;
    int age;
} Person;

Person alice = {"Alice", 30};
Person copy = alice;  // نسخ سطحي!
// يشير كل من alice.name و copy.name إلى نفس السلسلة النصية

إذا قمت بتحرير أحدهما، يصبح الآخر غير صالح. إذا قمت بتعديل السلسلة النصية من خلال أحدهما، فسيظهر التغيير في كليهما. نادرًا ما يكون هذا هو ما تريده.

تقوم عملية النسخ العميق (deep copy) بتخصيص ذاكرة جديدة للأعضاء المؤشرية وتنسخ البيانات الفعلية:

Person *clone_person(const Person *original) {
    Person *copy = malloc(sizeof(Person));
    copy->name = malloc(strlen(original->name) + 1);
    strcpy(copy->name, original->name);
    copy->age = original->age;
    return copy;
}

الآن يمتلك كل كائن سلسلته النصية الخاصة. يمكنك تعديل أو تحرير أحدها دون التأثير على الآخر. النمط بسيط: لكل عضو مؤشر (pointer member)، قم بتخصيص ذاكرة جديدة ونسخ المحتويات - وليس مجرد العنوان.

challenge icon

التحدي

سهل

لنقم ببناء وحدة Student توضح نمط النسخ العميق (deep copy). ستقوم بإنشاء وظيفة استنساخ تنتج نسخة مستقلة تماماً من كائن الطالب — نسخة لا يؤثر فيها تعديل أو تحرير الكائن الأصلي على النسخة المستنسخة.

ستقوم بإنشاء ثلاثة ملفات:

  • student.h: قم بالتصريح عن هيكل Student يحتوي على عضوين: char *name (سلسلة نصية مخصصة ديناميكياً) و int grade. قم بالتصريح عن ثلاث وظائف:
    • create_student — منشئ (constructor) يأخذ اسماً ودرجة، ويعيد مؤشراً إلى Student جديد.
    • clone_student — يأخذ const Student * ويعيد مؤشراً إلى نسخة جديدة ومستقلة.
    • free_student — مدمر (destructor) يقوم بتحرير جميع الذاكرة المخصصة بشكل صحيح.
    استخدم حواجز التضمين (include guards) مع الرمز STUDENT_H.
  • student.c: قم بتنفيذ الوظائف الثلاث. وظيفة clone_student هي المفتاح — يجب أن تخصص ذاكرة جديدة لكل من الهيكل وسلسلة الاسم، ثم تنسخ بيانات السلسلة الفعلية (وليس مجرد عنوان المؤشر). يضمن ذلك أن النسخة تمتلك ذاكرتها الخاصة بشكل منفصل تماماً عن الأصل.
  • main.c: قم بإنشاء طالب، واستنساخه، وعرض معلومات كلا الطالبين، ثم تنظيف كلا الكائنين بشكل صحيح.

ستتلقى مدخلين: اسم الطالب (سلسلة نصية) و الدرجة (عدد صحيح).

في ملفك الرئيسي، قم بإنشاء طالب بالقيم المقدمة، ثم قم بإنشاء نسخة مستنسخة من ذلك الطالب. اطبع معلومات كل من الأصل والنسخة للتحقق من أنهما يحتويان على نفس البيانات. أخيراً، قم بتحرير كلا الطالبين واطبع تأكيداً.

اطبع المخرجات بهذا التنسيق:

Original: {name}, Grade: {grade}
Clone: {name}, Grade: {grade}
Both freed

على سبيل المثال، مع المدخلات Alice و 95، ستكون المخرجات:

Original: Alice, Grade: 95
Clone: Alice, Grade: 95
Both freed

يضمن نمط النسخ العميق أن كل كائن مستقل حقاً — فالنسخة المستنسخة لديها سلسلة نصية مخصصة خاصة بها، وليست مؤشراً مشتركاً لذاكرة الأصل.

ورقة مرجعية

عندما تقوم بتعيين هيكل (struct) إلى آخر في لغة C، فإنك تحصل على نسخة ضحلة (shallow copy)—يتم نسخ جميع الحقول بايت تلو الآخر. مع المؤشرات (pointers)، ينتهي الأمر بكلا الهيكلين بالإشارة إلى نفس الذاكرة:

typedef struct {
    char *name;
    int age;
} Person;

Person alice = {"Alice", 30};
Person copy = alice;  // نسخة ضحلة!
// يشير كل من alice.name و copy.name إلى نفس السلسلة النصية

تقوم النسخة العميقة (deep copy) بتخصيص ذاكرة جديدة لأعضاء المؤشرات ونسخ البيانات الفعلية:

Person *clone_person(const Person *original) {
    Person *copy = malloc(sizeof(Person));
    copy->name = malloc(strlen(original->name) + 1);
    strcpy(copy->name, original->name);
    copy->age = original->age;
    return copy;
}

نمط النسخ العميق: لكل عضو مؤشر، قم بتخصيص ذاكرة جديدة ونسخ المحتويات—وليس العنوان فقط. يضمن ذلك أن كل كائن يمتلك ذاكرته الخاصة ويمكن تعديله أو تحريره بشكل مستقل.

جرّب بنفسك

#include <stdio.h>
#include <stdlib.h>
#include "student.h"

int main() {
    char name[100];
    int grade;
    
    // قراءة المدخلات
    scanf("%s", name);
    scanf("%d", &grade);
    
    // TODO: إنشاء طالب بالاسم والدرجة المقدمين
    
    // TODO: إنشاء نسخة (clone) من الطالب
    
    // TODO: طباعة معلومات الطالب الأصلي
    // التنسيق: "Original: {name}, Grade: {grade}"
    
    // TODO: طباعة معلومات النسخة
    // التنسيق: "Clone: {name}, Grade: {grade}"
    
    // TODO: تحرير (Free) كلا الطالبين
    
    // طباعة التأكيد
    printf("Both freed\n");
    
    return 0;
}
quiz iconاختبر نفسك

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

جميع دروس Object Oriented Programming