متى يتم تخصيص/تهيئة المتغيرات الثابتة على مستوى الوظيفة؟

StackOverflow https://stackoverflow.com/questions/55510

  •  09-06-2019
  •  | 
  •  

سؤال

أنا واثق تمامًا من أنه سيتم تخصيص المتغيرات المعلنة عالميًا (وتهيئتها، إن أمكن) في وقت بدء البرنامج.

int globalgarbage;
unsigned int anumber = 42;

ولكن ماذا عن تلك الثابتة المحددة داخل الوظيفة؟

void doSomething()
{
  static bool globalish = true;
  // ...
}

متى يكون الفضاء ل globalish المخصصة؟أظن متى يبدأ البرنامجولكن هل تتم تهيئته بعد ذلك أيضًا؟أم أنه يتم تهيئته عندما doSomething() يسمى أولا؟

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

المحلول

لقد كنت مهتمًا بهذا الأمر لذا كتبت برنامج الاختبار التالي وقمت بتجميعه باستخدام الإصدار 4.1.2 من g++.

include <iostream>
#include <string>

using namespace std;

class test
{
public:
        test(const char *name)
                : _name(name)
        {
                cout << _name << " created" << endl;
        }

        ~test()
        {
                cout << _name << " destroyed" << endl;
        }

        string _name;
};

test t("global variable");

void f()
{
        static test t("static variable");

        test t2("Local variable");

        cout << "Function executed" << endl;
}


int main()
{
        test t("local to main");

        cout << "Program start" << endl;

        f();

        cout << "Program end" << endl;
        return 0;
}

ولم تكن النتائج كما توقعت.لم يتم استدعاء مُنشئ الكائن الثابت إلا في المرة الأولى التي تم فيها استدعاء الوظيفة.هنا هو الإخراج:

global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed

نصائح أخرى

بعض الألفاظ ذات الصلة من معيار C++:

3.6.2 تهيئة الكائنات غير المحلية [basic.start.init]

1

تخزين الكائنات مع مدة التخزين الثابتة (basic.stc.static) يجب أن تتم تهيئته بصفر (dcl.init) قبل حدوث أي تهيئة أخرى.كائنات أنواع الجراب (أنواع أساسية) مع تهيئة مدة تخزين ثابت مع تعبيرات ثابتة (expr.const) يجب تهيئتها قبل إجراء أي تهيئة ديناميكية.يجب تهيئة كائنات نطاق مساحة الاسم مع مدة التخزين الثابتة المحددة في نفس وحدة الترجمة وتهيئة ديناميكيًا بالترتيب الذي يظهر به تعريفهم في وحدة الترجمة.[ملحوظة: dcl.init.aggr يصف الترتيب الذي تتم تهيئة الأعضاء الكليين.تم وصف تهيئة الكائنات الثابتة المحلية في stmt.dcl. ]

[مزيد من النص أدناه لإضافة المزيد من الحريات لكتاب المترجمين]

6.7 بيان الإعلان [stmt.dcl]

...

4

التهيئة الصفرية (dcl.init) من جميع الكائنات المحلية مع مدة التخزين الثابتة (basic.stc.static) يتم تنفيذها قبل إجراء أي تهيئة أخرى.كائن محلي من نوع الجراب (أنواع أساسية) مع تهيئة مدة التخزين الثابتة التي تم تهيئتها مع التعبيرات الثابتة قبل إدخال الكتلة لأول مرة.يُسمح للتنفيذ بإجراء التهيئة المبكرة للكائنات المحلية الأخرى مع مدة تخزين ثابتة في ظل نفس الشروط التي يُسمح للتنفيذ بتهيئة كائن مع مدة تخزين ثابتة في نطاق مساحة الاسم (basic.start.init).وإلا يتم تهيئة مثل هذا الكائن في المرة الأولى التي يمر فيها التحكم من خلال إعلانه ؛يعتبر مثل هذا الكائن تهيئته عند الانتهاء من تهيئته.إذا خرج التهيئة عن طريق إلقاء استثناء ، فإن التهيئة غير مكتملة ، لذلك سيتم تجربتها مرة أخرى في المرة التالية التي تدخل فيها عنصر التحكم في الوقت التالي.إذا أعاد التحكم في إدخال الإعلان (بشكل متكرر) أثناء تهيئة الكائن ، يكون السلوك غير محدد.[مثال:

      int foo(int i)
      {
          static int s = foo(2*i);  // recursive call - undefined
          return i+1;
      }

--مثال النهاية]

5

سيتم تنفيذ المدمر لكائن محلي مع مدة تخزين ثابت إذا وفقط إذا تم إنشاء المتغير.[ملحوظة: basic.start.term يصف الترتيب الذي يتم به تدمير الكائنات المحلية ذات مدة التخزين الثابتة.]

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

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

@آدم:إن حقن الكود خلف الكواليس بواسطة المترجم هو سبب النتيجة التي رأيتها.

أحاول اختبار الكود مرة أخرى من آدم بيرس وأضفت حالتين أخريين:متغير ثابت في الفئة ونوع POD.المترجم الخاص بي هو g++ 4.8.1، في نظام التشغيل Windows (MinGW-32).والنتيجة هي أن المتغير الثابت في الفصل يتم التعامل معه بنفس الطريقة مع المتغير العام.سيتم استدعاء منشئه قبل الدخول إلى الوظيفة الرئيسية.

  • الخلاصة (لبيئة g++، Windows):

    1. متغير عالمي و عضو ثابت في الصف:يتم استدعاء المنشئ قبل الدخول رئيسي وظيفة (1).
    2. متغير ثابت محلي:يتم استدعاء المنشئ فقط عندما يصل التنفيذ إلى إعلانه في المرة الأولى.
    3. لو المتغير الثابت المحلي هو نوع POD, ، ثم تتم تهيئته أيضًا قبل الدخول رئيسي وظيفة (1).مثال لنوع POD: عدد صحيح ثابت = 10؛

(1):يجب أن تكون الحالة الصحيحة: "قبل استدعاء أي دالة من نفس وحدة الترجمة". ومع ذلك، ببساطة، كما في المثال أدناه، فهو كذلك رئيسي وظيفة.

تشمل <iostream>

#include < string>

using namespace std;

class test
{
public:
   test(const char *name)
            : _name(name)
    {
            cout << _name << " created" << endl;
    }

    ~test()
    {
            cout << _name << " destroyed" << endl;
    }

    string _name;
    static test t; // static member
 };
test test::t("static in class");

test t("global variable");

void f()
{
    static  test t("static variable");
    static int num = 10 ; // POD type, init before enter main function

    test t2("Local variable");
    cout << "Function executed" << endl;
}

int main()
{
    test t("local to main");
    cout << "Program start" << endl;
    f();
    cout << "Program end" << endl;
    return 0;
 }

نتيجة:

static in class created
global variable created
local to main created
Program start
static variable created
Local variable created
Function executed
Local variable destroyed
Program end
local to main destroyed
static variable destroyed
global variable destroyed
static in class destroyed

أي شخص اختبار في بيئة Linux؟

يتم تخصيص المتغيرات الثابتة داخل مقطع التعليمات البرمجية - فهي جزء من الصورة القابلة للتنفيذ، وبالتالي يتم تعيينها في التهيئة بالفعل.

يتم التعامل مع المتغيرات الثابتة ضمن نطاق الوظيفة بنفس الطريقة، ويكون النطاق عبارة عن بناء على مستوى اللغة فقط.

لهذا السبب، نضمن لك تهيئة المتغير الثابت إلى 0 (ما لم تحدد شيئًا آخر) بدلاً من قيمة غير محددة.

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

في لغة C++ (النطاق العالمي)، يتم استدعاء مُنشئات الكائنات الثابتة كجزء من بدء تشغيل البرنامج، تحت سيطرة مكتبة وقت تشغيل C.ضمن Visual C++، يمكن التحكم في ترتيب تهيئة الكائنات على الأقل بواسطة init_seg براغما.

أم أنه يتم تهيئته عند استدعاء doSomething() لأول مرة؟

نعم إنه كذلك.يتيح لك هذا، من بين أمور أخرى، تهيئة هياكل البيانات التي يمكن الوصول إليها عالميًا عندما يكون ذلك مناسبًا، على سبيل المثال داخل كتل المحاولة/الالتقاط.على سبيل المثالبدلاً من

int foo = init(); // bad if init() throws something

int main() {
  try {
    ...
  }
  catch(...){
    ...
  }
}

يمكنك كتابة

int& foo() {
  static int myfoo = init();
  return myfoo;
}

واستخدمه داخل كتلة المحاولة/الالتقاط.في المكالمة الأولى، سيتم تهيئة المتغير.ثم، في الاستدعاءات الأولى والتالية، سيتم إرجاع قيمتها (حسب المرجع).

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