المؤشرات في لغة C: ما هو المؤشر في برمجة C؟ أنواع

⚡ ملخص ذكي

المؤشرات في لغة C هي متغيرات تخزن عنوان الذاكرة لمتغير آخر. وهي تُمكّن من تخصيص الذاكرة الديناميكي، والتعامل الفعال مع المصفوفات والسلاسل النصية، وهياكل البيانات المعقدة، باستخدام عوامل العنوان وفك المرجع لقراءة القيم أو تعديلها.

  • 📌 فريف: يحتوي المؤشر على عنوان الذاكرة لمتغير آخر.
  • 🔧 Operaتورس: يُعيد المعامل & عنوانًا؛ بينما يقوم المعامل * بإلغاء مرجعيته.
  • 🧷 إعلان: اكتب data_type *name، ثم قم بتهيئته بعنوان متغير.
  • 🏷️ الأنواع: تتصرف المؤشرات الفارغة والباطلة والجامحة بشكل مختلف وتحتاج إلى عناية خاصة.
  • علم الحساب: تؤدي زيادة قيمة المؤشر إلى تحريكه بمقدار حجم نوعه الأساسي.
  • 🤖 مساعدة الذكاء الاصطناعي: تقوم أدوات الذكاء الاصطناعي بتصحيح أخطاء التجزئة وتحديد المؤشرات المعلقة أثناء المراجعة.

المؤشرات في لغة C

ما هو المؤشر في لغة C؟

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

كيفية استخدام المؤشرات في C

إذا قمنا بتعريف متغير v من النوع int، فإن v يخزن قيمة. لكل متغير، بالإضافة إلى قيمته، عنوان، أي موقعه في الذاكرة. يمكن استرجاع العنوان بوضع علامة العطف (&) قبل اسم المتغير. عند طباعة عنوان المتغير، يظهر كرقم عشوائي قد يختلف من تشغيل لآخر.

بدلاً من تخزين قيمة، يخزن المؤشر عنوان متغير. على سبيل المثال:

int *y = &v;
متغير POINTER
A قيمنا المخزنة في عين عنوان التخزين/الذاكرة. A متغير أن نقاط ل عنوان التخزين/الذاكرة الخاص بـ طرق المتغير.

إعلان المؤشر

مثل المتغيرات، المؤشرات في البرمجة C يجب تعريف المؤشرات قبل استخدامها. يكون تعريف المؤشر بالشكل التالي:

data_type *pointer_variable_name;

هنا، نوع البيانات يمثل النوع الأساسي للمؤشر، ويشير إلى نوع المتغير الذي يشير إليه المؤشر. أما علامة النجمة (*)، وهي نفس الرمز المستخدم للضرب، فهي عامل التوجيه غير المباشر الذي يُعلن عن المؤشر. ومن أمثلة تعريفات المؤشرات الصحيحة ما يلي:

int    *ptr_thing;       /* pointer to an integer */
int    *ptr1, thing;     /* ptr1 points to an int; thing is an int */
double *ptr2;           /* pointer to a double */
float  *ptr3;           /* pointer to a float */
char   *ch1;            /* pointer to a character */

تهيئة مؤشر

بعد تعريف المؤشر، نقوم بتهيئته بعنوان متغير. إذا تم استخدام مؤشر غير مهيأ، فقد تكون النتائج غير متوقعة وربما كارثية. للحصول على عنوان متغير، استخدم عامل العطف (&) قبل اسم المتغير.

pointer = &variable;

فيما يلي برنامج بسيط يوضح مفهوم المؤشر:

#include <stdio.h>
int main()
{
   int a = 10;     // variable declaration
   int *p;         // pointer variable declaration
   p = &a;        // store address of a in pointer p
   printf("Address stored in p is: %x\n", p);  // access the address
   printf("Value stored in p is: %d\n", *p);    // access the value
   return 0;
}

الإخراج:

Address stored in p is: 60ff08
Value stored in p is: 10
المُشغل معنى
* يخدم غرضين: تعريف مؤشر، وإرجاع قيمة المتغير المشار إليه (فك المرجع).
& يخدم غرضاً واحداً: إرجاع عنوان متغير.

أنواع المؤشرات في C

مؤشر فارغ

نقوم بإنشاء مؤشر فارغ عن طريق إسناد القيمة NULL إليه أثناء تعريفه. يُفيد هذا عندما لا يكون قد تم إسناد عنوان للمؤشر بعد. يحتوي المؤشر الفارغ دائمًا على القيمة 0.

#include <stdio.h>
int main()
{
   int *p = NULL;     // null pointer
   printf("The value inside variable p is: %x", p);
   return 0;
}

مؤشر الفراغ

المؤشر الفارغ، أو المؤشر العام، لا يمتلك أي نوع بيانات قياسي. يتم إنشاؤه باستخدام الكلمة المفتاحية void ويمكنه تخزين عنوان أي متغير.

#include <stdio.h>
int main()
{
   void *p = NULL;    // void pointer
   printf("The size of pointer is: %d\n", sizeof(p));
   return 0;
}

كلب الصيد البري

يُطلق على المؤشر اسم "مؤشر جامح" إذا لم تتم تهيئته بأي قيمة. هذه المؤشرات غير فعّالة لأنها قد تشير إلى موقع ذاكرة غير معروف، مما قد يُسبب مشاكل ويؤدي إلى تعطل البرنامج. لذا، توخّ الحذر دائمًا عند استخدام المؤشرات الجامحة.

#include <stdio.h>
int main()
{
   int *p;          // wild pointer
   printf("%d", *p);
   return 0;
}

تشمل أنواع المؤشرات الأخرى في لغة C المؤشر المعلق، والمؤشر المعقد، والمؤشر القريب، والمؤشر البعيد، والمؤشر الضخم.

مؤشرات الوصول المباشر وغير المباشر

في لغة C، توجد طريقتان متكافئتان للوصول إلى محتوى المتغير ومعالجته. يستخدم الوصول المباشر اسم المتغير مباشرةً، بينما يستخدم الوصول غير المباشر مؤشرًا إلى المتغير:

#include <stdio.h>
int var = 1;        /* declare and initialize an int variable */
int *ptr;           /* declare a pointer to int */
int main(void)
{
   ptr = &var;       /* point ptr to var */
   printf("\nDirect access, var = %d", var);
   printf("\nIndirect access, var = %d", *ptr);
   *ptr = 48;        /* change var through the pointer */
   printf("\nIndirect access, var = %d", *ptr);
   return 0;
}

المؤشر الحسابي في C

عند التعامل مع المؤشرات، يُرجى مراعاة قواعد الأولوية التالية. يتمتع العاملان * و & بنفس أولوية العوامل الأحادية (النفي !، والزيادة ++، والنقصان –). في التعبير نفسه، تُقيّم العوامل الأحادية *، و &، و !، و ++، و — من اليمين إلى اليسار. إذا كان المؤشر P يشير إلى متغير X، فيمكن استخدام *P أينما أمكن كتابة X. التعبيرات التالية متكافئة:

التعبير التعبير المعادل
ص = * ع + 1 ص = س + 1
*P = *P + 10 س = س + 10
*P += 2 X += 2
+***ص ++X
(*ع)++ X ++

في الحالة الأخيرة، الأقواس ضرورية: لأن المعاملات الأحادية * و++ تُقيّم من اليمين إلى اليسار، فبدون أقواس سيتم زيادة قيمة المؤشر P، وليس الكائن الذي يشير إليه. العمليات الحسابية المتاحة على المؤشرات هي الإسناد، والزيادة، والنقصان، والجمع، والطرح.tracتحديد إزاحة عددية صحيحة، والتي تحرك المؤشر بمقدار هذا العدد من العناصر (كل خطوة تساوي حجم النوع الأساسي بالبايت).

المؤشرات والمصفوفات في لغة C مع أمثلة

تقليديًا، نصل إلى عناصر المصفوفة باستخدام فهرس، لكن المؤشرات تسهل ذلك أيضًا. يشير المؤشر المُعيّن لاسم المصفوفة إلى العنصر الأول:

#include <stdio.h>
int main()
{
   int a[5] = {1, 2, 3, 4, 5};   // array initialization
   int *p;                       // pointer declaration
   p = a;                       // p points to the first element; same as &a[0]
   for (int i = 0; i < 5; i++)
   {
      printf("%d\n", *p);            // print element
      p++;                      // move to the next element
   }
   return 0;
}

إضافة رقم إلى مؤشر تُحركه بمقدار ذلك الرقم إلى الأمام. فإذا كان المؤشر p يُشير إلى موقع الذاكرة 0، فإن إضافة 1 إلى p تجعله يُشير إلى موقع الذاكرة 1.

مؤشرات وسلاسل C مع أمثلة

السلسلة النصية هي عبارة عن مصفوفة من كائنات الأحرف تنتهي بحرف فارغ '\0'. يمكننا التعامل مع السلاسل النصية باستخدام المؤشرات.

#include <stdio.h>
#include <string.h>
int main()
{
   char str[] = "Hello Guru99!";
   char *p;
   p = str;
   printf("First character is: %c\n", *p);
   p = p + 1;
   printf("Next character is: %c\n", *p);
   p = str;                     // reset the pointer
   for (int i = 0; i < strlen(str); i++)
   {
      printf("%c\n", *p);
      p++;
   }
   return 0;
}

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

#include <stdio.h>
int main()
{
   char *materials[] = {"iron", "copper", "gold"};
   for (int i = 0; i < 3; i++)
      printf("%s\n", materials[i]);
   return 0;
}

مزايا المؤشرات في C

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

مساوئ المؤشرات في C

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

الأسئلة الشائعة

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

يؤدي الوصول إلى مؤشر فارغ (NULL) إلى حدوث خطأ في تجزئة الذاكرة وتعطل البرنامج أثناء التشغيل. لذا، تأكد دائمًا من أن المؤشر ليس فارغًا (NULL) قبل استخدام عامل التشغيل * للقراءة أو الكتابة.

يُخزّن المؤشر المزدوج، المُعرّف كـ int **pp، عنوان مؤشر آخر. ويُستخدم للمصفوفات ثنائية الأبعاد الديناميكية ولتعديل وسيط مؤشر داخل دالة.

يخزن المؤشر عنوان الذاكرة، وتكون العناوين بنفس العرض على منصة معينة، وعادة ما تكون 4 بايت على 32 بت و 8 بايت على 64 بت، بغض النظر عن النوع الذي يشير إليه.

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

تُعيد دوال مثل malloc و calloc مؤشرًا إلى الذاكرة المُخصصة في الكومة أثناء التشغيل. يمكنك الوصول إلى تلك الذاكرة من خلال المؤشر وتحريرها باستخدام free لتجنب تسريبات الذاكرة.

نعم. تقوم المساعدات المدعومة بالذكاء الاصطناعي بقراءة خطأ تجزئة الذاكرة والتعليمات البرمجية المحيطة به، ثم تشرح السبب المحتمل، مثل مؤشر غير مهيأ أو تم تحريره، وتقترح حلاً لمراجعته.

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

تلخيص هذه التدوينة بـ: