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

العوامل المنطقية في لغة سي C وعوامل أخرى


Naser Dakhel

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

العوامل المنطقية

وضحنا سابقًا كيف أن لغة سي C لا تميّز بين القيم "المنطقية" والقيم الأخرى، إذ تعطي المُعاملات العلاقيّة relational operators نتيجة متمثلةً بالقيمة 0 للخطأ، أو 1 للصواب. وتُقيّم قيمة تعبير منطقي لتحديد ما إذا كانت تعليمة تحكم بتدفق البرنامج ستنفّذ أم لا، إذ تعني القيمة 0 "لا تنفِّذ" وأي قيمة أخرى تعني "نفِّذ". تُعد جميع الأمثلة التالية تعابير منطقية صالحة:

while (a<b)...
while (a)....
if ( (c=getchar()) != EOF )...

لن يتفاجأ أي مبرمج لغة سي متمرس بأي تعبير من التعابير السابقة، إذ يمثّل التعبير الثاني (while (a اختصارًا شائعًا للتعبير (while (a != 0 (على الأغلب استنتجت معناه ضمنيًّا).

نحتاج الآن إلى طريقةٍ تمكِّننا من كتابة تعابير أكثر تعقيدًا باستخدام هذه القيم المنطقية "خطأ وصواب"، إذ استخدمنا حتى الآن الطريقة التالية لكتابة التعبير الذي يعطينا (if(a<b AND c<d، إلا أن هناك طريقةٌ أخرى لكتابة التعليمة.

if (a < b){
      if (c < d)...
}

هناك ثلاثة عوامل مشاركة في هذا النوع من العمليات، هي: العامل المنطقي AND أو "&&" والعامل المنطقي OR أو "||" والعامل NOT أو "!"، إذ يُعد العامل الأخير عاملًا أحاديًا، أما العاملان الآخران فهما ثنائيان. تستخدِم هذه العوامل تعابيرًا مثل مُعاملات وتعطي نتيجةً مساويةً إلى "1" أو "0"؛ إذ يعطي العامل "&&" القيمة "1" فقط في حال كانت قيمة المُعاملان غير صفرية؛ ويعطي "||" القيمة "0" فقط في حال كانت قيمة المُعاملان صفر؛ بينما يعطي "!" القيمة صفر إذا كانت قيمة المُعامل غير صفرية وبالعكس، فالأمر بسيطٌ للغاية، وتكون نتيجة العوامل الثلاث من النوع "int".

لا تخلط عوامل العمليات الثنائية bitwise operators "|" و"&" مع عواملها المنطقية المشابهة، فهي مختلفةٌ عن بعضها البعض؛ إذ أن للعوامل المنطقية ميزةٌ لا نجدها في العوامل الأخرى، ألا وهي تأثيرها على عملية تقييم التعبير، إذ تُقيّم من اليسار إلى اليمين بعد أخذ الأسبقية في الحسبان، ويتوقف أي تعبير منطقي عن عملية التقييم عند التوصُّل إلى قيمة التعبير النهائية. على سبيل المثال، تتوقف سلسلة من عوامل "||" عن التقييم حالما تجد مُعاملًا ذا قيمةٍ غير صفرية. يوضِّح المثالين التاليين تعبيرين منطقيين يمنعان القسمة على صفر.

if (a!=0 && b/a > 5)...
/* alternative */
if (a && b/a > 5)

ستُقيَّم قيمة b/a في كلا الحالتين فقط في حالة كون قيمة a غير صفرية؛ وفي حال كانت a مساويةً الصفر، ستُقيّم قيمة التعبير فورًا، وبذلك ستتوقف عملية تقييم التعبير وفق قوانين لغة سي C للعوامل المنطقية.

عامل النفي الأحادي NOT بسيط، لكن استخدامه غير شائع جدًا نظرًا لإمكانية كتابة معظم التعبيرات دون استخدامه، كما توضح الأمثلة التالية:

 if (!a)...
/* alternative */
if (a==0)...

if(!(a>b))
/* alternative */
if(a <= b)

if (!(a>b && c<d))...
/* alternative */
if (a<=b || c>=d)...

توضح الأمثلة السابقة مع بدائلها طريقةً لتجنُّب أو على الأقل الاستغناء عن استخدام العامل !. في الحقيقة، يُستخدم هذا العامل بهدف تسهيل قراءة التعبير، فإذا كانت المشكلة التي تحلّها تستخدم علاقة منطقية مثل العلاقة "b*b-4*a*c) > 0)" الموجودة في حل المعادلات التربيعية، فمن الأفضل كتابتها بالطريقة:

( !((b*b-4*a*c) > 0))

بدلًا من كتابتها بالطريقة:

if( (b*b-4*a*c) <= 0)

لكن التعبيران يؤديان الغرض ذاته، فاختر الطريقة التي تناسبك.

تعمل معظم التعابير التي تستخدم العوامل المنطقية وفق قوانين الأسبقية الاعتيادية، لكن الأمر لا يخلو من بعض المفاجآت، فإذا أخذت نظرةً أخرى على جدول الأسبقية، فستجد أن هناك بعض العوامل في مستوى أسبقية أقل موازنةً بالعوامل المنطقية، ويسبب ذلك خطأ شائعًا:

if(a&b == c){...

إذ تُوازَن b بالمساواة مع c أولًا في هذه الحالة، ومن ثم تُضاف القيمة إلى a سواءٌ كانت 0 أو 1، مما يتسبب بسلوك غير متوقع للبرنامج بسبب هذا الخطأ.

عوامل غريبة

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

عامل الشرط :?

كما هو الحال في عزف آلة الأكورديون، سيكون من الأسهل النظر إلى كيفية عمل هذا العامل بدلًا من وصفه.

expression1?expression2:expression3

إذا كان التعبير expression1 صحيحًا، فهذا يعني أن قيمة التعبير بالكامل هي من قيمة التعبير expression2، وإلا فهي قيمة التعبير expression3، إذ ستُقيَّم قيمة واحدٍ من التعبيرين حسب قيمة التعبير expression1.

تُعد أنواع البيانات المختلفة المسموح استخدامها في التعبير expression2 و expression3 والأنواع الناتجة عن التعبير بالكامل معقدة، والسبب في هذا التعقيد هو بعض الأنواع والمفاهيم التي لم نشرحها بعد. سنشرح هذه النقاط في الأسفل، لكن كن صبورًا بخصوص بعض المفاهيم التي سنشرحها لاحقًا.

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

a>b?1:3.5

يحتوي المثال الثابت 1 من النوع int والثابت 3.5 من النوع double، وبتطبيق قوانين التحويل الحسابية نحصل على نتيجة النوع double.

هناك بعض الحالات المسموحة أيضًا، هي:

  • إذا كان المُعاملان من نوع ذا هيكلية structure أو اتحاد union متوافق، فالنتيجة هي من هذا النوع.
  • إذا كان المُعاملان من نوع "void"، فالنتيجة هي من هذا النوع.

ويمكن مزج عدة أنواع مؤشرات على النحو التالي:

  • يمكن للمُعاملين أن يكونا من أنواع مؤشرات متوافقة (من المحتمل أن تكون مُؤهلة qualified).
  • يمكن لمُعامل أن يكون مؤشرًا والمُعامل الآخر مؤشر ثابت فارغ Null pointer constant.
  • يمكن لمُعامل أن يكون مؤشرًا لكائن pointer to an object أو نوع غير مكتمل incomplete type والمُعامل الآخر مؤشر إلى نوع "void" (من المحتمل أن يكون مُؤهل).

لمعرفة النوع الناتج عن التعبير عند استخدام المؤشرات نتبع الخطوتين:

  1. إذا كان أيٌ من المُعاملَين مؤشرًا إلى نوع قيمة مؤهلة، فستكون النتيجة مؤشرًا إلى نوع مؤهل من جميع المؤهِّلات في كلا المُعاملين.
  2. إذا كان أحد المُعاملات مؤشر ثابت فارغ، فستكون النتيجة هي نوع المُعامل الآخر؛ فإذا كان أحد المُعاملات يؤشر إلى الفراغ، سيُحوَّل المُعامل الآخر إلى مؤشر إلى "void" وسيكون هذا نوع النتيجة؛ أما إذا كان كلا المُعاملات مؤشرات من أنواع متوافقة (بتجاهل أي مؤهلات) فالنوع الناتج هو من نوع مركب composite type.

سنناقش كلًا من المؤهّلات والأنواع المركبة والمتوافقة في المقالات القادمة.

نلاحظ استخدام هذا العامل في المثال البسيط أدناه، الذي يختار السلسلة النصية لطباعتها باستخدام الدالة printf:

#include <stdio.h>
#include <stdlib.h>

main(){
      int i;

      for(i=0; i <= 10; i++){
              printf((i&1) ? "odd\n" : "even\n");
      }
      exit(EXIT_SUCCESS);
}

[مثال 10.3]

يُعد هذا العامل جيدًا عندما تحتاجه، مع أن بعض الناس يشعرون بالغرابة عندما يرونه للمرة الأولى إلا أنهم سرعان ما يبدأون باستخدامه.

بعد الانتهاء من تقييم قيمة المُعامل الأول، هناك مرحلة نقاط التسلسل sequence points التي سنشرحها لاحقًا.

عامل الفاصلة

يربح هذا العامل جائزة "أكثر العوامل غرابةً"، إذ يسمح لك بإنشاء قائمة طويلة من التعابير المفصول بينها بالفاصلة:

expression-1,expression-2,expression-3,...,expression-n

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

#include <stdio.h>
#include <stdlib.h>

main(){
      int i, j;

      /* comma used - this loop has two counters */
      for(i=0, j=0; i <= 10; i++, j = i*i){
              printf("i %d j %d\n", i, j);
      }

      /*
       * In this futile example, all but the last
       * constant value is discarded.
       * Note use of parentheses to force a comma
       * expression in a function call.
       */
      printf("Overall: %d\n", ("abc", 1.2e6, 4*3+2));
      exit(EXIT_SUCCESS);
}

[مثال 11.3]

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

بعد تقييم كل مُعامل، تأتي مرحلة نقاط التسلسل sequence points التي سنشرحها لاحقًا.

ترجمة -وبتصرف- لقسم من الفصل Control of Flow and Logical Expressions من كتاب 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.


×
×
  • أضف...