سؤال

ما هي الحاجة للمشغل الشرطي؟ وظيفيا أنها زائدة عن الحاجة، لأنه ينفذ بناء IF- آخر. إذا كان المشغل الشرطي أكثر كفاءة من التعايش المكافئ إذا لم يتم تفسيره، فلماذا لا يمكن تفسيره بشكل أكثر كفاءة من قبل المترجم؟

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

المحلول

مشغل Ternary هو راحة نصية وقابلة للقراءة، وليس اختصار الأداء. يتم تقسيم الأشخاص على مزاياها لشرائح التعقيد المختلفة، ولكن بالنسبة للظروف القصيرة، قد يكون من المفيد أن يكون لديك تعبير بخط واحد.

علاوة على ذلك، لأنه تعبير، كما كتب تشارلي مارتن, ، هذا يعني أنه يمكن أن يظهر على الجانب الأيمن من بيان في C. هذا هو قيمة لكونها موجزة.

نصائح أخرى

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

بعض الإجابات الأخرى المقدمة رائعة. لكنني فوجئت بأن لا أحد ذكر أنه يمكن استخدامه للمساعدة في فرض const صحة بطريقة مدمجة.

شيء من هذا القبيل:

const int n = (x != 0) ? 10 : 20;

في الأساس n هو const التي تعتمد القيمة الأولية على بيان الشرط. بديل أسهل هو جعل n ليس const, هذا من شأنه أن يسمح عادي if لتهيئة ذلك. ولكن إذا كنت تريد أن تكون const, ، لا يمكن القيام به مع عادي if. وبعد أفضل بديل يمكنك القيام به هو استخدام وظيفة المساعد مثل هذا:

int f(int x) {
    if(x != 0) { return 10; } else { return 20; }
}

const int n = f(x);

لكن الظهور إذا كان الإصدار أكثر إحكاما وأكثر قابلية للقراءة.

إنه أمر بالغ الأهمية للحصول على توضيح التعليمات البرمجية، مثل هذا:

Look->       See?!

No
:(
Oh, well
);

الإيصال والقدرة على إظهار إنشاء IF - إخراج آخر في تعبير.

هناك الكثير من الأشياء في ج غير مطلوبة من الناحية الفنية لأنها يمكن تنفيذها بسهولة أكثر أو أقل من حيث الأشياء الأخرى. فيما يلي قائمة غير مكتملة:

  1. في حين
  2. بالنسبة
  3. المهام
  4. بنيات

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

في بعض الأحيان يكون المشغل القصير هو أفضل طريقة لإنجاز المهمة. على وجه الخصوص عندما تريد أن تكون نتيجة Ternary قيمة L.

هذا ليس مثالا جيدا، لكنني أرسم فارغا بشكل أفضل. شيء واحد هو certian، ليس في كثير من الأحيان عندما تحتاج حقا إلى استخدام ternary، على الرغم من أنني ما زلت أستخدمها قليلا.

const char* appTitle  = amDebugging ? "DEBUG App 1.0" : "App v 1.0";

شيء واحد أود أن يحذر ضده على الرغم من تاتير التمرين معا. تصبح حقيقية
مشكلة في وقت الصيانة:

int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;

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

int getMyValue()
{
  if( myCondition )
    return 42;
  else
    return 314;
}

const int myValue = getMyValue();

... يمكن أن تصبح:

const int myValue = myCondition ? 42 : 314;

وهو أفضل سؤال قابل للنقاش سأختار عدم النقاش.

نظرا لأنه لم يذكر أحد بعد، فإن الطريقة الوحيدة للحصول على ذكي printf العبارات هي استخدام المشغل Ternary:

printf("%d item%s", count, count > 1 ? "s\n" : "\n");

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

إن حقيقة أن مشغل Ternary هو تعبير، وليس عبارة، يتيح استخدامه في التوسعات الماكرو لحدات ماكرو تشبه الوظيفة يتم استخدامها كجزء من تعبير. قد لا تكون Const جزءا من Formal C، لكن المعالج المسبق الماكرو يذهب إلى الوراء.

مكان واحد حيث رأيته يستخدم في حزمة صفيف تستخدم وحدات ماكرو للوصول إلى صفيف المحتملة. كان بناء الجملة للحصول على مرجع فحص شيء مثل aref(arrayname, type, index), ، حيث كان ArrayName في الواقع مؤشرا إلى بنية تضمن حدود الصفيف ومصفيف Char غير موقعة للبيانات، كان النوع هو النوع الفعلي للبيانات، وكان الفهرس هو الفهرس. كان التوسع في ذلك شعر رائع تماما (ولن أفعل ذلك من الذاكرة)، لكنه استخدم بعض المشغلين الفاحيين للقيام بالتحقق المرتبط.

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

تحرير: إليك المثال الذي كنت أتحدث عنه، من حزمة صفيف Berkeley Cad (GLU 1.4 Edition). توثيق استخدام Array_Fetch هو:

type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;

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

وهنا هو Defrintion الماكرو من Array_Fetch (لاحظ استخدام مشغل Ternary ومشغل التسلسل الفيزي لتنفيذ جميع الفرع الفرعية بالقيم المناسبة في الترتيب الصحيح كجزء من تعبير واحد):

#define array_fetch(type, a, i)         \
(array_global_index = (i),              \
  (array_global_index >= (a)->num) ? array_abort((a),1) : 0,\
  *((type *) ((a)->space + array_global_index * (a)->obj_size)))

يعد التوسع عن Array_Insert (الذي ينمو الصفيف إذا لزم الأمر، مثل Vector C ++) هو حلاقة، حيث تنطوي على مشغلي ثلاثي متداخلين.

إنه سكر متصنع واختصار سهل الاستخدام للحصول على موجز إذا / كتل أخرى تحتوي فقط على عبارة واحدة فقط. وظيفيا، يجب أن يؤدي كلا من بنيات متطابقة.

قد يكون مشغل Ternary أكثر أداء أكثر من بند عادي إذا كان آخر، فقد يكون هذا أمرا حاسما في التطبيقات المضمنة ولكن أيضا تحسين التحويل البرمجي قد ينهار هذا الاختلاف.

مثل DWN قال، كان الأداء أحد فوائده خلال صعود المعالجات المعقدة، مدونة MSDN سلوك المعالج غير الكلاسيكي: كيف يمكن أن يكون هناك شيء أسرع من عدم القيام بذلك يمنح مثالا يقول بوضوح الفرق بين المشغل الثلاثي (الشرطي) وإذا / بيان آخر.

إعطاء التعليمات البرمجية التالية:

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

int array[10000];

int countthem(int boundary)
{
 int count = 0;
 for (int i = 0; i < 10000; i++) {
  if (array[i] < boundary) count++;
 }
 return count;
}

int __cdecl wmain(int, wchar_t **)
{
 for (int i = 0; i < 10000; i++) array[i] = rand() % 10;

 for (int boundary = 0; boundary <= 10; boundary++) {
  LARGE_INTEGER liStart, liEnd;
  QueryPerformanceCounter(&liStart);

  int count = 0;
  for (int iterations = 0; iterations < 100; iterations++) {
   count += countthem(boundary);
  }

  QueryPerformanceCounter(&liEnd);
  printf("count=%7d, time = %I64d\n",
         count, liEnd.QuadPart - liStart.QuadPart);
 }
 return 0;
}

تكلفة الحدود المختلفة مختلفة بكثير و Wierd (انظر المواد الأصلية). بينما إذا كان التغيير:

 if (array[i] < boundary) count++;

ل

 count += (array[i] < boundary) ? 1 : 0;

وقت التنفيذ مستقل الآن عن قيمة الحدود، منذ:

تمكن المحسن من إزالة الفرع من التعبير الثغر.

ولكن على سطح المكتب Intel I5 وحدة المعالجة المركزية / Windows 10 / VS2015، فإن نتيجة الاختبار الخاصة بي مختلفة تماما مع مدونة MSDN.

عند استخدام وضع التصحيح, ، إذا / تكلفة آخر:

count=      0, time = 6434
count= 100000, time = 7652
count= 200800, time = 10124
count= 300200, time = 12820
count= 403100, time = 15566
count= 497400, time = 16911
count= 602900, time = 15999
count= 700700, time = 12997
count= 797500, time = 11465
count= 902500, time = 7619
count=1000000, time = 6429

وتكلفة المشغل الثلاثي:

count=      0, time = 7045
count= 100000, time = 10194
count= 200800, time = 12080
count= 300200, time = 15007
count= 403100, time = 18519
count= 497400, time = 20957
count= 602900, time = 17851
count= 700700, time = 14593
count= 797500, time = 12390
count= 902500, time = 9283
count=1000000, time = 7020 

عند استخدام وضع الإصدار, ، إذا / تكلفة آخر:

count=      0, time = 7
count= 100000, time = 9
count= 200800, time = 9
count= 300200, time = 9
count= 403100, time = 9
count= 497400, time = 8
count= 602900, time = 7
count= 700700, time = 7
count= 797500, time = 10
count= 902500, time = 7
count=1000000, time = 7

وتكلفة المشغل الثلاثي:

count=      0, time = 16
count= 100000, time = 17
count= 200800, time = 18
count= 300200, time = 16
count= 403100, time = 22
count= 497400, time = 16
count= 602900, time = 16
count= 700700, time = 15
count= 797500, time = 15
count= 902500, time = 16
count=1000000, time = 16

مشغل Ternary أبطأ من بيان IF / آخر على جهازي!

لذلك وفقا لتقنيات تحسين المحول البرمجي المختلفة، قد يتصرف / آخر مختلفا كثيرا.

ternary = شكل بسيط من if- آخر. إنه متوفر في الغالب للحصول على قابلية القراءة.

  • يوجد بعض المشغلين الأكثر إلحاحا في C فقط لأنهم يسمحون بتنفيذ وحدات ماكرو مختلفة تشبه الوظائف كتعبير واحد يعيد النتيجة. أود أن أقول أن هذا هو الغرض الرئيسي لماذا ?: و , يسمح للمشغلين بالوجود، على الرغم من أن وظائفهم زائدة عن الحاجة.

    دعونا نقول أننا نرغب في تطبيق ماكرو تشبه الوظيفة التي ترجع أكبر معلمين. ثم سيتم استدعاؤه على سبيل المثال:

    int x = LARGEST(1,2);
    

    الطريقة الوحيدة لتنفيذ هذا كعورو تشبه الوظيفة سيكون

    #define LARGEST(x,y) ((x) > (y) ? (x) : (y))
    

    لن يكون من الممكن مع if ... else بيان، لأنه لا يعيد قيمة النتيجة. ملحوظة)

  • الغرض الآخر من ?: هو أنه في بعض الحالات يزيد بالفعل من قابلية القراءة. في معظم الأحيان if...else هو أكثر قراءة، ولكن ليس دائما. خذ على سبيل المثال بيانات التبديل الطويلة المتكررة:

    switch(something)
    {
      case A: 
        if(x == A)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
    
      case B: 
        if(x == B)
        {
          array[i] = x;
        }
        else
        {
          array[i] = y;
        }
        break;
      ...
    }
    

    يمكن استبدال هذا بقراءة أكثر

    switch(something)
    {
      case A: array[i] = (x == A) ? x : y; break;
      case B: array[i] = (x == B) ? x : y; break;
      ...
    }
    
  • يرجى ملاحظة ذلك ?: هل أبدا النتيجة في كود أسرع من if-else. وبعد هذا بعض الأسطورة الغريبة التي أنشأها المبتدئين الخلطين. في حالة وجود رمز محسن، ?: يعطي أداء متطابق if-else في الغالبية العظمى من الحالات.

    إذا حدث شيء، ?: يمكن ان يكون أبطأ من if-else, ، لأنه يأتي مع عروض ترويجية من نوع ضمنية إلزامية، حتى للأمعاء التي لن يتم استخدامها. ولكن ?: لا يمكن أن يكون أسرع من if-else.


ملحوظة) الآن بالطبع شخص ما سوف يجادل ويتساءل لماذا لا تستخدم وظيفة. في الواقع إذا كنت تستطيع استخدام وظيفة، فإنه كذلك دائما الأفضل على ماكرو تشبه الوظيفة. ولكن في بعض الأحيان لا يمكنك استخدام الوظائف. لنفترض على سبيل المثال x في المثال أعلاه أعلن في نطاق الملف. يجب أن يكون التهيئة بالفعل تعبيرا ثابتا، لذلك لا يمكن أن يحتوي على مكالمة دالة. أمثلة عملية أخرى من حيث يجب عليك استخدام وحدات ماكرو تشبه الوظيفة تنطوي على نوع البرمجة الآمنة مع _Generic أو "x وحدات الماكرو".

كمثل

if(0)
do();


if(0)
{
do();
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top