سؤال

ماذا يكون ال , عامل القيام به في C؟

هل كانت مفيدة؟

المحلول

التعبير:

(expression1,  expression2)

يتم تقييم التعبير الأول، ثم يتم تقييم التعبير 2، ويتم إرجاع قيمة التعبير 2 للتعبير بأكمله.

نصائح أخرى

لقد رأيت استخدامها أكثر في while الحلقات:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

سيتم إجراء العملية، ثم إجراء اختبار بناءً على الآثار الجانبية.والطريقة الأخرى هي أن تفعل ذلك مثل هذا:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}

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

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

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

من مشروع معيار C99 القواعد هي كما يلي:

expression:
  assignment-expression
  expression , assignment-expression

و الفقرة 2 يقول:

ال يتم تقييم المعامل الأيسر لمشغل الفاصلة كتعبير باطل؛ هناك نقطة تسلسل بعد تقييمها.ثم يتم تقييم المعامل الصحيح.فالنتيجة لها نوعها وقيمتها. 97) إذا جرت محاولة لتعديل نتيجة عامل الفاصلة أو الوصول إليها بعد نقطة التسلسل التالية، فسيكون السلوك غير محدد.

الحاشية 97 يقول:

مشغل الفاصلة يفعل ذلك لا تسفر عن قيمة.

مما يعني أنه لا يمكنك التنازل عن نتيجة مشغل الفاصلة.

من المهم ملاحظة أن عامل الفاصلة لديه أدنى الأسبقية وبالتالي هناك حالات يتم فيها استخدام () يمكن أن يحدث فرقًا كبيرًا، على سبيل المثال:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

سيكون لها الإخراج التالي:

1 4

يقوم عامل الفاصلة بدمج التعبيرين على جانبيه في تعبير واحد، وتقييمهما بالترتيب من اليسار إلى اليمين.يتم إرجاع قيمة الجانب الأيمن كقيمة التعبير بأكمله. (expr1, expr2) يشبه { expr1; expr2; } ولكن يمكنك استخدام نتيجة expr2 في استدعاء وظيفة أو مهمة.

غالبا ما يتم رؤيته في for حلقات لتهيئة أو الحفاظ على متغيرات متعددة مثل هذا:

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
    /* do something with low and high and put new values
       in newlow and newhigh */
}

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

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

حيث كانت القيم shortق أو intلقد فعلنا هذا:

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

قرأنا لاحقًا أن هذا لم يكن صحيحًا حقًا في C، لأنه (short *)ptr لم تعد قيمة l ولا يمكن زيادتها، على الرغم من أن مترجمنا في ذلك الوقت لم يمانع.ولإصلاح ذلك، قمنا بتقسيم التعبير إلى قسمين:

*(short *)ptr = short_value;
ptr += sizeof(short);

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

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

باستخدام عامل الفاصلة، تمكنا من استخدام هذا في التعبيرات أو البيانات كما أردنا:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

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

إنه يتسبب في تقييم عبارات متعددة، ولكنه يستخدم العبارة الأخيرة فقط كقيمة ناتجة (على ما أعتقد rvalue).

لذا...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

يجب أن يؤدي إلى تعيين x على 8.

عامل الفاصلة لا يفعل شيئًا ذا معنى، فهو ميزة غير ضرورية بنسبة 100٪.الاستخدام الرئيسي له هو "الأشخاص الذين يحاولون أن يكونوا أذكياء" وبالتالي يستخدمونه (عن غير قصد) للتعتيم على التعليمات البرمجية القابلة للقراءة.مجال الاستخدام الرئيسي هو التعتيم على الحلقات، على سبيل المثال:

for(int i=0, count=0; i<x; i++, count++)

أين int i=0, count=0 في الواقع ليس عامل الفاصلة، ولكن قائمة الإعلانات (نحن بالفعل في حيرة من أمرنا هنا). i++, count++ هو عامل الفاصلة، الذي يقيم المعامل الأيسر أولاً ثم المعامل الأيمن.نتيجة عامل الفاصلة هي نتيجة المعامل الصحيح.يتم تجاهل نتيجة المعامل الأيسر.

ولكن يمكن كتابة الكود أعلاه بطريقة أكثر قابلية للقراءة بدون عامل الفاصلة:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

الاستخدام الحقيقي الوحيد الذي رأيته لمشغل الفاصلة هو المناقشات المصطنعة حول نقاط التسلسل، حيث أن عامل الفاصلة يأتي مع نقطة تسلسل بين تقييم المعاملين الأيسر والأيمن.

لذا، إذا كان لديك رمز سلوك غير محدد مثل هذا:

printf("%d %d", i++, i++);

يمكنك في الواقع تحويله إلى مجرد سلوك غير محدد (ترتيب تقييم معلمات الوظيفة) عن طريق الكتابة

printf("%d %d", (0,i++), (0,i++));

توجد الآن نقطة تسلسل بين كل تقييم i++, ، لذلك على الأقل لن يتعرض البرنامج لخطر التعطل والحرق لفترة أطول، على الرغم من أن ترتيب تقييم معلمات الوظيفة لا يزال غير محدد.

بالطبع لن يكتب أحد مثل هذا الكود في تطبيقات حقيقية، فهو مفيد فقط لمناقشات محامي اللغة حول نقاط التسلسل في لغة C.

تم حظر عامل الفاصلة بواسطة MISRA-C:2004 وMISRA-C:2012 بحجة أنه ينشئ تعليمات برمجية أقل قابلية للقراءة.

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

for (tmp=0, i = MAX; i > 0; i--)

المكان الوحيد الذي رأيته مفيدًا هو عندما تكتب حلقة غير تقليدية حيث تريد القيام بأشياء متعددة في أحد التعبيرات (ربما تعبير init أو تعبير حلقة.شيء مثل:

bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
  size_t i1, i2;
  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
  {
    if(a1[i1] != a2[i2])
    {
      return false;
    }
  }

  return true;
}

أعذرني إذا كان هناك أي أخطاء في بناء الجملة أو إذا قمت بخلط أي شيء غير صارم في لغة C.أنا لا أزعم أن عامل التشغيل هو شكل جيد، ولكن هذا هو ما يمكنك استخدامه من أجله.في الحالة أعلاه ربما سأستخدم ملف while Loop بدلاً من ذلك، لذا ستكون التعبيرات المتعددة في init وloop أكثر وضوحًا.(وسأقوم بتهيئة i1 وi2 في السطر بدلاً من الإعلان ثم التهيئة....الخ الخ الخ.)

أقوم بإحياء هذا ببساطة للإجابة على أسئلة @Rajesh وJeffMercado والتي أعتقد أنها مهمة جدًا نظرًا لأن هذا أحد أفضل نتائج محركات البحث.

خذ مقتطف التعليمات البرمجية التالي على سبيل المثال

int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);

سوف تتم طباعته

1 5

ال i يتم التعامل مع الحالة كما هو موضح في معظم الإجابات.يتم تقييم كافة التعبيرات بالترتيب من اليسار إلى اليمين ولكن يتم تعيين التعبير الأخير فقط i.نتيجة ( تعبير )is1`.

ال j تتبع الحالة قواعد أسبقية مختلفة منذ ذلك الحين , لديه أدنى أسبقية المشغل.وبسبب هذه القواعد، يرى المترجم التعبير عن الواجب، ثابت، ثابت ....يتم تقييم التعبيرات مرة أخرى بترتيب من اليسار إلى اليمين وتظل آثارها الجانبية مرئية، وبالتالي، j يكون 5 كنتيجة ل j = 5.

ومن المثير للاهتمام، int j = 5,4,3,2,1; غير مسموح به بواسطة مواصفات اللغة.ان مُهيئ يتوقع ان تعبير المهمة لذلك مباشرة , المشغل غير مسموح به.

أتمنى أن يساعدك هذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top