الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

كيف تكتب كود Generic في C

بدأه motamayez في 19 يوليو 2009 · 7 رد · 2,552 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكــم ورحمـة الله وبركاتــه ،،

هذا أول درس أقدمه في المنتدى و ان شاء الله لن يكون الأخير, الدرس عنوانه غريب فهو يتحدث عن الGenerics في لغة الC و كما نعلم جميعاً هو أنه لا يوجد Generics في الC بل هي في اللغات الأكثر تطوراً ففي ال++C تُسمى Templates و في ال#C و الJava تُسمى Generics. اذن كيف يمكن للC أن تقوم بهذا ؟؟

أولاً ما هو الهدف من الGenerics ؟؟

الهدف الاساسي هو كتابة كود واحد بحيث يمكنه أن يعمل على أكثر من data type بمعنى أنه على سبيل المثال لو كتبت كود للعملية الشهيرة Swap بهذا الأسلوب

void swap(int* x, int* y)
{
 int tmp = *x;
 *x = *y;
 *y = tmp;
}

فهذه العملية ستعمل فقط على الintegers أو الأعداد الصحيحة,و اذا أردت أن أقوم بعمل Swap للfloat أو الdouble أو ربما structs يجب علي أن أكتب نسخة مناسبة لكل نوع و لكن الكود في النهاية فكرته واحدة, فما اقوم به هو

  1. تعريف متفير مؤفت
  2. نقل البيانات من المتغير الأول الى المؤقت
  3. نقل البيانات من المتغير الثاني للأول
  4. نقل البيانات من المتغير المؤقت الى المتغير الثاني

وكما قلنا سابقاً اذا كنت أستخدم لغة من اللغات الأحدث كان يمكنني فوراً أن أعتبر أن المتغير من نوع T و أجعل مستخدم العملية يقدم هذا النوع أثناء استدعاء العملية, لكن في الC يجب أن نفكر بطريقة أخرى..

و لنسأل نفسنا سؤال, ما هو المتغير؟

المتغير هو مساحة في الذاكره لها عنوان و مساحة, على سبيل المثال المتغير A من نوع int (اي 4 بايت) و موجود في العنوان (xxxx) في الذاكرة,

اذاً لو أمكننا اعطاء العملية Swap هذه المعلومات (المساحة و العنوان) للمتغيرات, دون تحديد النوع يمكننا أن نتعامل مع المتغيرات بطريقة عامةً (على الأقل هذا كل ما يهم الC).

اذن كبداية يمكننا أن نقول أن العملية Swap يمكن اعادة كتابة الprototype الخاص بها بهذه الطريقة

void swapgeneric(void* x, void* y, int size)

كما نرى أنه تم استخدام المتغيرات من نوع void* و التي ترمز الى عنوان المتغير في الذاكرة, كما أن المتغيرين من نفس الحجم و لهذا تم استخدام متغير واحدة لتحديد الحجم و هو size

حتى الان الموضوع رائع, كتبنا الprototype كيف يمكننا الان ان نعيد كتابة الخطوات الأربع التي تم ذكرها سابقاً

بالنسبة للخطوة 1 و هي تعريف متغير مؤفت

  1. تعريف متغير مؤفت

كيف يمكن تعريف متغير دون استخدام نوع ؟؟؟

كما قلنا في السابق المتغير هو عنوان و مساحة و لهذا توجد ادالة malloc و التي تشبه في عملها الأمر new في ال++C و ال#C و هي دالة تقوم بحجز مساحة في الذاكرة يحددها المستخدم و تعيد مؤشر من نوع void* يُشير الى أول byte في هذه المساحة التي تم حجزها في الHeap

و هنا نكتب الكود هكذا

void* tmp = malloc(size);
 if(tmp == NULL)
 {
  printf("no memory available\n");
  return;
 }

ثم ننتقل الى الجزء الثاني من العملية و هو

2. نقل البيانات من المتغير الأول x الى المتغير المؤقت

كيف سننقل البيانات بطريقة عامة؟

البيانات في النهاية هي عبارة عن ترتيب معين للأصفار و الاحاد في الذاكرة, اذاً كل ما أحتاجه هو أن أنسخ هذا الترتيب دون الحاجة لمعرفة تفاصيل البيانات أو ما المقصود بها و نسخها من المساحة المُشار عليها بالمؤشر x الى المساحة المًشاؤ عليها بالمؤشر tmp, أي بطريقة أبسط نسخ عدد من البايتات يساوي size من المكان x الى المكان tmp و يتم هذا عن طريق الدالة memcpy و هي التي تقوم بنسخ البيانات من مكان في الذاكرة الى اخر

memcpy(tmp, x, size);

ثم نأتي للعملية الثالثة و هي مشابهة للعملية 2 و لكن سنقوم بنقل البيانات من المتغير y الى x

memcpy(x, y, size);

ثم العملية الرابعة و هي أيضاً مشابهة للعمليتين 2 أو 3

memcpy(y, tmp, size);

ثم سنحتاج كعملية أخيرة هو اعادة الذاكرة التي تم حجزها للمتغير tmp مرة اخرى للنظام, فلغة الC لا تقوم بهذه العملية تلقائياً بل من مهمة المطور أن يعيد الذاكرة الى النظام مرة اخرى بعد استخدامها و يكون هذا عن طريق الدالة free

free(tmp);

اذا اردنا الان أن نلقي نظرة على الكود كاملآ

void swapgeneric(void* x, void* y, int size)
{
 void* tmp = malloc(size);
 if(tmp == NULL)
 {
  printf("no memory available\n");
  return;
 }
 memcpy(tmp, x, size);
 memcpy(x, y, size);
 memcpy(y, tmp, size);
 free(tmp);
}

و يمكننا استدعاء هذا الكود لأي نوع نحب على سبيل المثال

int x = 10;
 int y=120;
 printf("x=%d and y=%d\n", x, y);
 swapgeneric(&x, &y, sizeof(int));
 printf("x=%d and y=%d\n", x, y);

أو على متغيرات نصية

char * t1 = "This is string 1";
 char * t2 = "And this is string 2";

 printf("t1=%s and t2=%s\n", t1, t2);
 swapgeneric(&t1, &t2, sizeof(char *));
 printf("t1=%s and t2=%s\n", t1, t2);

أو على متغيرات من نوع struct بفرض أن لدينا struct Person يحتوي على متغير للاسم و اخر للسن

struct Person p1, p2;
	p1.age = 10;
	p1.name = "Person 1";

	p2.age = 20;
	p2.name = "Person 2";

	printf("P1 age %d, name %s\n", p1.age, p1.name);
	printf("P2 age %d, name %s\n", p2.age, p2.name);

	swapgeneric(&p1, &p2, sizeof(struct Person));

	printf("P1 age %d, name %s\n", p1.age, p1.name);
	printf("P2 age %d, name %s\n", p2.age, p2.name);

أرجو أن يكون الدرس سهل الاستيعاب و أرجو أن أرى تفاعلكم و أسئلتكم.

و شكراً

تم تعديل هذه المشاركة بواسطة motamayez في 19 يوليو 2009 في 10:34

2

مصري في بلاد الفرنجة.

قريباً اقرأ مقالاتي على It-scoop

#2

الموضوع سلس جدا تسلم ايدك

--كان عندى احساس انى هتابعك فى قسم ال C# :D

(map share people)

فضلا لاتقم بمراسلتي من أجل أسئلة لها أقسامها في المنتدى حتى تعم الفائدة على الجميع وللحصول على إجابات أفضل من أعضاء أكثر خبرة.
Weblog
@bitbucket
@xmonader

#3

السلام عليكــم ورحمـة الله وبركاتــه ،،

درس ممتاز و من الدروس المميزة في المنتدى

بانتظار بقية دروسك

#4

السلام عليكم ...

جميل جداً, أعطيه خمس نجوم :)

يمكنك أيضاً التحايل بطريقة أخرى :

#define type int

void genericSwap(type* first, type* second)
{
	type temp = *first;
	*first = *second;
	*second = temp;	
}
.
.
int x = 5, y = 10;
genericSwap(&x, &y);

و لكن الطريقة التي ذكرتها هي الأفضل لأنها تعمل مع جميع الأنواع حتى المصفوفات.

كتابة Generic Code بـ C حقيقة عملية صعبة, لأن اللغة لا تدعم ذلك أصلاً... و لو قارنت طريقة #C و Java و ++C بطريقة C لوجدت أن طرق اللغات الثلاث على اختلافها جوهرياً أفضل بمراحل من طريقة C.

بانتظار توسيع النقاش ليشمل عملية تمرير الدوال كما في qsort التي تعتبر generic sort, بالطبع مع مقارنة C بـ ++C و Java و #C إن أمكن في دالة الترتيب التي توفرها المكتبات القياسية لهذه اللغات لنرى الفروق بينهم و بين C :)

تحياتي ...

#5

موضوع جميل وبداية حسنة

جزاك الله خيرا

amdvsintel22.jpg

--i use AMD--

لا اله الا الله محمد رسول الله

أستعن بالله و لا تعجز

تابعنى على تويتر

***

#6

شكراً جزيلاً على التعليقات الايجابية, و الله شجعتني يا أخ خالد لكتابة موضوع اخر لاظهار كيفية استخداك الfunction pointer و أعتقد أنني سأبدأ بعملية Linear Search بحيث تكون Generc أيضاً

مصري في بلاد الفرنجة.

قريباً اقرأ مقالاتي على It-scoop

#7

ألف شكر على الدرس الرائع (أفكار حلوه لاصحاب الC مثلي :):) )

أرجو أن تتابع في كتابة الدروس

#8

دخلت الموضوع بالصدفة، لكنها صدفة جميلة :happy:

فعلا طريقة رائعة ومنطقية جدا، كيف لم أجدها أولا :resentful:

في الحقيقة استغربت لما قرأت الجملة التي تقول أنه يجب إعادة الذاكرة إلى النظام يدويا من طرف المبرمج، لكن بعدما تذكرت معلوماتي القليلة عن السي بطل العجب. الجافا عملت لي غسيل للدماغ :haha:

لغة سي تبقى دائما لها سحرها الخاص :wub:

شكرا يا متميز على المقالة المتميزة

تم تعديل هذه المشاركة بواسطة herch في 20 سبتمبر 2009 في 16:01

مواضيع مشابهة