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("%d\n\n", &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 \n\n", *px);
04: printf("a = %d \n\n", 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 \n\n", *px);
07: printf("a = %d \n\n", 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