ما هو الغرض ونوع الإرجاع لمشغل __builtin_offsetof؟

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

  •  03-07-2019
  •  | 
  •  

سؤال

ما هو الغرض من عامل التشغيل __builtin_offsetof (أو عامل تشغيل _FOFF في Symbian) في C++؟

وبالإضافة إلى ذلك ماذا يعود؟المؤشر؟عدد البايتات؟

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

المحلول

إنه برنامج مدمج يقدمه مترجم دول مجلس التعاون الخليجي لتنفيذ offsetof الماكرو المحدد بواسطة معيار C وC++:

دول مجلس التعاون الخليجي - تعويض

تقوم بإرجاع الإزاحة بالبايت التي يوجد بها عضو في بنية/اتحاد POD.

عينة:

struct abc1 { int a, b, c; };
union abc2 { int a, b, c; };
struct abc3 { abc3() { } int a, b, c; }; // non-POD
union abc4 { abc4() { } int a, b, c; };  // non-POD

assert(offsetof(abc1, a) == 0); // always, because there's no padding before a.
assert(offsetof(abc1, b) == 4); // here, on my system
assert(offsetof(abc2, a) == offsetof(abc2, b)); // (members overlap)
assert(offsetof(abc3, c) == 8); // undefined behavior. GCC outputs warnings
assert(offsetof(abc4, a) == 0); // undefined behavior. GCC outputs warnings

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

نصائح أخرى

كما يشير @litb ويظهر @JesperE، يوفر Offsetof() إزاحة عددية بالبايت (مثل size_t قيمة).

متى يمكنك استخدامه؟

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

#include <stddef.h>

typedef stuct config_info config_info;
struct config_info
{
   int parameter1;
   int parameter2;
   int parameter3;
   char *string1;
   char *string2;
   char *string3;
   int parameter4;
} main_configuration;

typedef struct config_desc config_desc;
static const struct config_desc
{
   char *name;
   enum paramtype { PT_INT, PT_STR } type;
   size_t offset;
   int   min_val;
   int   max_val;
   int   max_len;
} desc_configuration[] =
{
    { "GIZMOTRON_RATING", PT_INT, offsetof(config_info, parameter1), 0, 100, 0 },
    { "NECROSIS_FACTOR",  PT_INT, offsetof(config_info, parameter2), -20, +20, 0 },
    { "GILLYWEED_LEAVES", PT_INT, offsetof(config_info, parameter3), 1, 3, 0 },
    { "INFLATION_FACTOR", PT_INT, offsetof(config_info, parameter4), 1000, 10000, 0 },
    { "EXTRA_CONFIG",     PT_STR, offsetof(config_info, string1), 0, 0, 64 },
    { "USER_NAME",        PT_STR, offsetof(config_info, string2), 0, 0, 16 },
    { "GIZMOTRON_LABEL",  PT_STR, offsetof(config_info, string3), 0, 0, 32 },
};

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

الجزء الرئيسي من هذه الوظائف هو:

static int validate_set_int_config(const config_desc *desc, char *value)
{
    int *data = (int *)((char *)&main_configuration + desc->offset);
    ...
    *data = atoi(value);
    ...
}

static int validate_set_str_config(const config_desc *desc, char *value)
{
    char **data = (char **)((char *)&main_configuration + desc->offset);
    ...
    *data = strdup(value);
    ...
}

وهذا يتجنب الاضطرار إلى كتابة دالة منفصلة لكل عضو منفصل في البنية.

الغرض من عامل التشغيل __offsetof المضمن هو أن بائع المترجم يمكنه الاستمرار في #تعريف ماكرو Offsetof()، مع جعله يعمل مع الفئات التي تحدد المشغل الأحادي&.يعمل تعريف الماكرو النموذجي C لـ Offsetof() فقط عندما يقوم (&lvalue) بإرجاع عنوان تلك rvalue.أي.

#define offsetof(type, member) (int)(&((type *)0)->member) // C definition, not C++
struct CFoo {
    struct Evil {
        int operator&() { return 42; }
    };
    Evil foo;
};
ptrdiff_t t = offsetof(CFoo, foo); // Would call Evil::operator& and return 42

كما قال @litb:الإزاحة بالبايت لعضو البنية/الفئة.في C++، هناك حالات حيث يكون الأمر غير محدد، في حالة شكوى المترجم.IIRC، إحدى طرق تنفيذ ذلك (في لغة C، على الأقل) هي القيام بذلك

#define offsetof(type, member) (int)(&((type *)0)->member)

ولكنني متأكد من أن هناك مشاكل في هذا الأمر، ولكن سأترك ذلك للقارئ المهتم ليشير إليه...

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