c++ ترتيب بدء المتغيرات الساكنة




visual-studio gcc (5)

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

ولكن ، ترتيب التهيئة للمتغيرات ، غير محدد عبر وحدات تجميع مختلفة.

أين يمكنني أن أرى بعض التفسيرات حول هذا الأمر لـ gcc و MSVC (أعلم أن الاعتماد على ذلك هو فكرة سيئة للغاية - فقط لفهم المشاكل التي قد نواجهها مع الكود القديم عند الانتقال إلى نظام التشغيل الرئيسي الرئيسي والمختلف في دول مجلس التعاون الخليجي) ؟



Answer #2

أتوقع أن ترتيب منشئ الوحدات النمطية بشكل رئيسي دالة ما ترتيب تمرير الكائنات إلى رابط.

ومع ذلك ، فإن دول مجلس التعاون الخليجي تتيح لك استخدام init_priority لتحديد ترتيب الطلبات العالمية بوضوح :

class Thingy
{
public:
    Thingy(char*p) {printf(p);}
};

Thingy a("A");
Thingy b("B");
Thingy c("C");

مخرجات 'ABC' كما تتوقع ، ولكن

Thingy a __attribute__((init_priority(300))) ("A");
Thingy b __attribute__((init_priority(200))) ("B");
Thingy c __attribute__((init_priority(400))) ("C");

مخرجات 'BAC'.


Answer #3

نظرًا لأنك تعرف بالفعل أنه لا ينبغي عليك الاعتماد على هذه المعلومات ما لم تكن هناك ضرورة قصوى ، فإليك الأمر. ملاحظتي العامة عبر مختلف سلاسل المفاتيح (MSVC ، gcc / ld ، clang / llvm ، إلخ) هي أن الترتيب الذي يتم فيه تمرير ملفات الكائن إلى رابط هو الترتيب الذي سيتم به تهيئة.

هناك استثناءات لهذا ، وأنا لا أدعي جميعهم ، لكن إليكم ما قابلته في نفسي:

1) تبدأ إصدارات GCC قبل 4.7 فعليًا في الترتيب العكسي لخط الارتباط. هذه التذكرة في دول مجلس التعاون الخليجي هي عندما حدث التغيير ، وكسر الكثير من البرامج التي اعتمدت على نظام التهيئة (بما في ذلك الأمر الخاص بي!).

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

3) في نظام التشغيل Windows ، توجد دالة نقطة إدخال مكتبة مشتركة (DLL) أنيقة ومفيدة للغاية ، تسمى DllMain() ، والتي إذا تم تعريفها ، ستعمل مع المعلمة "fdwReason" مساوية لـ DLL_PROCESS_ATTACH مباشرة بعد أن تتم تهيئة كافة البيانات العمومية و قبل التطبيق المستهلك لديه فرصة لاستدعاء أي وظائف على DLL. هذا مفيد للغاية في بعض الحالات ، وليس هناك سلوك مماثل لهذا على منصات أخرى مع GCC أو Clang مع C أو C ++. أقرب ما ستجد هو جعل وظيفة منشئ ذات أولوية (انظر أعلاه النقطة (2)) ، وهي بالتأكيد ليست الشيء نفسه ولن تعمل لكثير من حالات الاستخدام التي تعمل DllMain ().

4) إذا كنت تستخدم CMake لإنشاء أنظمة الإنشاء الخاصة بك ، والتي غالباً ما أقوم بها ، فقد وجدت أن ترتيب ملفات مصدر الإدخال سيكون ترتيب ملفات الكائنات الناتجة الناتجة إلى رابط. ومع ذلك ، غالبًا ما يتم ربط تطبيقك / DLL أيضًا في المكتبات الأخرى ، وفي هذه الحالة ستكون هذه المكتبات على سطر الرابط بعد ملفات مصدر الإدخال. إذا كنت تبحث عن أن يكون أحد الكائنات العالمية الخاصة بك هو أول كائن تهيئته ، فأنت محظوظ ويمكن وضع الملف المصدر الذي يحتوي على هذا الكائن ليكون الأول في قائمة الملفات المصدر. ومع ذلك ، إذا كنت تبحث أن يكون لديك واحد آخر واحد لتهيئة (التي يمكن أن تتكرر بشكل فعال سلوك DllMain ()!) ثم يمكنك إجراء مكالمة إلى add_library () مع هذا الملف المصدر واحد لإنتاج مكتبة ثابتة ، وإضافة مكتبة ثابتة الناتج كآخر ارتباط ارتباط في استدعاء target_link_libraries () الخاص بك للتطبيق / DLL. كن حذرًا من أن الكائن العالمي قد يتم تحسينه في هذه الحالة ويمكنك استخدام --whole-archive لإجبار الرابط على عدم إزالة الرموز غير المستخدمة --whole-archive المحدد.

نصيحة ختامية

لمعرفة مطلقة ترتيب التهيئة الناتجة للتطبيق المرتبط / المكتبة المشتركة ، قم بتمرير -print-map to ld linker و grep لـ .init_array (أو في GCC قبل 4.7 ، grep لـ .ctors). سيتم طباعة كل منشىء عالمي بالترتيب الذي سيتم به التهيئة ، وتذكر أن الأمر معاكس في GCC قبل 4.7 (راجع النقطة (1) أعلاه).

العامل المحفّز لكتابة هذه الإجابة هو أنني بحاجة إلى معرفة هذه المعلومات ، ولم يكن أمامي خيار آخر سوى الاعتماد على أمر التهيئة ، ووجدت بتّات متفرقة فقط من هذه المعلومات عبر مشاركات SO الأخرى ومنتديات الإنترنت. تعلمت معظمها من خلال الكثير من التجارب ، وآمل أن هذا ينقذ بعض الناس وقت القيام بذلك!


Answer #4

كما تقول أن الترتيب غير معروف عبر وحدات تجميع مختلفة.

داخل وحدة التجميع نفسها يتم تعريف الطلب بشكل جيد: نفس الترتيب مثل التعريف.

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

لـ gcc: تحقق من ld

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

هذا بشكل عام فقط مشكلة عند تهيئة العمومية التي تشير بعضها البعض أثناء التهيئة الخاصة بهم (بحيث يؤثر فقط على الكائنات مع المنشئ).

هناك تقنيات للالتفاف على المشكلة.

  • التهيئة كسول.
  • شوارز كاونتر
  • ضع جميع المتغيرات العالمية المعقدة داخل نفس وحدة التجميع.

Answer #5

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





linker