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

سؤال في المؤشرات إلى سلاسل نصية

بدأه مومو (momo) في 28 أغسطس 2012 · 9 رد · 932 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم

مساعدة بسيطة في المؤشرات هناك شيء مبهم لا أعرف ما هو؟

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

 int lire(char *, int );

 int main(int argc, char *argv[])
{
	char nom[10];

	printf("Quel est votre nom ? ");
	if(lire(nom, 10))
	printf("Ah ! Vous vous appelez donc %s !\n\n", nom);

	return 0;
}

int lire(char *chaine, int longueur)
{
	char *positionEntree = NULL;

	if (fgets(chaine, longueur, stdin) != NULL)  
	{
    	positionEntree = strchr(chaine, '\n'); 
    	if (positionEntree != NULL) 
    	{
        	*positionEntree = '\0'; // هنا الإشكالية
    	}
    	return 1;
	}
	else
	{
    	return 0; 
	}
}

التناقض الذي وقعت فيه هو كالتالي :

لماذا عدلنا على القيمة النصية الثابتة التي يتضمنها المؤشر كعنوان؟

الشيء المبهم بالنسبة لي في الكود !!!

  *positionEntree = '\0';

لنأخذ هذا المثال:

char *B  = "hello !";

فكما نعلم فإن المؤشر B يؤشر على سلسلة ثابتة من الحروف مخزنة في مكان ما في الذاكرة.

يمكننا تغيير المؤشر ليؤشر على شيء آخر(نص ثابت ,عنوان لمتغير char..) أما السلسلة النصية الثابتة يمكننا قراءتها ولكن لا يمكن التعديل عليها

بتبديل حرف ما من الأحرف التي تتكون منها

char *p="abcdef";

*p='z'; // error

ولكن في المثال السابق

*positionEntree = '\0'; // لا وجود لأي خطأ الكود يشتغل عادي

وهنا أقع في تناقض wacko.gif ما هو تفسيركم لهذاالأخير؟؟

i love you mama

#2
اقتباس
لماذا عدلنا على القيمة النصية الثابتة التي يتضمنها المؤشر كعنوان؟ الشيء المبهم بالنسبة لي في الكود !!!

*positionEntree = NULL; ليس ثابت مؤشر وانما هو متغير مؤشر. وتعيين القيمة المبدئية NULL له ، هذا تلقيد عام حتى نتفادي بدء تحميلة بقيمة عشوائية قد تضر بالبرنامج.

*positionEntree = '\0'; المشكلة تأتى هنا انت تقوم بتعيين قيمة لمكان لم تحجزة فى الذاكرة وهذة كارثة. ولكن لا كارثة ولا حاجة لان الدالة strch() سوف ترجع عنوان الحرف التى بحثت عن فى chaine او ترجع NULL.

وأن كلا من NULL تسخدم مع المؤشرات.

'\0' تستخدم كعلامة نهاية سلسلة نصية مع المصفوفات من نوع char فى لغة سي و سي++.

اقتباس
فكما نعلم فإن المؤشر B يؤشر على سلسلة ثابتة من الحروف مخزنة في مكان ما في الذاكرة.

يمكننا تغيير المؤشر ليؤشر على شيء آخر(نص ثابت ,عنوان لمتغير char..) أما السلسلة النصية الثابتة يمكننا قراءتها ولكن لا يمكن التعديل عليها

انا اتعجب انك اجبت على نفسك فأنه لايمكن تغير السلسلة النصية الثابتة فلما تحاول تعديلها بالكود: *p='z';

نعم يمكننا تغيير المؤشر ليؤشر على شيء آخر مثال:

#include<iostream>
using namespace std;

int main()
{
    char *B = "hello !";
    char *p = "abcdef";
    cout << p << endl;
    p = B;
    cout << p;
    return 0;
}

وتذكر: ان p أو B متغير مؤشر. وأن "abcdef" سلسلة نصية ثابتة.

مخرجات:

abcdef
hello !
RUN SUCCESSFUL (total time: 308ms)

اتمني ان تكون تفهمت هذة النقطة جيدا ... اذا وجت صعوبة فحاول ثانياً

والله ولى التوفيق

سأكمل باقى النقاط فى الصباح لان النعاس يسبب كوارث

تم تعديل هذه المشاركة بواسطة محمد عودة في 28 أغسطس 2012 في 02:02

1

post-15367-027053900%201340345097.gifpost-15367-087183200%201340344597.gif
post-247365-0-10080400-1379403554_thumb.

#3

بالنسبة للكود الأول, الدالة fgets ستحتفظ بحرف نهاية السطر \n مع السلسلة التي تمت قرائتها

هذه الدالة راح تواصل القرائة إلى أن تعثر على نهاية السطر أو أن تقرأ عدد من الحروف مساوي لـ longueur

لذلك تم البحث عن الحرف \n باستخدام الدالة strchr و التي بدورها سترجع مؤشر أو عنوان لأول تطابق مع الحرف المراد البحث عنه, يعني في هالحالة راح تكون القيمة المحفوظة في positionEntree هي عنوان الحرف \n الموجود في آخر السلسلة chaine

و بما أنه مؤشر, و المراد تغيير القيمة المحفوظة فيه \n بـ \0 فلابد من التعليمة

*positionEntree = '\0';

-----------

بالنسبة لتساؤلك, فلا يوجد أي تناقض عزيزي, فقط يوجد تفسير صغير

استعمال char* سيقوم بحجز مؤشر يمكن أن تغير عنوانه أثناء التشغيل, بينما استعمال char[] يقوم بحجز مصفوفة و لا يمكن تغيير عنوانها أثناء التشغيل, فقط تغيير محتواها

char B[]  = "hello !\0";
char *C  = "hi";
C = B; // صحيحة
B = C; // خطأ, لا يمكن تغيير المؤشر B
printf("%s\n", C);

من جهة أخرى, عند تنفيذ الكود التالي :

char *B  = "hello !\0";

فان الكومبايلر سيحجز مكان في الذاكرة ستاتيك (static) للسلسلة hello و لا تحاول أن تغير محتوى السلسلة بعدها باستعمال:

*B = 'z';

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

char B[]  = "hello !\0";
*B ='z';
printf("%s\n", B); //zello

والله أعلم

تم تعديل هذه المشاركة بواسطة khatibe_30 في 28 أغسطس 2012 في 01:24

1

[سبحان الله و بحمده, سبحان الله العظيم]

#4

السلام عليكم

توقيع الدالة strchr هو :

char *strchr (const char *s, int c);

و هي تُعيد مؤشر يشير إلى مكان أول ظهور للحرف c داخل السلسلة الحرفية s.

أعتقد أنه يوجد خطأ ثانٍ و هو اسخدام المؤشر المحلي positionEntree كقيمة مُعادة من طرف الدالة Lire !, أو على الأقل قمتَ باستخدام المؤشر المحلي خارج نطاق الدالة المُعرف فيها !!

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

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

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

1
#5
أحمد الشنقيطي كتب:
/color]

أعتقد أنه يوجد خطأ ثانٍ و هو اسخدام المؤشر المحلي positionEntree كقيمة مُعادة من طرف الدالة Lire !, أو على الأقل قمتَ باستخدام المؤشر المحلي خارج نطاق الدالة المُعرف فيها !!

أخي أحمد, لم أفهمك, عذرا يمكن فهمي بطيء شوي :blink:

كيف استخدم المؤشر positionEntree كقيمة معادة من الدالة لير؟

ما أراه هو أنه استخدم هذا المؤشر بشكل مؤقت لحفظ العنوان المرجع من الدالة strchr(chaine, '\n'); حتى يغير محتواه و يخرج أم هناك شيء لم أره؟

[سبحان الله و بحمده, سبحان الله العظيم]

#6
محمد عودة كتب:

لمشكلة تأتى هنا انت تقوم بتعيين قيمة لمكان لم تحجزة فى الذاكرة وهذة كارثة.

نفس الملاحظة تقريبا !! فأنا كذلك لم أستصغ فكرة تعيين قيمة لمكان غير محجوز في الذاكرة

ولكن هكذا وجدت الكود في هذه الصفحة site de zero

عدلت الكود قليلا ليحجز لنا مكانا للمؤشر positionEntree

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

 int lire(char *, int );
 void pb_memoire(void);

 int main(int argc, char *argv[])
{
    	char nom[10]="";

    	printf("Quel est votre nom ? ");
    	if(lire(nom, 10))
    	printf("Ah ! Vous vous appelez donc %s !\n\n", nom);

    	return 0;
}

int lire(char *chaine, int longueur)
{
    	char *positionEntree = NULL;

    	positionEntree=(char*) calloc(2,sizeof(char)); // "\n\0"سيكون هكذا  positionEntree سنحجز فقط لحرفين لأن النص المعاد ل

    	if (positionEntree==NULL) pb_memoire();

    	if (fgets(chaine, longueur, stdin) != NULL)
    	{
    	positionEntree = strchr(chaine, '\n');
    	if (positionEntree != NULL)
    	{
            	*positionEntree = '\0';
    	}
    	free(positionEntree); return 1;
    	}
    	else
    	{
    	free(positionEntree); return 0;
    	}
}

void pb_memoire()
{
	printf("ERREUR : probleme de memoire !\n");
	exit(EXIT_FAILURE);
}

محمد عودة كتب:

سأكمل باقى النقاط فى الصباح لان النعاس يسبب كوارث

نوما هنيئا happy.gif

تم تعديل هذه المشاركة بواسطة مومو (momo) في 28 أغسطس 2012 في 04:21

i love you mama

#7
khatibe_30 كتب:

بالنسبة للكود الأول, الدالة fgets ستحتفظ بحرف نهاية السطر \n مع السلسلة التي تمت قرائتها

هذه الدالة راح تواصل القرائة إلى أن تعثر على نهاية السطر أو أن تقرأ عدد من الحروف مساوي لـ longueur

لذلك تم البحث عن الحرف \n باستخدام الدالة strchr و التي بدورها سترجع مؤشر أو عنوان لأول تطابق مع الحرف المراد البحث عنه, يعني في هالحالة راح تكون القيمة المحفوظة في positionEntree هي عنوان الحرف \n الموجود في آخر السلسلة chaine

و بما أنه مؤشر, و المراد تغيير القيمة المحفوظة فيه \n بـ \0 فلابد من التعليمة

*positionEntree = '\0';

كإضافة لما قلت

فعندما ندخل للدالة fgets هذا النص "hello" ولما نضغط على entree سيصبح هكذا "hello\n\0"

وعندما نستدعي الدالة lire ستقوم باستبدال الحرف 'n\' ب '0\' تصبح chaine هكذا "hello\0\0" ولكن هذه ليست مشكلة لأن الجهاز سيتوقف عند أول '0\'

تم تعديل هذه المشاركة بواسطة مومو (momo) في 28 أغسطس 2012 في 02:52

i love you mama

#8
أحمد الشنقيطي كتب:

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

هل هذا يعني أنه بلزمني تحرير المؤشر عند الإنتهاء منه أم شيئا آخر غير ذلك؟؟wacko.gif

تم تعديل هذه المشاركة بواسطة مومو (momo) في 28 أغسطس 2012 في 02:53

i love you mama

#9
مومو (momo) كتب:

نفس الملاحظة تقريبا !! فأنا كذلك لم أستصغ فكرة تعيين قيمة لمكان غير محجوز في الذاكرة

ولكن هكذا وجدت الكود في هذه الصفحة site de zero

عدلت الكود قليلا ليحجز لنا مكانا للمؤشر positionEntree

انا قلت:

اقتباس
*positionEntree = '\0'; المشكلة تأتى هنا انت تقوم بتعيين قيمة لمكان لم تحجزة فى الذاكرة وهذة كارثة. ولكن لا كارثة ولا حاجة لان الدالة strch() سوف ترجع عنوان الحرف التى بحثت عن فى chaine او ترجع NULL.

ويتضح ان ليس هناك مشكلة اطلاقاً ﻻن الدالة strch() سوف ترجع قيمتان:

أم NULL وهي قيمة مقبولة لمتغير مؤشر .

او عنوان حرف ترجعة الدالة strch()

وبالتالى مشكلة تعيين قيمة الى مؤشر ﻻيشير الى شئ قد انتهت.

والبرمج قد سيطر على هذا السيناريو بالكود:

        if (positionEntree != NULL) 
        {
                *positionEntree = '\0'; // هنا الإشكالية
        }
        return 1;

والكود يعمل وان شاء الله ليس فية اي مشكلة .. وراجع مواصفات الدالة fgets

وان الكود:

        positionEntree=(char*) calloc(2,sizeof(char)); // "\n\0"سيكون هكذا  positionEntree سنحجز فقط لحرفين لأن النص المعاد ل

ﻻ داعى له اطلاقا ﻻن الدالة strchr قد قامت بعملة

post-15367-027053900%201340345097.gifpost-15367-087183200%201340344597.gif
post-247365-0-10080400-1379403554_thumb.

#10

عفوا ! لم أدقق في المثال, كلامي السابق ينطبق على كود كهذا :

char *foo(void) {
    char x[] = "foo";
    return(&x);
}

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