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

اختبر مهاراتك في المؤشرات (الحلقة الثانية)

مغلق
بدأه khaled_prg في 23 فبراير 2012 · 16 رد · 2,166 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

مرحبا بكم من جديد :)

من خلال هذه السلسلة سنحاول التطرق إلى المواضيع النائمة في المؤشرات, فهناك بعض المواضيع التي من النادر التطرق إليها و التحدث عنها بشيئ من التفصيل .. و هدفنا في هذه السلسلة هو كشف المستور (شكلي صرت صحفي ! :D ).

أسئلتنا هذه المرة ستكون عن أنواع المؤشرات من حيث الثبوتية و من حيث الإشارة. يعني هل المؤشر ثابت أم متغير ؟ و هل ما يشير إليه ثابت أم متغير ؟ و هل بإمكان المؤشر الإشارة إلى عدة أنواع ؟ و هل يمكن للمؤشر أن يشير إلى زميله في العمل ؟ :thinking: .

بعد أن تحدثنا في الحلقة الأولى عن كيفية إجراء بعض العمليات الحسابية البسيطة على المؤشرات. نود أن نتطرق في الحلقة الثانية من سلسلتكم اختبر مهاراتك في المؤشرات إلى موضوع جديد وهو أنواع المؤشرات و ما يوُاجهه المبرمجون من مخاطر محدقة بهم و على رأسها الــ casting و ما ينجم عن ذلك من إساءة لتسيير و إدارة الذاكرة.

مقدمة ساخنة :D

هل أصبحت ظاهرة المؤشرات أو ما بات يُعرف بــ عفاريت البرمجة ظاهرة برمجية محلية ؟؟ تستهدف أبناء العم Dennis Ritchie لتهدد أمن برامجهم و تخيفهم أكثر ؟ :cry:

أم أنها إشاعات مُغرضة ! هدفها الإضرار بسمعة المؤشرات في أوساط البرمجين ؟ :P

لماذا كل هذا التهويل ؟؟ :sleep:

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

أم أن تعقيد المؤشرات طغى على سرعتها ؟ رغم عدم وجود بديل !, يتساءل البعض !! :resentful:

و لكن في المقابل, كما يقول المثل : أهل مكة أدرى بشعابها, أليس حريا بمبرمجي السي++ أن يُتقنوا التعامل مع المؤشرات و إدارة شؤون الذاكرة في غضون أسابيع ؟ :hmm:

أم أن صعوبة المؤشرات تقف عائقا دون هذا الأمر ؟ :mad:

أسئلة من بين أخرى نطرحها في الموضوع مباشرة على كل من محمد أبو زايد و ahmedsaoud31 :evil:

  1. المؤشرت من حيث الثبوتية, تحدث بالتفصيل الممل عن :
    • المؤشر الثابت.
    • المؤشر إلى ثابت.
    • المؤشر الثابت إلى ثابت.

[*]المؤشرت من حيث الإشارة, تحدث بالتفصيل الممل عن :

  • المؤشر ذو النوع void (المؤشرات العائمة).
  • مؤشر إلى مؤشر.
  • مؤشر إلى مؤشر إلى مؤشر.

ملاحظة :

من أجل تنظيم الموضوع, نرجوا منك التحدث عن النقاط التالية :

  1. فائدة كل نوع !
  2. متى يُستخدم ؟
  3. كيف يتم التعامل معه ؟
  4. الفرق بينه و بين باقي الأنواع.

تحياتي.

تم تعديل هذه المشاركة بواسطة khaled_prg في 24 فبراير 2012 في 12:10

2
#2

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

اقتباس
أسئلة من بين أخرى نطرحها في الموضوع مباشرة على كل من العضوين محمد أبو زايد و ahmedsaoud31 :evil:
:haha:

مرحباً مجدداً أخ خالد لنواصل ألاعيب السحرة واللعب من وراء الأستار بإستخدام العصا السحرية "المؤشرات"

أطفئوا الأنوار وإستحضروا الشجاعة ... رجاء على السادة ضعاف القلوب مغادرة القاعة حالاً :D ...

khaled_prg كتب:

المؤشرت من حيث الثبوتية, تحدث بالتفصيل الممل عن :

المؤشر الثابت.

المؤشر إلى ثابت.

المؤشر الثابت إلى ثابت.

بالنسبة بالتفصيل الممل أنا هستبعدها النقطة دى وأهديها ليك أخ خالد :D

1 - المؤشر الثابت : هو تعريف مؤشر ثابت أى لا يمكن تغيير العنوان المسند لهذا المؤشر أثناء عمل البرنامج .

ويعرف كالأتى :

Type *const Pointer=&VarType;

حيث Type نوع المؤشر وال VarType متغير من نوع Type .

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

المتغير " للمؤشر ثابت القيمة " ولكن تستطيع تغيير العنوان الذى يشير له هذا المؤشر ,

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

ويعرف على الشكل التالى :

const Type *Pointer = &ValType;

or

const Type *Pointer = &ConstValType;

3 - المؤشر الثابت إلى ثابت : هو تعريف مؤشر ثابت العنوان ثابت القيمة أى لا تستطيع تغيير عنوانه أو قيمتة ولكن إن كانت القيمة المسندة للمؤشر من متغير غير ثابت ففى هذه الحالة تستطيع تغيير قيمة المؤشر ولكن من خلال

المتغير وليس المؤشر ,

ويعرف على الشكل التالى :

const Type *const Pointer=&ValType;

or

const Type *const Pointer=&ConstValType;

أما بالنسبة للجزء الثانى:

khaled_prg كتب:

المؤشرت من حيث الإشارة, تحدث بالتفصيل الممل عن :

المؤشر ذو النوع void (المؤشرات العائمة).

مؤشر إلى مؤشر.

مؤشر إلى مؤشر إلى مؤشر.

1 - المؤشر ذو النوع void : هو كبير المؤشرات يعنى المعلم فى كل حاجة شغال :D ... وهو مؤشر غير محدد النوع ولك مطلق الحرية لإسناد أى نوع من القيم له.

2 - مؤشر إلى مؤشر : وهو تعريف مؤشر يشير لمؤشر أخر من نوع معين ويعرف بإضافة علامتى ** بدلاً من واحدة والقيمة التى تكون داخل هذا المؤشر هى عبارة عن مؤشر أيضاً وللوصول للقيمة النهائية الموجودة داخل المؤشرين

نقوم بوضع علامتى ** قبل المؤشر للوصول للقيمة من النوع المعرف منه المؤشر , يستخدم عندما نريد عمل عملية ما على متغير خارج نطاق تعريفة "راجع فتره حياه المتغيرات" أى نرسل عنوان المؤشر ذاته.

3 - مؤشر إلى مؤشر إلى مؤشر : لا كده مش هنخلص :D على العموم بنفس فكرة المؤشر لمؤشر مع زيادة عدد النجمات :yes: لكن إستخدامه "لم أستخدمة من قبل" وعلى هذا النهج أيضاً تعريف مؤشر لمؤشر لمؤشر لمؤشر... مش بقلك مش هنخلص :D

دمتم بود

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

#3

أهلا بك أخي احمد :)

بارك الله فيك, فعلا معلومات قيمة.

اقتباس
أطفئوا الأنوار وإستحضروا الشجاعة ... رجاء على السادة ضعاف القلوب مغادرة القاعة حالاً :D ...
أظنني سأختبئ خلفك :D, هل توافق ؟ :lol:
اقتباس
بالنسبة بالتفصيل الممل أنا هستبعدها النقطة دى وأهديها ليك أخ خالد :D
و انا كمان هستبعدها و أهديها لأخي محمد أبو زايد :D

قبل الدخول في التفاصيل, نذكر أنه توجد كتابتان للإعلان عن متغير ثابت :

const Type x;
OR
Type const x;

من خلال الكتابتان السابقتان يمكننا استنتاج كتابتين لكل من المؤشر إلى ثابت و المؤشر الثابت إلى الثابت.

يمكننا الإعلان عن مؤشر إلى ثابت باستخدام إحدى ىالكتابتين :

const Type* x;
OR
Type const* x;

أما المؤشر الثابت إلى ثابت :

const Type* const x;
OR
Type const* const x;

الثوابت التى تحتوى على المؤشرات قد تجعلك تخطئ أحيانا في قرائتها و يقف شعرك من شدة التفكير :D، و هذا ما جعل Bruce Eckel يخصص لها فصل كامل فى كتابه Thinking in Cpp.

في بعض الأحيان تلزمك قراءة كتابة المؤشر من اليمين لليسار لكي تفهمها, و لكن ماذا يحدث إذا لم تفهمها بهذه الطريقه ؟, الحل حينها ان تقوم بتقسيم التعريف - أو التصريح - إلى جزئين أولهم الجزء على يمين علامة * و يتبع إسم الثابت (بالإضافه لكون الإسم مؤشر) و الجزء على يسار علامة * و يمثل قيمة الثابت، فمثلا:

int* const w;
const int* x;

بتطبيق الطريقه المذكوره على المثال الأول ستجد لديك جزئين const w و int و حيث ان w مؤشر لذا ستقرأ كالتالي : w هو مؤشر ثابت لرقم.

بتطبيقها على المثال الثاني ستجد لديك جزئين هما x و const int و حيث ان x هو مؤشر لذا ستقرأ كالتالي : x هو مؤشر لثابت رقمي.

للمزيد : فكر معي فى معني هذا الثابت

بالنسبة للجزء الثاني من الأسئلة :

اقتباس
المؤشر ذو النوع void (المؤشرات العائمة).

مؤشر إلى مؤشر.

مؤشر إلى مؤشر إلى مؤشر.

كما ذكر الأخ أحمد, العجيب في الأمر أن حضرة void لا مكان له بين المتغيرات (لا يمكن إنشاء منغير من النوع void) و مع ذلك فهو كبير العفاريت في المؤشرات, (كبيرهم الذي علمهم السحر :D )

تُستخدم المؤشرات العائمة في تكوين مصفوفة لأنواع مختلطة أو تمرير متغيرات لدوال تتوقع عدة أنواع من البيانات.

بخصوص الــ Pointer to Pointer فيتم الإعلان عنه هكذا :

Type **Pointer;

هذا النوع من المؤشرات يحوي عنوان, و العنوان بدوره يحوي قيمة. فهو إذا لا يشير إلى قيمة بل إلى مؤشر و يُستخدم عادة للتعامل مع مصفوفة ديناميكية ثنائية البعد.

اقتباس
مؤشر إلى مؤشر إلى مؤشر إلى مؤشر :D (ضفتها أنا من عندي)

يتم الإعلان عنه هكذا :

Type ****Pointer;

هذا النوع من المؤشرات يحوي عنوان, العنوان يحوي عنوان آخر, العنوان الآخر يحوى عنوان آخر مختلف, العنوان الآخر المختلف يحوي قيمة :D

يُستخدم هذا النوع من المؤشرات في إنشاء المصفوفات الديناميكية ذات البعد الرباعي.

تم تعديل هذه المشاركة بواسطة khaled_prg في 24 فبراير 2012 في 12:14

1
#4

ممتاز أخ خالد ... ننتظر مزيد من ألاعيبك السحرية قريباً

اقتباس
يُستخدم هذا النوع من المؤشرات في إنشاء المصفوفات الديناميكية ذات البعد الثلاثي.

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

وأيضاً ما حال باقى المؤشرت التى تتعدى المؤشرات " ثلاثية البعد " :D

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

#5

@khaled_prg: الكود التالي:

const Type* x;
OR
Type* const x;

اعتقد انك تقصد التالي:

const Type* x;
OR
Type const* x;

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

مدونتي: C++ Tips and Tricks

#6
اقتباس
@khaled_prg: الكود التالي:

const Type* x;
OR
Type* const x;

اعتقد انك تقصد التالي:

const Type* x;
OR
Type const* x;

نعم أخي محمد علاء الدين, هذا ما كنت أقصده. تم تحرير المشاركة.

هذه الكتابة :

Type* const x;

تعني مؤشر ثابت و ليس مؤشر لثابت. أشكرك على التصحيح.

اقتباس
أنا إستخدمت مؤشر واحد للمصفوفة ذات البعدين

جميل جدا, إذا أرنا طريقتك :)

بالنسبة لي, أستخدم مؤشر لمؤشر للإعلان عن مصفوفة ديناميكية ثنائية البعد.

نقوم بقراءة عدد الأسطر و عدد الأعمدة من المستخدم :

int row,col;
cin>>row>>col;

و نعلن عن مصفوفة مؤشرات تحتوي على عدد a من العناصر :

int **p=new int*[row];

ثم نحجز لكل عنصر بالبعد الأول عدد من العناصر التي تمثل البعد الثاني :

for(int i=0;i<row;i++)
   p=new int[col];

اقتباس
بالنسبة للمصفوفات ثلاثية البعد أريد مثالاً عليها.

انظر هنا : C++ 3d array - dynamic memory allocation aligned in one line

اقتباس
وأيضاً ما حال باقى المؤشرت التى تتعدى المؤشرات " ثلاثية البعد " :D

يُطلقون عليها 4D arrays و يقولون أنها من ألذ أنواع المصفوفات :D مع أنني لا أعلم متى أستخدمها :P

يبدون أن أسئلة الحلقة الثانية قاربت على الإنتهاء :)

ما رأيكم, هل نعلن عن الحلقة الثالثة اليوم أو ماذا ؟

تم تعديل هذه المشاركة بواسطة khaled_prg في 24 فبراير 2012 في 14:21

#7
اقتباس
و انا كمان هستبعدها و أهديها لأخي محمد أبو زايد :D

على فكرة أنا كنت سأبدأ بنقطة void pointer وان شاء الله سأوضحها ...

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

#9

المؤشرات العامة Void Pointer

تعرف المؤشرات العامة بأنها نوع خاص من المؤشرات التى يمكن ان تشير الى اى نوع من البيانات أو الكائنات.

يتم اﻻعلان عنها كأى متغير عادى باستخدام الكلمة الحاكمة void كمحدد نوع المتغير :

void *Pvoid;

وكما عرفنا فأن المتغير العام يمكن أن يشير الى اى نوع من البيانات أو الكائنات.

#include <iostream>

using namespace std;

int main()
{
    int nValue;
    float fValue;

    struct Something
    {
        int nValue;
        float fValue;
    };

    class OneCalass
    {
    public:
        int Ones;
    };

    Something sValue;
    OneCalass cOneclass;

    void *pVoid;
    pVoid = &nValue;    // صحيح
    pVoid = &fValue;    // صحيح
    pVoid = &sValue;     // صحيح
    pVoid = &cOneclass ; // صحيح

    return 0;
}

ومع ذلك ، وﻻنة بسبب أن المؤشر العام ﻻيعرف نوع البيانات أو الكائنات التى يشير اليها . فأنة ﻻيمكن يغير قيمتها مباشرا. لذلك فأنة يجب تحويل المؤشر العام بشكل صريح الى نوع مؤشر آخر متوافق مع نوع البيانات أو الكائنات اﻻصلى قبل محاولة تغيير قيمة المتغير اﻻصلى.:

int nValue = 5;
void *pVoid = &nValue;
*pVoid = 100 ; غير صحيح ﻻيمكن عمل ذلك وسوف يولد المترجم خطأ.
// ﻻبد من تحويل *void الى *int.
int *pInt = static_cast<int*>(pVoid);
*pInt = 100 ;
cout << nValue; // or cout << *pInt;

وعلى نفس الطريقة ﻻيمكن استخدام المعاملات الحسابية مع المؤشر العام مثل Pvoid++; وبما انة ﻻيمكن للمؤشرات العامة تغيير القيمة التىتشير اليها وبالتالى ليس هناك ما يسمى بالمرجع العام reference.

السؤال التالى بوضوح: اذا المؤشر العام ﻻيعرف نوع البيانات التى يشير اليها. كيف لنا نعرف نوع المتغير المشار اليها حتى نتمكن من أجراء التحويل المناسب علية لتمكن من استخدامة؟ للأسف هذا متروك لك حبيبى المبرمج وعليك تتبعة:

#include <iostream>

enum Type
{
    INT,
    FLOAT,
    STRING,
};

void Print(void *pValue, Type eType)
{
    using namespace std;
    switch (eType)
    {
        case INT:
            cout << *static_cast<int*>(pValue) << endl;
            break;
        case FLOAT:
            cout << *static_cast<float*>(pValue) << endl;
            break;
        case STRING:
            cout << static_cast<char*>(pValue) << endl;
            break;
    }
}

int main()
{
    int nValue = 5;
    float fValue = 7.5;
    char *szValue = "Mollie";

    Print(&nValue, INT);
    Print(&fValue, FLOAT);
    Print(szValue, STRING);
    return 0;
}

المخرجات:

5
7.5
Mollie

عموما من اﻻفضل لك ان تتجنب استخدام المؤشر العام أن لم تكن تعلم بشكل تام وصريح ماذا تفعل. انها تسمح لك بتفادى موضوع التأكد من نوع المتغيرات قبل استخدامها وهذة مهمة المترجم طبعا وبالتالى أنت ستقوم بهذا بدﻻ من المترجم اى ان برنامج لن يصدر أى خطأ اثناء الترجمة بينما تحدث كوارث وقت التشغيل ... . على سبيل المثال سطر الكود التالى ربما يكون صحيحا:

Print(&nValue, STRING);

من يعرف النتيجة الفعلية التى يمكن ان تظهر؟

على الرغم من أن الدالة الاعلاة تبدو طريقة لطيفة لكى تتمكن من تمرير أكثر من نوع بيانات الى متغير واحد شئ رائع اليس كذلك؟ لكن تذكر أن سي++ لديها امكانية أخرى لعم ذلك عن طريق التحميل الزائد على الدوال مع تمتعك بدعم كامل من المترجم لفحص كل نوع بيانات مرر والتأكد انة النوع المطلوب على غرار المؤشر العام. وقد تستخدم المؤشرات العامة فى اغراض متعددة ولكن مع السي++ فى ميزة القوالب سوف يرتاح عقلك وبالك .

ولكن فى بعض اﻻحيان ربما تجد سببا ﻷستخدام المؤشر العام.

فقط كن متأكد أنة ﻻتوجد طريقة آمنة لفعل نفس الشئ بأستخدام لغات اخرى . . . .

توضيح: عندما نتحدث فى السي++ عن انواع البيانات فأننا نقصد انواع الكائنات مثل اﻻعداد الصحية والنصوص int و string وكلنا يعلم أنها فئات معرفة مسبقا بداخل اللغة وبالتالى فان أى استخدم للفئة يكون فى صورة كائن. ولكننا للتبسيط على اخواننا المبتدئين نقول أى نوع من البيانات أو الكائنات لكن فى اﻻصل أى نوع من الكائنات فقط.

واخير وليس اخير ساحول ان اضيف فى وقت ﻻحق مشاركة صغيرة تتمثل فى ما قالة معلمى اﻻول فى سي و سي++ اﻻستاذ روبيرت ﻻفر أن شاء الله

جميع الكود تم اختبارة فى بيئة تطوير QT Creator وابينتو 11.10

تحياتى العطرة والله ولى التوفيق....

1

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

#10

السلام عليكم

1. كما ذكر الأخ محمد فإن مؤشر الـ void يمكن اعتباره يشير الى كائن غير معروف الطول او الهوية ، مجرد كتلة من الذاكرة .

انت من يحدد كيف يستخدمه ، فمثلا يمكنك كتابة دالة تقوم بتصفير ذاكرة اي كائن ، ستحتاج ان تمرر لها طول الكائن ومؤشر void لانك لا تعرف ان كنت ما ستقوم بتصفيره هو int ام char ام class ام structure ، هنا تظهر اهمية مؤشر الـ void.

2. يوجد حطأ بسيط في هذا الكود :

#include <iostream>

enum Type
{
    INT,
    FLOAT,
    STRING,
};

void Print(void *pValue, Type eType)
{
    using namespace std;
    switch (eType)
    {
        case INT:
            cout << *static_cast<int*>(pValue) << endl;
            break;
        case FLOAT:
            cout << *static_cast<float*>(pValue) << endl;
            break;
        case STRING:
            cout << static_cast<char*>(pValue) << endl;
            break;
    }
}

int main()
{
    int nValue = 5;
    float fValue = 7.5;
    char *szValue = "Mollie";

    Print(&nValue, INT);
    Print(&fValue, FLOAT);
    Print(szValue, STRING);
    return 0;
}

عند الإستدعاء الثالث للدالة Print أرسلتَ لها مؤشر لثابت حرفي و مع ذلك فهي تستقبل مؤشر لمتغير حرفي ! :)

3.

اقتباس
من يعرف النتيجة الفعلية التى يمكن ان تظهر؟

أعتقد أنه سيتم إظهار القيمة المقابلة للمتغير nValue في جدول ASCII.

4.

اقتباس
عندما نتحدث فى السي++ عن انواع البيانات فأننا نقصد انواع الكائنات مثل اﻻعداد الصحية والنصوص int و string وكلنا يعلم أنها فئات معرفة مسبقا بداخل اللغة وبالتالى فان أى استخدم للفئة يكون فى صورة كائن. ولكننا للتبسيط على اخواننا المبتدئين نقول أى نوع من البيانات أو الكائنات لكن فى اﻻصل أى نوع من الكائنات فقط.

إذا كان النوع Primitive مثل int فلا أظن أنه يمكننا القول بأنه كائن, لأن مصطلح "الكائنات" يستخدم الأنواع البدائية (الأصلية) لتعريفه.

اقتباس
ساحاول ان اضيف فى وقت ﻻحق مشاركة صغيرة تتمثل فى ما قالة معلمى اﻻول فى سي و سي++ اﻻستاذ روبيرت ﻻفر أن شاء الله

بانتظار مشاركتك أخي محمد :)

تم تعديل هذه المشاركة بواسطة khaled_prg في 24 فبراير 2012 في 21:06

1
#11
اقتباس
2. يوجد حطأ بسيط في هذا الكود :

عند الإستدعاء الثالث للدالة Print أرسلتَ لها مؤشر لثابت حرفي و مع ذلك فهي تستقبل مؤشر لمتغير حرفي ! :)

وضح لى النتائج المترتبة على هذا الخطأ؟

حيث ان cout تبحث عن عنوان بداية السلسة char تذكر ان الكود يقوم تحويل المؤشر العام الى مؤشر حرف فى طرف ثالث او يستقبل النتيجة طرف ثالث وانة لنيتمكن من تعديل السلس النصية اﻻصلية .... !

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

#12

شكراً أخ محمد على الشرح بالتفصيل الممل للمؤشر void :D

khaled_prg كتب:
ahmedsaoud31 كتب:

أنا إستخدمت مؤشر واحد للمصفوفة ذات البعدين

جميل جدا, إذا أرنا طريقتك :)

للسرعة قمت بشرح الطريقة على ورقة وأرفقت صوره منها " عذراً على سوء الخط :happy: "

scane.jpg

وهذا برنامج كتطبق على ما سبق شرحه :

#include <stdio.h>
#include <stdlib.h>
int main(void)
{   
	int i=0,j=0,m=0,n=0,*pB=NULL;
	printf("Please enter the size Of Array 2D :\nm n\n");
	scanf("%d%d",&m,&n);
    pB = (int *)malloc(sizeof(int)*(m*n));
	for(i=0;i<m;i++)
		for(j=0;j<n;j++)
			scanf("%d",(pB+j)+(i*n));
	printf("Otputs:\n");
    for(i=0;i<m;i++)
		for(j=0;j<n;j++)
			printf("%d\n",*((pB+j)+(i*n)));
    return 0;
}

وقمت بتنفيذ الطريقة على المصفوفات ثلاثية البعد أيضاً بإستخدام مؤشر واحد فقط وإليك كود البرنامج :

#include <stdio.h>
#include <stdlib.h>
int main(void)
{   
	int i=0,j=0,k=0,x=0,y=0,z=0,*pB=NULL;
	printf("Please enter the size Of Array 3D :\nx y z\n");
	scanf("%d%d%d",&x,&y,&z);
    pB = (int *)malloc(sizeof(int)*(x*y*z));
	for(i=0;i<x;i++)
		for(j=0;j<y;j++)
			for(k=0;k<z;k++)
			scanf("%d",(pB+k)+(j*y)+(i*z));
	printf("Otputs:\n");
    for(i=0;i<x;i++)
		for(j=0;j<y;j++)
			for(k=0;k<z;k++)
			printf("%d\n",*((pB+k)+(j*y)+(i*z)));
    return 0;
}

وهكذا بنفس الطريقة للمصفوفات رباعية البعد

khaled_prg كتب:
ahmedsaoud31 كتب:

وأيضاً ما حال باقى المؤشرت التى تتعدى المؤشرات " ثلاثية البعد " :D

يُطلقون عليها 4D arrays و يقولون أنها من ألذ أنواع المصفوفات :D مع أنني لا أعلم متى أستخدمها :P

أنا كنت أقصد المؤشرات التى تتعدى المؤشر لمؤشر لمؤشر أو بلغة الكود p**** أو p***** أو أكثر من هذا :D

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

#13
اقتباس
وضح لى النتائج المترتبة على هذا الخطأ؟

ليس خطأ بالمعنى الدقيق .. و إنما هو تحذير (warning) :

warning: deprecated conversion from string constant to 'char*'
اقتباس
للسرعة قمت بشرح الطريقة على ورقة وأرفقت صوره منها

لذيذ جدا :D, بارك الله فيك.

اقتباس
أنا كنت أقصد المؤشرات التى تتعدى المؤشر لمؤشر لمؤشر أو بلغة الكود p**** أو p***** أو أكثر من هذا :D

الروابط التي وضعتها لك بالأعلى تحتوى على مصفوفات ديناميكية, ثلاثية و رباعية البعد :)

يمكنك الإعلان عن مصفوف ديناميكية رباعية البعد (5*5*5*5) هكذا :

int ****array_4d;
int dim1 = 5;
int dim2 = 5;
int dim3 = 5;
int dim4 = 5;
int i, j, k;

array_4d = calloc(dim1, sizeof(array_4d[0]));
for(i = 0; i < dim1; i++)
{
    array_4d = calloc(dim2, sizeof(array_4d[0][0]));
    for(j = 0; j < dim2; j++)
    {
        array_4d[j] = calloc(dim3, sizeof(array_4d[0][0][0]));
        for(k = 0; k < dim3; k++)
        {
            array_4d[j][k] = calloc(dim4, sizeof(array_4d[0][0][0][0]));
        }
    }
}

و قس على ذلك البقية :D

بالمناسبة, أرغب في الإعلان الليلة عن الحلقة الثالثة لأنني ربما أنقطع عن المنتدى في الأيام القادمة بسبب ظروف الدراسة.

بالتوفيق.

تم تعديل هذه المشاركة بواسطة khaled_prg في 25 فبراير 2012 في 00:22

#14
اقتباس
الروابط التي وضعتها لك بالأعلى تحتوى على مصفوفات ديناميكية, ثلاثية و رباعية البعد :)

عذراً أخ خالد لم أتصفح الروابط ... :wink:

ولكن ما رآيك بإستخدام المؤشر الواحد فى عمل المصفوفات الديناميكية متعددة البعد :blush: والفرق بينها وبين الكود الموجود فى الروابط " الكود فى المشاركة السابقة "

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

#15

مرحبا إخوتي :)

بمناسبة التحدث عن الحجز الديناميكي, أحب أن أوضح للقارئ بعض المفاهيم الأساسية حول كل من malloc, calloc, memset et realloc :

  • الدالة malloc تستقبل عدد البايتات (نوعية حجم البيانات) و تعيد مؤشر void يشير إلى مكان الحجز مع العلم أنها لا تقوم بتصفير الذاكرة لذلك سنحتاج إلى الدالة memset (إن أردنا التصفير) التي تستقبل ثلاث معاملات, المعامل الأول هو المؤشر نفسه و الثاني هو القيمة المراد وضعها بداخل الخانة التي يشير إليها المؤشر و الثالث هو عدد البايتات أو كمية البيانات المُؤشر عليها انظر المثال:
    char* ptr = malloc(sizeof(char)) ;
    if(ptr != NULL)
    {
    memset(ptr,0x00,sizeof(char));
    …
    free(ptr);
    }


  • الدالة calloc تستقبل معاملين, الأول هو عدد العناصر المراد حجزها، و المعامل الثاني هو حجم العنصر الواحد بالبايت, هذه الدالة تُعيد مؤشر void يشير إلى مكان أول خانة من هذه الخانات مع العلم أنها متتالية في الترتيب لذلك يمكننا الوصول إلى بقية الخانات عن طريقة تحريك المؤشر.
    مثلا اذا اردنا حجز 5 عناصر من نوع int اذا نقوم بالتالي :
    malloc(5 * sizeof(int));


    أو

    calloc(5, sizeof(int));


    مع اختلاف بسيط وهو أن الدالة calloc تقوم بتصفير البيانات التي تحجزها، وفي العادة calloc تقوم باستدعاء malloc داخلها .

  • لكن, ما فائدة calloc إذا كانت تستدعي malloc !؟و مالفائدة من استخدام calloc إذا كانت malloc تفي بالغرض !؟ ... عمليا ليس هناك فرق بينهما, لكن لجعل الكود اكثر وضوحا يفضل استخدام malloc عند حجز ذاكرة لكائن واحد و calloc عند حجز مصفوفة ...
  • لتقريب الفكرة, يمكنك اعتبار أن malloc + memset = calloc
  • realloc تقوم باعادة حجز ذاكرة تم حجزها مسبقا عن طريق calloc/malloc بحجم مختلف وتقوم بنسخ البيانات من الذاكرة القديمة الى الاحدث اذا كان هذا ممكنا ثم تقوم بتحرير الذاكرة القديمة وارجاع عنوان للذاكرة الاحدث . (يمكن استخدامها عندما نريد حذف أو إضافة عناصر إلى المصفوفة)

نعود الآن إلى سؤال الأخ أحمد أبو السعود :

اقتباس
ما رآيك بإستخدام المؤشر الواحد فى عمل المصفوفات الديناميكية متعددة البعد :blush: والفرق بينها وبين الكود الموجود فى الروابط

توجد ثلاث طرق للإعلان عن المصفوفات الديناميكية سواء كانت ثنائية أو ثلاثية أو رباعية البعد أو ...

للتبسيط, سأعتبر أن المصفوفة ثنائيىة البعد :

  • الطريقة الأولى (و هي التي استخدمتـَـها بالأعلى) : نحجز مؤشر P هكذا :
    P = malloc(NBLIG* NBCOL* sizeof(élément))


    حيث NBLIG هو عدد الأسطر و NBCOL هو عدد الأعمدة و élément هو نوع عناصر المصفوفة.
    بهذه الطريقة يمكننا الوصول إلى العنصر الذي يوجد عند تقاطع السطر i و العمود j بالكتابة التالية :

    *( (P+j)+(i*NBCOL) )


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

سيتم الإعلان عن الحلقة الثالة اليوم إن شاء الله. انتظرونا :)

بالتوفيق.

1
#16

تسلم أخ خالد أظن بكده نكتفى مناقشات " الموضوع تشبع " :D ونلتقى فى حلقة جديدة من حلقات المسلسل الغامض :ph34r:

لاتحسبن العلم ينفع وحده مالم يتوج ربه بخلاق

لا إله إلا أنت سبحانك إني كنت من الظالمين

#17

من أجل فهرسة الموضوع, هذا رابط الحلقة الثالثة :

اختبر مهاراتك في المؤشرات (الحلقة الثالثة) - المشروع الأول : مُحاكاة عملية التسجيل في الــ Yahoo

نلتقي هناك :wink:

تم تعديل هذه المشاركة بواسطة khaled_prg في 26 فبراير 2012 في 20:25

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

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