ممكن خدمة أكون شاكره لكم ...|شبكة المعلومات|منتديات إبحار بلا مركب|إبحار بلا مركب

Forum Scope


Match



Forum Options



Min search length: 3 characters / Max search length: 84 characters
Forum Login
Lost password?
ممكن خدمة أكون شاكره لكم ...
أفاتار (الصورة التعريفية)
لحظـ غروب ـــة
24 Posts
(Offline)
1
الثلاثاء 21 شوال 1429مساءً12 21-10-2008مساءًالثلاثاء -
Print

** you do not have permission to see this link **

ممكن أحد يعطيني خلفية عن برمجة ++c …
سهلة أو صعبة وإذا في أحد مجربها ياليت يفيدنا بخبرته …
طبعاً أنا عندي فكرة عنها …
بس أبغى تفاصيل بسيطة في كتابة البرنامج…تساعدني أن أبدأ في دراستها …

لحظ غروب ــــة

أفاتار (الصورة التعريفية)
زهير
168 Posts
(Offline)
2
الثلاثاء 21 شوال 1429مساءً20 21-10-2008مساءًالثلاثاء -
Print

المؤشرات في لغة السي و السي++

Pointers

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

أولاً: ماذا هو المؤشر ؟

المؤشر هو متغير يحتوي على عنوان متغير آخر.

و لكن ماذا يعني هذا الكلام ؟

عندما نعرف متغير من نوع int هكذا:

int a;

إذاً عند تنفيذ البرنامج سوف يحجز نظام التشغيل مقدار 2 أو 4 بايت لهذا المتغير في الذاكرة الرئيسية Main Memory يضع فيها المتغير معلوماته من قيمت المتغير و ما إلى ذلك و لكن كيف سيصل لها البرنامج عندما يحتاج أن يعرف قيمة هذا المتغير ؟

إذاً لا بد أن يكون لهذا المكان عنوان لا سيما إذا عرفنا ان الذاكرة الرئيسية مقسمة إلى عناوين.

ففي الأنترنت مثلاً لا تستطيع أن ترسل لي شيئاً إلا إذا كنت تعرف بريدي الإلكتروني (عنواني) و لكن لماذا ؟

لأنه يوجد في الانترنت آلاف الأشخاص فكيف تفرق بيني و بينهم ؟ بالعنوان طبعاً, و هذا الشيئ ينطبق على أجزاء الذاكرة الرئيسية.

إذاً المؤشر هم متغير يوجد بداخلة العنوان الذي يؤشر عليه.

ثانياً: كيف نعرف مؤشر ؟

ببساطة نستطيع تعريف المؤشر بإضافة العلامة * قبل إسم المتغير.

بالشكل التالي:

type *variable;

حيث أن type من الممكن أن يكون أي نوع نت أنواع البينات مثل:

int, char, float, double, …

أو من الممكن حتى أن يكون Structure أو Class .

فمثلاً عندما نريد تعريف مؤشر من نوع int يكون كالتالي:

int *p;

و يفضل أن يكون إسم المؤشر محتوي على الحرف p أو ptr بحيث يدل على أنه مؤشر.

ثالثاً: العاملان * و & :

لقد بينا عمل العامل * و لكننا لم نتطرق إلى العامل &, العامل & يفيد إيجاد عنوان متغير, فلو كان عندنا متغير من نوع int مثلاً هكذا:

int a;

و أسندنا له القيمة 10 مثلاً:

a = 10;

و أردنا أن نعرف عنوان هذا المتغير و طبعاته سيكون الامر كالتالي:

printf("%dnn", &a);

في هذه الجملة سوف يطبع البرنامج عنوان المتغير a .

و لنتعرف على قاعدة مهمه جداً و هي أنه إذا عرفنا مؤشر px من نوع int هكذا:

int *px;

و اردنا أن نعرف عنوان المؤشر كيف ؟

نحن قلنا نعرف عنوان المتغير العادي بالعلامة & و بالتالي نفس الشيئ سيكون للمؤشرات و ستكتب هكذا:

&*px;

هذه الجملة تعطينا عنوان المؤشر px و لكن أليس هذا طويلاً ؟ و مقعد ؟

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

px;

أي أن علامة & تلغي علامة *.

رابعاً: إسناد المؤشرات:

في إسناد المؤشرات لن يخرج الإسناد عن حالتين ثنتين هما:

1.إسناد مؤشر لمتغير أو العكس.

2.إسناد مؤشر لمؤشر.

سنتعرف الآن على كيفية إسناد متغير لمؤشر:

لو كان لدينا التعريف التالي:

int *px;
int a = 3;

هنا عرفنا px من نوع مؤشر إلى int و عرفنا أيضاً a متغير من نوع int و أسندنا لـ a القيمة 3.

السؤال هنا: كيف سنجعل px يؤشر على a ?

الجواب بسيط جداً ولكن أكثرنا سيظن أن الجواب هو:

*px = a;

و لكن لتجربو هذه الطريقة ستجون أنها خاطئة لماذا ؟!؟

نحن عرفنا المؤشر أنه مؤشر إلى عنوان في الذاكرة, إذا px لابد أن يحوي عنوان في الذاكرة الرئيسية وسوف نحجز لها هذا العنوان عن طريق الدالة :

malloc و بالنسبة لمستخدمي السي++ سوف يستخدمون المعامل new .

إذا سوف نضيف هذا السطر أولاً:

px = (int *)malloc(sizeof(int));

وهذه الدالة موجوده في المكتبة stdlib.h .

و بالنسبة لمستخدمي السي++:

px = new int;

و الان قد حجزنا للمتغير px مكان في الذاكرة مخصص لها و الان فقط نستطيع أن نقول:

*px = a;

أو:

*px = 10;

و من الممكن أن أجعل المؤشر px يؤشر على المتغير a كالتالي:

px = &a;

هنا لن نحتاج للدالة malloc أو المعامل new لماذا ؟

لأننا في الإسناد السابق قمنا بإسناد عنوان المتغير a إلى المؤشر px أي بعبارة أوضح:

" جعلنا px يؤشر على a ". و هنا عند أي تغيير يحدث في px سوف يحدث في عنوان px الذي هو عنوان a أي سيتغير a لنأخذ هذا البرنامج كدليل على هذا الكلام:

#include "stdio.h"

int main ()
{
int *px;
int a;

01: px = &a; /* 'px' will point on 'a' */

02: *px = 10; /* Changes on 'px' will effect on 'a' */

03: printf("px = %d nn", *px);
04: printf("a = %d nn", a); /* 'a' and 'px' will have the same value that is 10 */

05: a = 20; /* Changes on 'a' will effect on 'px' */

06: printf("px = %d nn", *px);
07: printf("a = %d nn", a); /* 'a' and 'px' will have the same value that is 20 */

return 0;
}

لاحظ عزيزي أنه في السطر 1 جعلنا px يؤشر على a أي أن px سيحتوي على عنوان a .

في السطر 2 جلعا غيرنا px و جعلناها تساوي 10 و لكن في الحقيقة نحن لم نغير px و لكننا قمنا بتغيير العنوان الذي يوجد بداخل px و المعنى الحرفي لهذا السطر هو:

" إذهب إلى العنوان الموجود داخل px و إجعل هذا العنوان يحتوي على القيمة 10 " و نحن نعلم أن هذا العنوان هو عنوان a ( من السطر 1 ) إذا سوف يغيير هذا السطر المتغير a و سنلاحظ هذا التغيير في السطرين 3 و 4 في السطر 3 قلنا للدالة printf إطبعي محتوى العنوان الموجود داخل px و في السطر 4 قلنا لها إطبعي قيمة المتغير a و سنلاحظ أن هذين القيمتين هما 10 إذاً سيطبع على الشاشة القيمة 10.

أما في السطر 5 أسندنا للمتغير a القيمة 20.

و في السطر 6 قلنا للدالة printf إطبعي القيمة الموجوده في العنوان الموجود داخل px و نحن نعرف أن العنوان الموجود داخل px هو عنوان a ( من السطر 1 ), ثم في السطر 7 طبعنا قيمة a و سيبطع على الشاشة القيمة 20 مرتين, مره من المؤشر px و مرة أخرى من المتغير a .

طبعاً هذا المثال سهل و لكن الذي يفهم هذا المثال سيفهم %90 من موضوع المؤشرات, و هذا الموضوع ليس صعب كما يعتقده الكثيرون :).

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

ففرضاً لو كان لدينا التعريق التالي:

int *p1, *p2;

int a = 5;

و جعلنا p1 يؤشر على a بكتابة هذا السطر:

p1 = &a;

( أي جعلنا p1 يحوي عنوان a )

الآن نريد أن نجعل p2 يؤشر على p1 كيف سيكون ذلك ؟!؟

الحل ببساطة نجعل في p2 عنوان p1 الذي هو في الأصل عنوان a, إذاً يكون السطر الذي يجعل p2 يؤشر على P1 هو:

p2 = p1;

أي عنوان p2 يساوي عنوان p1 .

الآن لو طبعاً قيم:

*p1, *p2, a

ستحتوي جميع هذه القيم على القيمة 5 الموجوده في a.

و لو غيرنا قيمة واحده منها فقط سيتغير الكل تبعاً لذلك, لأن p1 يؤشر على a و p2 يؤشر على p1 و p1 يؤشر على a إذاً منطقياً أن سؤشر p2 إلى a و الفي الحقيقة أي تغيير في p1, p2 سيكون التغيير في a لأنهما لا يحملان في الأصل إلى عنوان المنتغير a.

خامساً: التعابير الرياضية على المؤشرات:

لقد تطرقنا فيما سبق إلى العامل الرياضي = (الإسناد) و هو شبية جداً مع إسناد المتغيرات.

و لكن في هذا الجزء سوف نتطرق إلى العوامل الرياضيه مثل:

++, –, +, -,,,,

و قبل ان نسترسل في هذا الجزء لنلقي أولاً نظره على جدول الأولوية في التنفيذ في جمل لغة سي و سي++:

Highest () [] -> .
! ~ ++ — – (type) * & sizeof
* / %
+ –
<< >>
< <= > >=
== !=
&
^
|
&&
||
?:
= += -= *= /= etc
Lowest ,

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

فلو كان لدينا هذا التعريف:

int x, y, *px;

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

x = 3;

px = &x;

y = *px + 1;

لو لاحظنا الجملة الأخيره سنلاحظ أنه لو إستبدلناها بالجملة:

y = x + 1;

ستكون مكافئة لها تماماً لماذا ؟

لأنه كما قلنا مراراً و تكراراً أن px يؤشر إلى x فلو كتبنا px* أو x في مكان سيكون لهما نفس التأثير.

و لو قلنا:

printf("%d", *px);

فهذا السطر سيطبع لنا قيمة x لأن px تشير إلى x .

ولو قلنا :

*px = 8;

سيكون هذا الشرط مكافئ لهذا:

x = 8;

لأن px يشير إلى x.

و لكن ماذا لو أردنا أن نزيد المؤشر px بواحد بواسطة العامل (++) أو إنقاصة بواسطة العامل (–) ؟

الجواب المتوقع هو:

*px++;

و لكن هذا خطأ. لماذا ؟

أولاً بالرجوع إلى جدول الأولويات ستجد أن علامة (++ و * (للمؤشر)) لهما نفس الأولويه و لكن عندما تتساوى الأولويات فالكمبايلر ينفذ من اليمين لليسار أي سيزيد واحد على عنوان المؤشر أي سيؤشر على الخانه التاليه للعنوان القديم.

و لكن كيف ممكن أن نتغلب على مشكلة الأولوية ؟!

الحل هو أن تستخدم عامل له اولوية أكبر من جدول العوامل أعلاه ألا وهو الأقواس الدائريه ( و ) عندها نقول:

(*px)++;

في هذه الحالة سيزيد واحد على المتغير الذي يؤشر عليه px وهو x, و نفس الشيئ ينطبق على (–).

و لكن لو قلت:

++*px;

في هذه الحالة سيزيد واحد على المتغير الذي يؤشر عليه px وهو x, و نفس الشيئ ينطبق على (–). بدون الحاجة إلى الأقواس لأنه سينفذ الجملة من اليمين لليسار أي سيؤشر أولاً ثم سيزيد.

ملاحظة:

لنرى هذا البرنامج:

#include "stdio.h"

int main ()
{
int *ptr, i = 10;

ptr = &i; /* ptr Will point to (i) */

printf("ptr Points to: %un", ptr);

ptr++;

printf("ptr Points to: %un", ptr);

return 0;
}

نفذ البرنامج الآن و لترى على أي عنوان يؤشر ptr بالنسبة لي ظهر:

ptr Points to: 1245048
ptr Points to: 1245052
Press any key to continue

و لكن نحن زدنا العنوان واحد فقط و لكن لماذا ؟

السبب في ذلك أن حجم size للـ int في جهازي هو 4 كيلو بايت لذلك زاد المشر من عنده 4.

أما لو كان الـ int في جهازك بحجم 2 كيلو بايت فسيزيد العنوان بمقدار 2.

و تستطيع ان تعرف حجم أي منغير بواسطة الـدالة sizeof .

سادساً: المؤشرات و المصفوفات:

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

r = x[1]; تساوي r = *(x+1);

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

ptr = x;

أو:

ptr = &x[0];

هاتين الطريقتين لهما نفس الفاعليه و لكن الفرق هو انه في الأول عاملنا المصفوفه x كمؤشر إلى أول عنصر و لكن في الطريقة الثانية عاملنا المصفوفه x كمصفوفه أي أن [x[0 هو متغير عادي مثل أي متغير.

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

#include "stdio.h"

int main ()
{
int x[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i;

printf("Arrays As Pointers:n");
for( i = 0 ; i < 10 ; i++) { printf("*(x + %d) = %dn", i, *(x + i)); } printf("nnArrays As Arrays:n"); for( i = 0 ; i < 10 ; i++) { printf("x[%d] = %dn", i, x[i]); } printf("nnn"); return 0; } في هذا المثال يتبين لنا كيف إستخدمنا المصفوفات كمؤشرات تاره و كمصفوفات تارة أخرى و أن لهما نفس النتائج. سابعاً السلاسل الحرفيه: لقد تحدثت عن هذا الموضوع في درس مستقل راجع الدروس في قسم البرمجة في قسم السي بإسم: دوال التعامل مع الحروف و السلاسل الحرفية ثامناً:المؤشرات ضمن المتغيرات المرسلة للدالة: من المؤسف أن الدوال في لغة السي و كل لغات البرمجة التي أعرفها لا تستطيع إلا إرجاع متغير واحد فقط !! سواء كان هذا المتغير من الانواع القياسيه كـ int, float, char ... أو من نوع سجلات كما أخذنا في درس السجلات مع السي Structures in C إذا السؤال اللذي يطرح نفسه هو كيف أستطيع أن أرجع أكثر من قيمه من الدالة ؟! الطريقة سهله جداً و هي عن طريق ما يسمى بـ Call By Reference أي المناداة بالمرجع. و سنأخذ هذا المثال و بالتالي سنشرح عليه هذه العمليه: فلو كان أردنا أن نكتب داله نرسل لها رقمين num1 و num2 و تعيد لنا حاصل الجمع و الضرب و القسمه أي sum و pro و div عندئذ لابد من إستخدام ما يسمى بـ Call By Reference و سيكون رأس الدالة هكذا: void function1( int num1, int num2, int *sum, int *pro, float *div); و عندما نريد أن نرسل للدالة المتغيرات سيكون هكذا: function( n1, n2, &s, &p, &d); السؤال لماذا إستبدلنا كل * في تعريف الدالة بـ& في الإرسال للدالة ؟!!! من المعروف أنه عند إرسال متغيرات للداله سوف يقوم الكمبايلر بالآتي على مثالنا السابق: سوف يساوي num1 بـ n1 و num2 بـ n2 و s بـ sum و هكذا و لكن في تعريف الدالة sum و pro و div عبارة عن مؤشرات إذا عن الإسناد إلى مؤشر كما تعلمنا أعلاه سوف نسند له العنوان فقط و ليس المتغير هنا عرفنا لمذا قلنا في الإرسال & و ليس * . و لكن ربما سئل سائل كيف سنرجع القيمه ؟ أقول له بأن sum مؤشر و إذا أرسلنا له عنوان s فقط فسيكون sum و s شيئ واحد أي يشيران إلى نفس المكان لذلك عند الخروج من الدالة سيتكون القيمة متغيره و ليست كما كانت عليه قبل الارسال. لنكتب هذه الدالة كامله الآن: void function1( int num1, int num2, int *sum, int *pro, float *div) { *sum = num1 + num2; *pro = num1 * num2; if( num2 == 0 ) *div = num1 / num2; else *div = 0; } فلو كتبنا هذا البرنامج الكامل: #include "stdio.h" void function1( int num1, int num2, int *sum, int *pro, float *div) { *sum = num1 + num2; *pro = num1 * num2; if( num2 == 0 ) *div = num1 / num2; else *div = 0; } int main() { int n1, n2, s, p; float d; n1 = 12; n2 = 2; function(n1, n2, &s, &p, &d); printf("Sum = %dn", s); printf("Pro = %dn", p); printf("Div = %.2fn", d); return 0; } الآن لننفذ البرنامج !!! سنجد أن المخرجات هي: Sum = 14 Pro = 24 Div = 6.00 إذا لو تأملنا المخرجات لوجدنا أن قيمة s و p و d قد أعدناها من الداله بدون الأمر return . تاسعاً: مصفوفة المؤشرات: المصفوفات من الممكن أن تكون مؤشر إلى نوع ما بحيث كل عنصر من المصفوفة يصبح مؤشر إلى نوع معين. من أكثر الأمثله لهذا التركيب هو مصوفة مؤشرات إلى char أي كل عنصر عبارة عن string أي كالتالي: char *names[4]; أي أن المصوفه name تحتوي على أربع عناصر كل منها هو string . لنأخذ مثال بارسم على ذلك: لنفرض انه لدينا هذا التعريف: char *name[4] = { "Talal", "Abdullah", "Thamer", "Mohammad" }; name[0] ===> "Talal"

name[1] ===> "Abdullah"

name[2] ===> "Thamer"

name[3] ===> "Mohammad"

سيكون تمثيل العناصر كما هو مبين أعلاه.

و لكن ربما قال أحدهم لماذا لا أستخدم مصفوفة ذات بعدين للآتي ؟!!

سنرد عليه بسهوله و نقول:

أن المصفوفة ذات البعدين لها حجم ثابت لا نستطيع أن نتعداه مهما كبر الإسم, و لكن الامر مختلف مع مصفوفة المؤشرات لأنه كما نلاحظ أن Talal و Abdullah و Thamer و Mohammad مختلفت الأطول لذلك نجد أن المؤشرات تعطي مرونه و قوة أكثر من المصفوفات.

أفاتار (الصورة التعريفية)
لحظـ غروب ـــة
24 Posts
(Offline)
3
الأربعاء 22 شوال 1429صباحًا01 22-10-2008صباحًاالأربعاء -
Print

(الله يسعد أيامك ويحقق أمانيك ولايجعلك تفقد غالي ) يــــــــــــارب…
مشكور أخوي سالم …
والله يستر من ++C شكلها ماتطمن ….!

Forum Timezone: America/New_York
All RSSShow Stats
Administrators: إبحار
Top Posters:
jana: 231
Hassanhegazy: 203
Ahmed Samy: 114
fatinn: 36
marwa: 35
nagham_n: 28
no way: 27
Awrad: 24
MONA AND MONA: 17
zarkaa: 16
Newest Members:
Forum Stats:
Groups: 1
Forums: 9
Topics: 2581
Posts: 16488

 

Member Stats:
Guest Posters: 0
Members: 6557
Moderators: 0
Admins: 1

Most Users Ever Online
415
Currently Online
Guest(s)
12
Currently Browsing this Page

1 Guest(s)