C struck الاستيلاء على البيانات عن طريق الإزاحة
سؤال
دعنا نقول لدي هذا الهيكل:
typedef struct nKey {
int num;
widget* widget;
} NUMBER_KEY;
ووظيفة:
void dialKey(widget* widget) {
// Need to print 'num' of the struct that this widget resides in
}
كيف يمكنني إنجاز هذا؟ حاولت شيئًا مثل:
printf("%d", * (int *) widget - sizeof(int)); // Failure.org
تحرير: من الآمن افتراض أن القطعة التي يتم تمريرها هي في الواقع عضو في هيكل رقم _key
تحرير: البحث عن حل للمشكلة وليس طريقة أخرى.
المحلول
بالنظر إلى مجرد widgit* وليس widgit ** يتم تمريره إلى DialKey ، لا توجد طريقة للقيام بما تريد (قيمة widgit* ليس لها علاقة بـ number_key struct). على افتراض أنك تعني حقًا شيئًا مثل:
void dialKey(widget** ppWidget)
{
// Need to print 'num' of the struct that this widget resides in
}
لدى Microsoft ماكرو أنيق للقيام بهذا النوع من الأشياء (يساعد في القدرة على الحصول على إجراءات تتعامل مع القوائم المرتبطة بشكل عام في C):
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
يمكنك استخدام هذا مثل ذلك:
NUMBER_KEY* pNumberKey = CONTAINING_RECORD( *ppWidgit, NUMBER_KEY, widgit);
printf( "%d", pNumberKey->num);
نصائح أخرى
كما يشرح مايكل في إجابته ، لا يمكنك القيام بذلك مع قيود معينة لأنه لا توجد طريقة "للتراجع" الرسم البياني للمؤشر. لجعل الأمور أكثر وضوحًا ، اسمحوا لي أن أرسم مخططًا للكائنات (من حيث C ، وليس بالمعنى) المعني:
+- NUMBER_KEY --+
| ... | points to
| widget field -+-----------+
| ... | |
+---------------+ | + widget +
+-->| ... |
| ... |
+-->| ... |
| +--------+
points to |
[widget argument]-----------+
لاحظ الأسهم. إنها في اتجاه واحد - يمكنك "المشي" من مؤشر إلى قيمة مدببة ، لكن لا يمكنك "المشي" مرة أخرى. حتى تتمكن من الاحترام widget
حجة وظيفتك للوصول إلى widget
كائن ، ولكن بمجرد وجوده ، لا توجد طريقة لإخبار من يشير إليه - بما في ذلك أي حالات NUMBER_KEY
هياكل. فكر في الأمر: ماذا لو كان لديك عشرات المؤشرات الأخرى widget
, ، بعضهم من مختلف NUMBER_KEY
أشياء؟ كيف يمكن أن تتبع ذلك دون الاحتفاظ بقائمة بجميع المؤشرات في الداخل widget
هدف؟ إذا كنت بحاجة فعلاً إلى هذا ، فهذا ما عليك فعله - قم بعمله widget
أشر إلى امتلاكها NUMBER_KEY
.
يتم تعريف تخطيط ذاكرة الهيكل بواسطة برنامج التحويل البرمجي الخاص بك ، وسيعمل الكود الخاص بك فقط إذا كان لديك حشوة بايت 0 بين الأعضاء الهيكلية. عادةً ما لا ينصح بذلك حشوة بايت ب/ج 0 سيكون تراكب البنية عبر أحجام القراءة القياسية لمعظم المعالجات.
بعض وحدات الماكرو المفيدة لمشكلتك:
#define GET_FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field))
#define GET_FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
مثال:
NUMBER_KEY key;
key.num = 55;
int nOffSetWidget = GET_FIELD_OFFSET( NUMBER_KEY, widget);
int *pKeyNumAddress = (int *) &(key.widget) - nOffSetWidget );
printf("%d", * pKeyNumAddress ); // Should print '55'
C المعيار المعياري offsetof()
الماكرو (محدد في stddef.h
رأس) ، وهو مفيد في قضيتك.
#include <stddef.h>
void DialKey(widget** foo) {
printf("%d", *((char *)foo - offsetof(struct nKey, widget)));
}
لاحظ أنه يجب عليك تمرير تبوك من حقل الهيكل ، وليس قيمة!
الحل الأكثر أمانًا هو الحفاظ على مؤشر بنية nkey ذات الصلة في عنصر واجهة مستخدم
printf("%d", widget->myKey->num);
جميع الطرق الأخرى غير آمنة