العمليات الحسابية بين الثوابت
-
09-12-2019 - |
سؤال
النظر في هذا الرمز.
#define A 5
#define B 3
int difference = A - B;
هل يتم ترميز قيمة "الفرق" كـ "2" في وقت الترجمة، أم أنه يتم حسابها في وقت التشغيل؟
المحلول
ال A
و B
وحدات الماكرو هي نوع من التشتيت.هذا:
#define A 5
#define B 3
int difference = A - B;
يعادل بالضبط هذا:
int difference = 5 - 3;
لذلك دعونا نناقش هذا الأخير.
5 - 3
هو التعبير المستمر, ، وهو تعبير "يمكن تقييمه أثناء الترجمة بدلاً من وقت التشغيل، وبالتالي يمكن استخدامه في أي مكان قد يكون فيه الثابت".إنه أيضًا تعبير *عدد صحيح ثابت".على سبيل المثال، يجب أن تكون تسمية الحالة عبارة عن تعبير عدد صحيح ثابت، لذا يمكنك كتابة إما ما يلي:
switch (foo) {
case 2: /* this is a constant */
...
}
أو هذا:
switch (foo) {
case 5 - 3: /* this is a constant expression */
...
}
لكن لاحظ أن التعريف يقول ذلك يمكن ان يكون تقييمها أثناء الترجمة، وليس أنه يجب أن يكون.هناك بعض السياقات التي تتطلب تعبيرات ثابتة، وفي تلك السياقات التعبير يجب يتم تقييمها في وقت التجميع.
لكن بافتراض ذلك difference
تم الإعلان عنه داخل بعض الوظائف، فإن المُهيئ ليس واحدًا من تلك السياقات.
أي مترجم يستحق ما تدفعه مقابله (حتى لو كان مجانيًا) سوف ينخفض 5 - 3
ل 2
في وقت الترجمة، وإنشاء التعليمات البرمجية التي تخزن القيمة 2
في difference
.ولكن ليس مطلوبا القيام بذلك.يحدد معيار C سلوك من البرامج؛ولا يحدد كيفية تنفيذ هذا السلوك.ولكن من الآمن أن نفترض أن أي مترجم تستخدمه سوف يحل محله 5 - 3
بواسطة 2
.
حتى لو كتبت:
int difference = 2;
يمكن للمترجم بشكل قانوني إنشاء تعليمات برمجية تقوم بتحميل القيمة 5
في السجل، ويطرح 3
منه، ويخزن محتويات السجل فيه difference
.قد يكون هذا أمرًا سخيفًا، لكن معيار اللغة لا يستبعده.
طالما أن النتيجة النهائية هي ذلك difference
لديه القيمة 2
, ، معيار اللغة لا يهتم بكيفية القيام بذلك.
ومن ناحية أخرى، إذا كتبت:
switch (foo) {
case 5 - 3: /* ... */
case 2: /* ... */
}
ثم المترجم يجب احسب النتيجة حتى تتمكن من تشخيص الخطأ (لا يمكن أن يكون لديك تصنيفان لحالة بنفس القيمة.
وأخيرا، إذا قمت بتحديد difference
في نطاق الملف (خارج أي وظيفة)، ثم القيمة الأولية يفعل يجب أن تكون ثابتة.لكن التمييز الحقيقي في هذه الحالة ليس ما إذا كان الأمر كذلك أم لا 5 - 3
سيتم تقييمه في وقت الترجمة، فهو ما إذا كنت مسموح لاستخدام تعبير غير ثابت.
مرجع:أحدث مسودة لمعيار 2011 C هي N1570 (ملف PDF كبير)؛تمت مناقشة التعبيرات الثابتة في القسم 6.6.
نصائح أخرى
المعيار لا يحدد هذا النوع من الأشياء.لا يذكر شيئًا عن التحسينات المحتملة مثل هذا (ولسبب وجيه.المعيار يحدد الدلالات، وليس التنفيذ).
لماذا لا تنظر إلى التفكيك للمترجم الخاص بك؟سيعطيك ذلك إجابة محددة.
...
لذلك دعونا نفعل ذلك.
هنا هو الإخراج من VC++ 10:
#include <iostream>
#define A 5
#define B 3
int main() {
int x = A - B;
std::cout << x; // make sure the compiler doesn't toss it away
010A1000 mov ecx,dword ptr [__imp_std::cout (10A2048h)]
010A1006 push 2
010A1008 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (10A2044h)]
return 0;
010A100E xor eax,eax
كما ترون، فإنه مجرد استبدال حدوث x
بقيمة ثابتة تبلغ 2 ودفعتها إلى المكدس للاتصال بـ cout
.لم يتم تقييم التعبير في وقت التشغيل.