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

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

مغلق
بدأه Snack3r في 6 سبتمبر 2012 · 5 رد · 729 مشاهدة · في اختبر قداراتك
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم

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

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

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

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

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

مرحبا بكم في الجزء القبل الأخير من الحلقة الثانية من سلسلة اختبر قدراتك, قمتُ بتقسيم الحلقة الثانية إلى عدة أجزاء, مُرتبة حسب الصعوبة, لكن قمتُ بتغيير نمط السؤال هذه المرة, حتى لا يُصاب المشاركون بالملل :happy:

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

حاول تنفيذ الكود التالي :

#include <stdio.h>

int main() {
    int *p1, *p2, *f1(), *f2();
    p1 = f1(4);
    p2 = f2(*p1);
    printf("*p1 = %d\t*p2 = %d\n", *p1, * p2);
    return 0;
}

int *f1(m) int m;
{
    int n = 5;
    n += m;
    return (&n);
}

int *f2(m) int m;
{
    int p = 8;
    p += m;
    return (&p);
}

ماذا يحدث لو كانت المتغيرات المحلية لــ f1 و f2 ساكنة (static) ؟

علل جوابك ! :)

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

#2
int *f1(m) int m;

أليست هذه هي طريقة الC في عام 1979

هل هي تكافئ :

int *f1(int m)

_______

جاري التفكير

_________________________تحديث____________

عفواً ,,, ظننت أن الكود صحيح وعلينا قراءته ...

ولكن الكود ناقص وعلينا تصحيحه ...

_____

جاري التفكير

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 6 سبتمبر 2012 في 19:09

#3

هذا هو تصحيح الكود :

#include<cstdio>
int main() {
	int *p1, *p2, *f1(int), *f2(int);
	p1 = f1(4);
	p2 = f2(*p1);
	printf("*p1 = %d\t*p2 = %d\n", *p1, * p2);
	return 0;
}
int *f1(int m)
{
	int n = 5;
	n += m;
	return (&n);
}
int *f2(int m)
{
	int p = 8;
	p += m;
	return (&p);
}

الغريب أنني حصلت على

اقتباس
warning C4172: returning address of local variable or temporary

_________________________________سبحان الله ______________________________

وإليك الحل ...

عندما نعيد عنوان المتغير ...

صحيح أن المؤشر سيحتفظ بالعنوان ...

ولكن المترجم سيلغي محتويات الحجرة وستأخذ قيمة لا يحددها المستخدم ...

أما عندما نجعل القيمة static فإن المؤشر سيحتفظ بعنوان الحجرة التي ستبقى محتفظة بالقيمة

أي أن الخرج سيكون في الحالة الأولى عجيباً ...حصلت على 17 و 17 لا أدري لماذا ؟ ..

تحديث : وبعد التجربة اكتشفت أن p1 صار يؤشر إلى نفس الحجرة التي يؤشر لها p2 !!!

تحديث : وبعد تتبع الكود ...بواسطة الزر السحري F11 اكتشفت ما يلي :post-256536-068247900 1346950574_thumb.j

وهذا بفسر كل شيء ... إن الحالة التي تعاملنا معها هي حالة دخول غير نظامي لحجرة حُجزت ديناميكيا ثم ألغيت ...

ولاحظت أيضاً أنه بمجرد استخدام القيمة في الحجرة لمرة واحدة فإنها ستتغير إلى قيمة عشوائية ... لا يحددها المستخدم ... عجيب ..

أما في الحالة الثانية ..سيكون والله أعلم : 9 من أجل المؤشر الأول ... 17 للمؤشر الثاني ...

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

_____ملاحظة لم أجرب static على الكومبايلر حفاظاً على الأمانة العلمية ...._______

تحياتي

المرفقات
1.jpg

تم تعديل هذه المشاركة بواسطة مصطفى 36a2 في 6 سبتمبر 2012 في 20:09

1
#4
اقتباس
أليست هذه الكتابة int *f1(m) int m هي طريقة الــ C في عام 1979 ؟

هل هي تكافئ int *f1(int m) ؟

نعم, بالضبط :)

اقتباس
هذا هو تصحيح الكود : ...

تفسير جيد أخي مصطفى.

::__________________::

فكرة السؤال تدور حول عناوين المتغيرات و مدى المتغيرات أو مجالات الرؤية (Variables scope).

بداية, سأوضح الفرق بين المتغيرات المحلية (local) و المتغيرات العامة (global) :

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

بالنسبة للمتغيرات العامة فتنتهي أعمارها عند انتهاء تنفيذ البرنامج بينما ينتهي عمر المتغيرات الخاصة عند الانتهاء من تنفيذ الدالة الموجودة بها.

نأتي الآن للكلمة static :

تُستخدم هذه الكلمة مع المتغيرات الساكنة, و عند إضافتها إلى أي متغير فهذا يعني أنك قد أضفت له صفتين رئيسيتين هما:

  • أصبح عمر المتغير مثل عمر المتغيرات العامة التي لا تُمسح من الذاكرة إلا إذا طلبت ذلك برمجيا, أو بانتهاء تنفيذ البرنامج .. لأن المتغيرات العامة تكون مُعرضة للقراءة في أي وقت ومن أي دالة.
  • لا ُيمكن رؤيتها إلا داخل الإطار التي أُعلنت بداخله.

المتغيرات الساكنة تأخذ مزايا النوعين السابقين .. فهي مثل المتغيرات الخاصة لأن هناك دالة وحيدة تستطيع رؤيتها وهي الدالة التي تم الإعلان عن المتغير بداخلها ,و من ناحية أُخرى فالمتغيرات الساكنة مثل المتغيرات العامة لأنها لا تنتهي أو تُمسح من الذاكرة عندما ينتهي تنفيذ الدالة التابعة لها .. بل تظل مخزنة في الذاكرة (جاهزة للاستدعاء) حتى ينتهي تنفيذ البرنامج.

أيضا, إضافة الكلمة static إلى الدوال تعني أنه لا يُمكن رؤية تلك الدوال خارج الملف الذي أعلن عنهم فيه.

باختصار, تدور فكرة الكود حول إعادة عنوان لم يعد محجوزا في الذاكرة, لأنه كان يشير إلى متغير محلي و بمجرد انتهاء عمل الدالة تم تحرير ذاكرة المتغير.

لذا سنحتاج في هذه الحالة إلى استخدام الخاصية static من أجل الحفاظ على عنوان المتغير.

1
#5
اقتباس
تفسير جيد أخي مصطفى.

teeth_smile.gifteeth_smile.gifteeth_smile.gifteeth_smile.gif

اقتباس
بالنسبة للمتغيرات العامة فتنتهي أعمارها عند انتهاء تنفيذ البرنامج

تعبير تنتهي أعمارها ... أقنعني طول الفترة الماضية ... ولكن بعد هذه الحلقة أرغب بمعرفة التعبير الدقيق ... ما معنى تنتهي أعمارها :

هناك ثلاثة احتمالات ...

  1. يتم تحرير الحجرة نهائياً ... أي أن الدخول إليها دون إعادة حجز يعتبر خرقاً .... AccessViolation
  2. يتم حذف التحكم بها .. (هذا أفضل تعبير عندي) كما يتم حذف المؤشر بواسطة delete أي أن الحجرة يمكن الوصول لها .. ولا يمكن التحكم بمحتواها لأنها خارج التحكم ...
  3. احتمال ثالث لم يخطر على بالي

وشكراً أخي أحمد ...

تم تعديل هذه المشاركة بواسطة أحمد الشنقيطي في 7 سبتمبر 2012 في 00:37 — السبب: Mise à jour

#6
اقتباس
تعبير تنتهي أعمارها ... أقنعني طول الفترة الماضية ... ولكن بعد هذه الحلقة أرغب بمعرفة التعبير الدقيق ... ما معنى تنتهي أعمارها ؟

المتغيرات المحلية لا يتم "تحريرها" عند الخروج من الـ Scope الخاص بالدالة, بل يتم استخدام Frame مختلف عن طريق زيادة الـ Stack pointer مما يؤدي الى جعل الـ Frame السابق قابل للكتابة عليه عن طريق تعريف متغيرات محلية اخرى في الـ Parent Routine.

في هذه الحالة سيظل المؤشر يشير الى ذاكرة صالحة للاستخدام لكننا لا نضمن أنه لن يتم الكتابة عليها Overwrite عن طريق حجز متغير محلي آخر.

بعبارة أبسط, الداله التى تحتوى على المتغير من نوع مؤشر قد انتهى عملها و تمت العودة منها ليتم تنفيذ باقى البرنامج, فى هذه الحالة المكان الموجود داخل ذاكرة الـ stack و الذى كان مستخدما من قبل المتغير من نوع المؤشر سيظل محتفظ بالقيمة التى به طالما لم يُستخدم ذلك المكان من قبل دالة أخرى.

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

يُغلق.

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

هذا الموضوع مغلق.

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