سؤال

لدي تعريف بنية اختبار على النحو التالي:

struct test{
    int a, b, c;
    bool d, e;
    int f;
    long g, h;
};

وفي مكان ما أستخدمه بهذه الطريقة:

test* t = new test;   // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15;          // directly manipulate the third word
cout << t->c;         // look if it really affected the third integer

هذا يعمل بشكل صحيح على النوافذ الخاصة بي - يطبع 15 كما هو متوقع ، ولكن هل هو آمن؟ هل يمكنني أن أكون متأكدًا حقًا من أن المتغير على الفور في الذاكرة ، أريد أن يكون - بشكل واضح في حالة هذه الهياكل المدمجة (على سبيل المثال F موجود على المترجم الخاص بي الكلمة الخامسة ، لكنه متغير سادس)؟

إذا لم يكن الأمر كذلك ، فهل هناك أي طريقة أخرى لمعالجة أعضاء الهيكل مباشرة دون وجود بنية-الأعضاء بالفعل في الكود؟

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

المحلول

يبدو أنك تطرح سؤالين

هل من الآمن علاج واختبار 3 طول int int؟

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

هل هناك طريقة أفضل للوصول إلى عضو بدون اسمه؟

نعم. حاول استخدام إزاحة الماكرو/المشغل. سيوفر ذلك إزاحة الذاكرة لعضو معين داخل هيكل وسيسمح لك بوضع نقطة لهذا العضو بشكل صحيح.

size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);

طريقة أخرى على الرغم من أنها مجرد أخذ عنوان C مباشرة

int* pointerToC = &(someTest->c);

نصائح أخرى

لا ، لا يمكنك التأكد. المترجم حر في تقديم الحشو بين أعضاء الهيكل.

إضافة إلى إجابة جاريدار, ، خيار آخر في C ++ فقط (وليس في Cla C) هو إنشاء كائن مؤشر إلى عضو:

struct test
{
  int a, b, c;
  bool d, e;
  int f;
  long g, h;
};

int main(void)
{
  test t1, t2;

  int test::*p;  // declare p as pointing to an int member of test
  p = &test::c;  // p now points to 'c', but it's not associating with an object
  t1->*p = 3;    // sets t1.c to 3
  t2->*p = 4;    // sets t2.c to 4

  p = &test::f;
  t1->*p = 5;    // sets t1.f to 5
  t2->*p = 6;    // sets t2.f to 6
}

ربما تبحث عن offsetof دقيق. هذا سيحصل على إزاحة البايت للعضو. يمكنك بعد ذلك معالجة العضو في هذا الإزاحة. لاحظ رغم ذلك ، هذا الماكرو هو التنفيذ المحدد. تضمن stddef.h للحصول عليه للعمل.

ربما لا تكون آمنة ولا يمكن قراءة 100 ٪ ؛ وبالتالي جعل هذا النوع من التعليمات البرمجية غير مقبول في رمز إنتاج الحياة الحقيقية.

استخدم أساليب SET و BOOST :: BIND لإنشاء functor الذي سيغير هذا المتغير.

بصرف النظر عن مشكلات الحشو/المحاذاة التي طرحتها الإجابات الأخرى ، فإن الكود ينتهك قواعد التعرجات الصارمة ، مما يعني أنه قد يكسر للبناء الأمثل (لست متأكدًا من قيام MSVC بذلك ، لكن GCC -O3 سوف تنكسر على هذا النوع من السلوك). في الأساس ، لأن test *t و int *ptr من أنواع مختلفة ، قد يفترض المترجم أنه يشير إلى أجزاء مختلفة من الذاكرة ، وقد يعيد ترتيب العمليات.

النظر في هذا التعديل البسيط:

test* t = new test;
int* ptr = (int*) t;

t->c = 13;
ptr[2] = 15;
cout << t->c;

يمكن أن يكون الإخراج في النهاية أيضًا 13 أو 15, ، اعتمادًا على ترتيب العمليات التي يستخدمها المترجم.

وفقًا للفقرة 9.2.17 من المعيار ، من القانوني في الواقع إلقاء مؤشر إلى هيكل على مؤشر إلى عضوه الأول ، مما يوفر الهيكل هو جراب:

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

ومع ذلك ، لا تقدم المعيار أي ضمانات حول تخطيط الهياكل - حتى هياكل POD - بخلاف أن عنوان عضو لاحق سيكون أكبر من عنوان عضو سابق ، شريطة عدم وجود مواصفات وصول (private:, protected: أو public:) بينهم. لذلك ، علاج الجزء الأولي من الخاص بك struct test كمجموعة من 3 أعداد صحيحة هو سلوك غير محدد تقنيا.

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