ahmed.o.mohamed

اختبر قدراتك في C/CPP - الحلقة الثانية, الجزء السادس و الأخير

12 ردود في هذا الموضوع

السلام عليكم

الحلقة الأولى.

الحلقة الثانية - الجزء الأول.

الحلقة الثانية - الجزء الثاني.

الحلقة الثانية - الجزء الثالث.

الحلقة الثانية - الجزء الرابع.

الحلقة الثانية - الجزء الخامس.

مرحبا بكم إخوتي الكرام في الجزء الأخير من الحلقة الثانية من سلسلة اختبر قدراتك, قمتُ بتقسيم الحلقة الثانية إلى عدة أجزاء, مُرتبة حسب الصعوبة.

سؤالنا هذه المرة يختلف قليلا عن الأسئلة السابقة :happy:

الإختبار الثاني (الجزء السادس و الأخير) :

قم بتنفيذ الكود التالي :

#include <stdio.h>
typedef unsigned long long BigInt;
unsigned short i;

BigInt Func1(BigInt(*Func2)()) {
unsigned short j;
j = i;
if (j == 0) return 1;
else {
i--;
return j * Func2(Func2);
}
}

int main(int argc, char * argv[]) {
printf("Value ? ");
scanf("%hu", &i);
printf("Result = %llu\n", Func1(Func1));
return 0;
}

هل توجد أخطاء منطقية في الكود ؟ :)

  • إذا كان الجواب نعم, فما هي تلك الأخطاء ؟
  • إذا كان الجواب لا, فقم بشرح طريقة تنفيذ الكود خطوة بخطوة ! (التفاصيل مهمة جدا ..)

سأضع حل السؤال بعد إنتهاء عرض المحاولات.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

Factorial!!!

مترجمي لا يدعم long long ولكن لا داعي ... إنه العاملي ! factorial

أليس كذلك !!!

_____________________________

في البداية ظننت أنه لا يوجد خطأ منطقي فهممت بالشرح ولكن ... انتظر ...

هناك خطأ ...

الدالة Func1 تأخذ وسيطاً

BigInt(*Func2)()

وبالتالي فالوسيط هو مؤشر إلى دالة لا تأخذ وسطاء

وهذا يعني شيئا واحداً ... لا يمكن أن نمرر للدالةFunc1وسيطا هو الدالة نفسها ..؟!؟!؟!

أليس هذا صحيحاً ؟؟

إذا فالخطأ المنطقي هو أنه لا يمكن تمرير مؤشر إلى نفس الدالة من ضمن وسطائها !!!

لأن هذا لو حدث فسيضطرنا إلى كتابة الوسطاء داخل الوسطاء إلى اللانهاية ... مارأيك؟

تم تعديل بواسطه مصطفى 36a2
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

ما هذا, الكود لحساب العاملي, لكن فيه أخطاء

return j * Func2(Func2);

func2 لا تأخذ بارمترات

printf("Result [code]= %llu\n", Func1(Func1));

البارمتر المرر للدالة Func1 يجب أن يكون دالة تعيد BigInt و لا تأخذ وسائط

الكود هكذا سيعمل:

#include <stdio.h>
typedef unsigned long long BigInt;
unsigned short i;

BigInt Func1() {
unsigned short j;
j = i;
if (j == 0) return 1;
else {
i--;
return j * Func1();
}
}

int main(int argc, char * argv[]) {
printf("Value ? ");
scanf("%hu", &i);
printf("Result = %llu\n", Func1());
return 0;
}

تم تعديل بواسطه khatibe_30
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

رائع فهمت الكود ... جاري الشرح !! ..

_______________ التحديث_______________

ليس رائعاً ... يبدو أنه بالفعل .. لا يعمل .

يظهر الخطأ

cannot convert parameter 1 from 'unsigned long (unsigned long (__cdecl *)(void))' to 'unsigned long (__cdecl *)(void)'

__________________ولكن بفرض أنه يعمل فهذا هو الشرح laugh.gif________يُنصح بعدم القراءة لأسباب تتعلق بالصحة___________

بعد أن يتم عرض الرسالة ثم استقبال المتحول i ...واستدعاء الدالة Func1

فإن عملية تمرير الوسطاء تتضمنها عمليتان :

عملية إنشاء المتحول المؤقت

ثم عملية إسناد المتحول المؤقت إلى الوسيط الممرر ..

عندما قمنا بتمرير الوسيط

printf("Result = %llu\n", Func1(Func1));

ستحدث ضمنيّاً العملية التالية :

BigInt(*Func2)()=Func1;

وبما أن Func1 تعريفها (ضمنيا) كما يلي :

BigInt (*Func1)(BigInt(*Func2)());

فهناك اختلاف في نوعي الوسطاء .. الطرف الأيسر وسطاؤهvoid والثاني وسطاؤه

BigInt(*Func2)()

وبما أن الكود لم يشتغل عندي ولا عند الأخ ياسين .. والأخ أحمد يؤكد أنه اشتغل 200%

فهذا يعني حدوث عملية casting ضمنية بحيث يصبح Func2 يستقبل وسطاء Func1 رغماً عن أنفه ...

ثم تأخذ j قيمة i

إن وصلت i إلى 0 تعيد الدلة 1 لأن 0!=1

وإلا حسب العودية recursive سننقص i ونستدعي الدالة Func2 والتي هي ليست إلا Func1 وستدث نفس عملية الcasting العجيبة المشروحة

أعلاه ...

وشكراً ... رغم أنني نصحتكم بعدم قراءة الشرح ..

وشكراً

تم تعديل بواسطه مصطفى 36a2
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

يبدو أنه بالفعل .. لا يعمل

الكود يعمل بشكل ممتاز :)

انظر :

post-219439-021407900 1346974890_thumb.p

أريد تفسيراً يُقنع القارئ أكثر :cool:

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

وعليكم السلام أخ أحمد

مرحبا بكم إخوتي الكرام في الجزء الأخير من الحلقة الثانية من سلسلة اختبر قدرات

كنت أتمنى أن تطول السلسلة حتى نستفيد أكثر و أكثر wink.gif

هل توجد أخطاء منطقية في الكود ؟ :)

typedef unsigned long long BigInt;
unsigned short i;

بالنسبة لل variable globale ينصح بتجنب استعمالها

هذا النوع من المتغيرات يكون متاحا لجميع دوال ملفات المشروع , في الأول ستعتقد أنها تسهل من العمليات البرمجية

ولكن سرعان ما تتراجع عن فكرة استعمالها لأن بعد ذلك قد تجد نفسك مع العديد من المتغيرات يمكن الوصول إليها في أي مكان

وهذا قد يلحق مشاكل بالبرنامج أنت في غنى عنها

فكما جاء في الكود

unsigned short i;

فإن هذا المتغير استعمل داخل الدالة main وكذلك داخل الدالة Func1 وماذا لو أضفنا دالة أخرى

و قمنا بمؤثر الزيادة لهذا المتغير و بعد ذلك استدعينا الدالة Func1 أكيد أن المتغير سوف يحتفظ بالقيمة الأولى و ستظهر لك نتائج غير مرغوب بها عند استعمالها في دالة funct1

لنرجع للسؤال

أريد أن أسألك فقط سؤال ؟ لما كنت تضع هذا السؤال ألم تفكر مثلا في أنك قد تكون سببا في جنون مبرمج ما إذا حاول الإجابة علي السؤال و استعصت عليه الإجابة؟laugh.gif

فلنبدأ من هذه الصورة huh.gif

1346973180511.jpg

البداية الحقيقية للبرنامج من هذا السطر

printf("Result = %llu\n", Func1(Func1));

نستدعي الدالة funct1 لها كوسيط دالة funct1

BigInt Func1(BigInt(*Func2)())

الدالة الأصلية من نوع unsigned long long لها كبارامتر مؤشر دالة من نوع unsigned long long وبدون أي بارامتر

عندما استدعينا الدالة الأصلية الأولى في هذا السطر

printf("Result = %llu\n", Func1(Func1));

أصبح بارامتر الدالة بهذا الشكل

BigInt(*Func2)()=Func1;

في هذه الأسطر

	unsigned short j;
j = i;
if (j == 0) return 1;
else {
i--;

ستأخد j قيمة i و سنقارنها مع 0 إذا كانت تساوي 0 هذا يعني أن الدالة ستتوقف

غير ذلك سيتحقق هذا الأمر

i--;

هذه الأسطر مفهومة للجميع تقريبا

المشكلة في هذا الأمر

return j * Func2(Func2);

هنا الفخ!!

أصبح بارامتر الدالة بهذا الشكل كما قلنا في البداية

BigInt(*Func2)()=Func1;

و بالتالي هذا الأمر

return j * Func2(Func2);

سيتحول هو الآخر لهذا الشكل

return j * Func1(Func1);

و سنتحدث إذن عن دالة تعيد استدعاء نفسها إلى أن يتحقق هذا الشرط

if (j == 0)

فتكون الدالة انتهت من مهامها و أرجعت لنا النتيجة المطلوبة

والله أعلم

وبالتوفيق للجميع

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

المثال التالي لدالة تعيد مؤشر دالة حتى يفهم الإخوة طريقة عمل هذا النوع من الدوال

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

long somme(int *,int);
long (* p(void))(int *,int);


int main()
{

int t[5]={2,4,6,8,10};
printf("%ld",(* p())(t,5)); // ==> 30

return 0;
}


long somme(int *p, int size)

{
int i=0;
long s=0;
while(i<size) s+=p[i++];
return s;
}


long (* p(void))(int *,int)
{
return somme;
}

وبالتوفيق للجميع

تم تعديل بواسطه أحمد الشنقيطي
Mise à jour
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

الخطأ في البرنامج عند السطر :

BigInt Func1(BigInt(*Func2)())

و :

return j * Func2(Func2);

printf("Result = %llu\n", Func1(Func1));

تعريف الدالة Func1 حسب كتابتك: الدالة Func1 تعيد BigInt وتأخذ مؤشر لدالة تُعيد BigInt فقط.

ورغم هذا فأنت تُمرر لها مؤشر دالة تعيد BigInt "وتأخذ مؤشر دالة" ... لإصلاحها سجد نفسك أنك أمام حلقة لامنتية من الـcasting. يمكن إصلاح الكود هكذا بإستخدام void * (إصلاح ضعيف):

#include <stdio.h>

typedef unsigned long long BigInt;
unsigned short i;

BigInt Func1(BigInt(*Func2)(void *)) {
unsigned short j;
j = i;
if (j == 0) return 1;
else {
i--;
return j * Func2((void *) Func2);
}
}


int main(int argc, char *argv[])
{
printf("Value ? ");
scanf("%hu", &i);
printf("Result = %llu\n", Func1((BigInt (*) (void *)) Func1));

return 0;
}

أو الإستغناء عن مؤشر الدالة وإختصاره في :

#include <stdio.h>

typedef unsigned long long BigInt;
unsigned short i;

BigInt Func1() {
unsigned short j;
j = i;
if (j == 0) return 1;
else {
i--;
return j * Func1();
}
}


int main(int argc, char *argv[])
{
printf("Value ? ");
scanf("%hu", &i);
printf("Result = %llu\n", Func1());

return 0;
}

عمل البرنامج يكافئ عمل هذا البرنامج :

#include <stdio.h>

unsigned long long calc(unsigned short number);

int main(int argc, char **argv)
{
unsigned short number = 0;

printf("Value? ");
scanf("%u", &number);
printf("Result = %llu\n", calc(number));

return 0;
}

unsigned long long calc(unsigned short number)
{
unsigned long long result;

for( result = 1 ; number != 0 ; number-- )
result *= number;

return result;
}

إلا أنه بدل إستخدام حلقة التكرار يجعل الدالة تعيد إستدعاء نفسها والمتيغر العام i يكافئ عمل number في البرنامج الأخير .

تم تعديل بواسطه Mr.B
0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

لم أجد أي خطأ, بعد تتبع تنفيذ الكود و مراقبة الـ call stack لم أجد أخطاء

أول إستدعاء للدالة func1 يبدأ من هنا

printf("Result = %llu\n", Func1(Func1));

أي سندخل إلى الدالة func1 :

Func1(Func1) {
//...
return j * Func1(Func1);
}

وجدت أنه لا مشكلة في تمرير Func1 إلى Func1 مع أن التصريح عنها في البداية فيه وسيط لدالة Func2 لا تأخذ وسائط

ليكون هذا الكود صحيح أيضا و نتخلص من المتغير i الـ global

#include <stdio.h>
typedef unsigned long long BigInt;

BigInt Func1(int a, BigInt(*Func2)()) {
unsigned short j;
j = a;
if (j == 0) return 1;
else {
a--;
return j * Func2(a, Func2);
}
}

int main(int argc, char * argv[]) {
unsigned short i;
printf("Value ? ");
scanf("%hu", &i);
printf("Result = %llu\n", Func1(i, Func1));
scanf("%hu", &i);
return 0;
}

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

حسنا يبدو أن المشكلة ليست في الSyntax إنها مثلما فهمناها تماماً ...

يمكن تبسيط المشكلة إلى هذا الحد :

int Func1(int(*Func2)())
{
return 0;
}
int main() {
int(*Func2)()=Func1;
return 0;
}

واضح أن compiler لغة الC سيقوم بتحويل المؤشر Func2 من مؤشر إلى تابع لا يأخذ وسطاء ------> إلى مؤشر إلى تابع يأخذ وسطاء ... ولا أظن أن هناك حلاً آخر ...

وبالتالي حل مشكلتنا هي أنه خلال تمرير التابع Func1 كوسيط فإن المتحول المؤقت Func2 سيتم عمل casting له ليصبح بنفس هيكلية Func1 ,,,

هذه إجابتي النهائية ...

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

السلام عليكم

فكرة السؤال تدور حول "تداخل الدوال" (Nested Function).

لغة C القياسية لا تدعم الدوال المتداخلة على عكس لغات كثيرة منها :

Algol, Pascal, Ada, Scheme, Common Lisp, Python, D and Perl

وجد مصمموا اللغة أن إدراج تداخل الدوال في C سيكون مُعقدا جدا (خصوصا من ناحية Variables Scope, Stack Management, ...), لكن رغم ذلك توجد بعض الــ implementations التي تدعم NF مثل GNU CC, أيضا يُمكننا محاكاة التداخل باستخدام مؤشرات لبنى أو مؤشرات لدوال داخل الدالة المستهدفة.

بالنسبة للكود فهو صحيح و لا يحتوي على أي خطأ, و قد كتبتُه كتطبيق بسيط لدالتين متداخلتين, لألفت انتباهكم إلى فكرة تداخل الدوال.

كل ما سيحدث هو عملية Casting يقوم بها الــ Compiler تلقاءيا من أجل تطابق طرفي الإسناد.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

تم منح النقاط للمشاركات المفيدة و حذف الردود الغير مفيدة, حفاظا على تنسيق الموضوع.

يُغلق.

أراكم في الجزء الأول من الحلقة الثالثة.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .