اذهب إلى المحتوى

البنية النصية لبرامج سي C


Naser Dakhel

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

تخطيط البرنامج

اعتمدت أمثلتنا حتى اللحظة في تنسيقها على المسافات البادئة indentation والأسطر الجديدة newlines، وهذا الأسلوب في التنسيق شائع في لغات البرمجة التي تنتمي إلى عائلة لغة سي، إذ تُعد هذه اللغات لغات "حرة التنسيق free format" وتُستخدم هذه الحرية في كتابة وتنسيق السطور البرمجية بحيث يحسِّن قراءتها ويبرز تسلسل منطقها. تُستخدم محارف المسافات الفارغة space بما فيها مسافات الجدولة tab الأفقية لإنشاء المسافات البادئة في أي مكان دون أن تؤثّر على عمل البرنامج عدا ضمن المعرِّفات identifiers والكلمات المفتاحية. تعمل الأسطر الجديدة على نحوٍ مماثل لعمل المسافات الفارغة ومسافات الجدولة باستثناء أسطر أوامر المعالج المسبق، الذي يمتلك بنية سطريّة line-by-line.

هناك حلان يمكنك اللجوء إليهما في حال كان أحد السطور طويلًا جدًا وغير مريحًا للقراءة، إذ يمكنك استبدال محرف المسافة space بمحرف سطر جديد، بحيث يصبح لديك سطرين بدلًا من سطرٍ واحد. ألقِ نظرةً على المثال التالي للتوضيح:

/* a long line */
a = fred + bill * ((this / that) * sqrt(3.14159));
/* the same line */
a = fred + bill *
        ((this / that) *
        sqrt(3.14159));

لن تستطيع في بعض الحالات استبدال المحارف بالطريقة السابقة، وذلك بسبب اعتماد المعالج المسبق على "تعليمات" السطر الواحد، ولحلّ هذه المشكلة يمكننا استخدام السلسلة "n\" التي تعني الانتقال لسطرٍ جديد، إذ يصبح هذا المحرف غير مرئي لنظام تصريف لغة سي، ويمكن نتيجةً لذلك استخدام هذه السلسلة في أماكن لا نستطيع استخدام الفراغات فيها في الحالات الاعتيادية، أي ضمن المعرّفات مثلًا أو الكلمات المفتاحية أو السلاسل النصية أو غيرها، وتسبق مرحلة معالجة هذه المحارف مرحلة معالجة ثلاثيات المحارف Trigraphs فقط.

/*
 * Example of the use of line joining
 */
#define IMPORTANT_BUT_LONG_PREPROCESSOR_TEXT \
printf("this is effectively all ");\
printf("on a single line ");\
printf("because of line-joining\n");

ينبغي أن تُستخدم هذه الطريقة في تقسيم الأسطر (بعيدًا عن أسطر تحكم المعالج المُسبق) في حالة واحدة فقط، ألا وهي لمنع السلاسل النصية الطويلة من أن تختفي إلى اليمين عند النظر لسطور البرنامج. بما أن السطور الجديدة غير مسموحة داخل السلاسل النصية والمحارف الثابتة، قد تعتقد أن هذه الفكرة جيّدة:

/* not a good way of folding a string */
printf("This is a very very very\
long string\n");

سيعمل المثال السابق بالتأكيد، ولكن من المحبّذ استخدام ميزة ضمّ السلاسل النصية string-joining التي قُدّمَت ضمن المعيار عند التعامل مع السلاسل النصية:

/* This string joining will not work in Old C */
printf("This is a very very very"
       "long string\n");

يسمح لك المثال الثاني بإضافة الفراغات دون تغيير مضمون السلسلة النصية، إذ إنّ المثال الأول يضيف الفراغات إلى مضمون السلسلة النصية.

لكن هل انتبهت أن المثالين يحتويان على خطأ؟ لا يوجد هناك أي مسافة فارغة تسبق الكلمة "long"، مما يعني أن خرج البرنامج سيكون "verylong" دون مسافة بين الكلمتين.

التعليق

تكلمنا عن التعليق سابقًا وقلنا أن التعليق يبدأ بالمحرفَين "*/" وينتهي بالمحرفين "/*"، ويُترجم التعليق إلى مسافة فارغة واحدة أينما وجد وهو يتبِّع القوانين ذاتها الخاصة بالمسافة الفارغة. من المهم هنا معرفة أن هذا التعليق لا يختفي -كما كان الحال في لغة سي القديمة- ومن غير الممكن وضع التعليق بداخل سلسلة نصية أو محرف ثابت وإلا أصبح جزءًا منهما:

/*"This is comment"*/
"/*The quotes mean that this is a string*/"

لم تكن لغة C القديمة واضحةً بشأن تفاصيل حذف التعليق، إذ كان من الممكن أن يكون الناتج هنا:

int/**/egral();

هو حذف التعليق، أي أن المصرف سينظر للتعليمة السابقة بكونها استدعاءً لدالة اسمها integral، لكنه سيُبدَّل التعليق بالاعتماد على معيار سي بمسافة فارغة وستكون التعليمة السابقة مساويةً للتعليمة التالية:

int egral();

التي تصرح عن دالة باسم egral تُعيد قيمةً من نوع int.

مراحل الترجمة

تُجرى ترجمة المحارف المختلفة وضمّ الأسطر والتعرف على التعليقات ومراحل أخرى من الترجمة المبكّرة وفق ترتيبٍ معيّن، إذ يقول المعيار أن هذه المراحل تحدث بالترتيب التالي:

  1. ترجمة المحارف الثلاثية.
  2. ضمّ الأسطر.
  3. ترجمة التعليقات إلى مسافات فارغة (عدا التعليقات الموجودة ضمن السلاسل النصية والمحارف الثابتة)، إذ من الممكن في هذه المرحلة جمع عدّة مسافات فارغة إلى مسافة فارغة وحيدة.
  4. ترجمة البرنامج.

تُنهى كل مرحلة قبل أن تبدأ المرحلة التي تليها.

اقتباس

ملاحظة: هناك مزيدٌ من المراحل ولكننا ذكرنا أكثر المراحل أهمية

الكلمات المفتاحية والمعرفات

بعد أن تكلمنا عن أبجدية لغة سي، سنلقي نظرةً على مزيدٍ من عناصر اللغة المثيرة للاهتمام، إذ تُعد الكلمات المفتاحية keywords والمعرفات identifiers المكونات الأكثر وضوحًا، وعلى الرغم من تشابه تركيبها إلا أنها مختلفة.

الكلمات المفتاحية

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

struct int double auto
switch long else break
typedef register enum case
union return extern char
unsigned short float const
void signed for continue
volatile sizeof goto default
while static if do

[جدول 3.2. كلمات مفتاحية]

الكلمات المفتاحية المُضافة جديدًا التي قد تفاجئ المبرمجين السابقين للغة سي هي: const وsigned وvoid وvolatile (على الرغم من وجود void منذ فترة). سيلاحظ بعض القراء شديدي الانتباه أن الكلمات المفتاحية entry و asm و fortran غير موجودة في الجدول، إذ أنها ليست من ضمن المعيار، وسيفتقدها القليل منهم فقط.

المعرفات

يُعد المعرف Identifier مرادفًا لما نطلق عليه "اسم name"، وتُستخدم المعرفات في لغة سي للدلالة على العديد من الأشياء، فقد لاحظنا استخدامها حتى الآن في تسمية المتغيرات والدوال، ولكنها تُستخدم أيضًا في تسمية مزيدٍ من الأشياء التي لم نراها بعد، مثل:

  • العناوين labels
  • "وسوم" الهياكل tags of structures
  • الاتحادات unions
  • المعدّدات enums

قواعد إنشاء معرف بسيطةً جدًا، إذ يمكنك استخدام الأحرف البالغ عددها 52 حرفًا من الأبجدية الإنجليزية (أحرف كبيرة أو صغيرة)، والأرقام العشرة من 0 حتى 9 وأخيرًا الشرطة السفلية "_"، التي يمكن عدّها حرفًا من الأبجدية في حالتنا هذه، لكن هناك قيدٌ واحدٌ ألا وهو أن المعرّف يجب أن يبدأ بحرف أبجدي.

على الرغم من أن المعيار لا ينص صراحةً على حد أقصى لطول اسم المعرّف، إلا أننا نحتاج أن نتكلم عن هذه النقطة، إذ لا يوجد هناك أي حد في لغة سي القديمة ومعيار سي لطول اسم المعرف، ولكن المشكلة هي أنه لا يوجد هناك أي ضمانات أن جميع محارف اسم المعرف تُفحص أثناء موازنة المساواة، فقد كان حد الموازنة في لغة سي القديمة 8 محارف، أما في المعيار فهو 31 محرف.

وبذلك يكون عمليًّا الحد الجديد للمعرف في المعيار هو 31 محرف، ومع ذلك يمكن للمعرفات تجاوز هذا الطول ولكنها يجب أن تختلف في أول 31 محرفًا إذا كنت تريد التأكُّد من أن برامجك محمولة portable. يسمح المعيار بالأسماء الطويلة لبعض التطبيقات، لذا إذا استخدمت الأسماء الطويلة وكان لا بدّ منها، فتأكد من أنها فريدةً قبل أن يتوقف التحقُّق من اسمها في المحرف ذو الرقم 31.

يُعد طول المعرفات الخارجية external identifiers واحدًا من أكثر الأشياء المثيرة للجدل في الإصدار الجديد؛ إذ تُعرف المعرفات الخارجية بأنها المعرفات التي يجب أن تكون مرئية خارج نطاق الشيفرة المصدرية المستخدمة فيها، وتُعد برامج المكتبة أو الدوال التي يجب أن تُستدعى من عدّة ملفات مصدرية مثالًا جيدًا عليها.

اختار المعيار الحفاظ على القيود القديمة التي تخص هذه المعرفات، إذ لا تعدّ المعرفات الخارجية مختلفةً عن بعضها إلا في حالة اختلافها مع بعضها في المحارف الستّ الأولى، وليزداد الأمر سوءًا فقد تُعامل الأحرف الكبيرة والصغيرة بنفس الطريقة؛ والسبب وراء هذا عملي، إذ أن معظم أنظمة تصريف لغة سي تعمل بمساعدة أدوات نظام معينة للربط بين دوال المكتبات والبرنامج المكتوب بلغة سي C، وهذه الأدوات هي خارج تحكم مصرّف لغة C، ولذا على المعيار أن يضع بعض الحدود العملية التي ستتوافق مع شروط هذه الأدوات.

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

يذكرنا المعيار دائمًا بأنه ينظر إلى استخدام تكافؤ الحالة بين الأحرف الصغيرة والكبيرة إضافةً لحدّ المحارف في تسمية المعرفات على كونها ميزاتٍ قديمة، ومن الممكن أن يلغي المعيار القادم استخدام هذه القيود. لنأمل أن يحصل ذلك قريبًا.

التصريح عن المتغيرات

ذكرنا في المقالات السابقة أنه يجب التصريح عن أسماء الأشياء قبل استخدامها، والاستثناء الوحيد هنا لهذه القاعدة هو أسماء الدوال التي تُعيد قيمةً من النوع int، لأنه مصرّحٌ عنها افتراضيًا بالإضافة لأسماء العناوين labels. بإمكانك إما التصريح declaration عن الأشياء، وهي العملية التي تصف اسم ونوع الشيء ولكنها لا تحجز أيّ مكان على الذاكرة، أو التعريف definition، الذي يحجز مكانًا في الذاكرة للشيء المُصرَّح عنه.

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

القواعد المتعلقة بجعل التصريح ضمن التعريف معقّدةٌ بعض الشيء، لذا سنؤجلها ونكتفي حاليًّا ببعض الأمثلة والقواعد التي ستؤدي الغرض لأمثلتنا القادمة.

/*
* A function is only defined if its body is given
* so this is a declaration but not a definition
*/

int func_dec(void);

/*
* Because this function has a body, it is also
* a definition.
* Any variables declared inside will be definitions,
* unless the keyword 'extern' is used.
* Don't use 'extern' until you understand it!
*/

int def_func(void){
     float f_var;            /* a definition */
     int counter;            /* another definition */
     int rand_num(void);     /* declare (but not define) another function */

     return(0);
}

سنستمرّ قدُمًا في القسم التالي ونتكلم عن نوع المتغيرات والتعابير.

ترجمة -وبتصرف- لقسم من الفصل Variables and Arithmetic من كتاب The C Book.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...