سؤال

أعتقد أنه يجب أن يكون 01 لكن أحدهم يقول "غير محدد" ، أي سبب لذلك؟

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

المحلول

c++ هي كل من الزيادة والتعيين. عندما تحدث المهمة (قبل أو بعد رمز آخر في هذا السطر) يتم ترك حسب تقدير المترجم. يمكن أن يحدث بعد cout << أو قبل.

يمكن العثور على هذا في معيار C99 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

يمكنك العثور عليه في الصفحة 28 في PDF أو القسم 5.1.2.3

يمكن أن تحدث الزيادة الفعلية لـ P في أي وقت بين نقطة التسلسل السابقة ونقطة التسلسل التالية

نظرًا لأن شخصًا ما طلب معيار C ++ (لأن هذا سؤال C ++) يمكن العثور عليه في القسم 1.9.15 صفحة 10 (أو 24 بتنسيق PDF)

تقييمات معاملات المشغلين الفرديين والاعتداء الفرعي للتعبيرات الفردية غير متسلسلة

ويشمل أيضًا كتلة الكود التالية:

i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined

أشعر أن شرح C99 Standard أكثر وضوحًا ، لكنه صحيح في كلتا اللغتين.

نصائح أخرى

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

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

السبب في أنه غير محدد هو أن المترجم حر في حساب معلمات الوظائف بأي ترتيب. ضع في اعتبارك ما إذا كنت تتصل بالوظيفة (لأنك ، ولكن من الأسهل تصورها عندما تكون في بناء جملة الوظيفة):


  cout.output(c++).output(c);

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

يتم تعريف السلوك ولكن غير محدد. لم يتم تحديد الترتيب النسبي لتقييم استخدامين "C" في التعبير. ومع ذلك ، إذا قمت بتحويله إلى تدوين وظيفي ، يبدو أن هذا:

cout.operator<<(c++).operator<<(c);

هناك نقطة تسلسل بين تقييم الوسيطات إلى وظيفة ، وتنفيذ جسم الوظيفة ، ولا يتم ربط أجسام الوظائف ، وبالتالي فإن النتيجة غير محددة فقط وليس سلوكًا غير محدد.

إذا لم يكن لديك مشغل محمّل:

int c=0;
int a = c++ << c;

ثم سيكون السلوك غير محدد ، لأن كلا من تعديل واستخدام قيمة c بدون نقطة تسلسل متداخلة.

تحرير: التسلسل litb تجرب هو ببساطة خطأ. يحدد المعيار (§1.9/17): "عند استدعاء وظيفة (سواء كانت الوظيفة مضمنة أم لا) ، هناك نقطة تسلسل بعد تقييم جميع وسيطات الوظائف (إن وجدت) قبل تنفيذ أي تعبيرات أو بيانات في جسم الوظيفة. "

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

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

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

operator<<(operator<<(cout,c++), c);

هذا لا يزيل متطلبات نقاط التسلسل إما.

بقدر ما يكون غير محدد: الأمر بسيط للغاية حقًا: هناك نقطة تسلسل بعد تقييم جميع وسيطات الوظائف ، لذلك يجب تقييم جميع الوسائط لمكالمة وظيفة واحدة بالكامل (بما في ذلك جميع الآثار الجانبية) ، ثم يمكن أن تكون وسيطات استدعاء الوظيفة الأخرى تم تقييمه (مع الأخذ في الاعتبار أي آثار جانبية من الآخر) - ولكن لا يوجد أي شرط حول وسيطات استدعاء الوظيفة التي يجب تقييمها أولاً أو ثانية ، لذلك يمكن أن تكون c, ، ومن بعد c++, أو يمكن أن يكون c++, ، ومن بعد c - ولكن يجب أن يكون واحدًا أو آخر ، وليس تشابكًا.

كما أراها ، F (C ++) ؛ يعادل: f (c) ؛ C += 1 ؛

و F (C ++ ، C ++) ؛ يعادل: f (c ، c) ؛ C += 1 ؛ C += 1 ؛

ولكن قد يكون هذا هو أن F (C ++ ، C ++) ؛ يصبح f (c ، c+1) ؛ C+= 2 ؛

تجربة مع GCC و Clang ، أولاً في ج

#include <stdio.h>

void f(int a, int b) {
    printf("%d %d\n",a,b);
}

int main(int argc, char **argv) {
    int c = 0;
    f(c++,c++);

    return 0;
}

وفي C ++

#include <iostream>

int main(int argc, char **argv) {
    int c = 0;
    std::cout << c++ << " " << c++ << std::endl;
    return 0;
}

من المثير للاهتمام ، حيث أن GCC و G ++ المترجمة ينتجون في 1 0 في حين أن Clang المترجمة النتائج في 0 1

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