c struct захват данных по смещению - PullRequest
0 голосов
/ 30 июля 2009

Допустим, у меня есть такая структура:

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

edit: можно с уверенностью предположить, что передаваемый виджет на самом деле является членом структуры NUMBER_KEY

edit: поиск решения проблемы, а не другой метод.

Ответы [ 5 ]

5 голосов
/ 31 июля 2009

Как Майкл объясняет в своем ответе, вы не можете сделать это с заданными ограничениями, потому что нет способа "отойти" назад от графика указателя. Чтобы сделать вещи более очевидными, позвольте мне нарисовать диаграмму объектов (в терминах C, а не в смысле ООП):

+- NUMBER_KEY --+
|  ...          | points to      
| widget field -+-----------+
|  ...          |           |
+---------------+           |   + widget +
                            +-->|  ...   |
                                |  ...   |
                            +-->|  ...   |
                            |   +--------+
                  points to |
[widget argument]-----------+

Обратите внимание на стрелки. Они односторонние - вы можете «идти» от указателя к указанному значению, но вы не можете «идти» назад. Таким образом, вы можете прекратить аргумент widget вашей функции, чтобы добраться до объекта widget, но, находясь там, нет способа узнать, кто еще указывает на него - включая любые экземпляры структур NUMBER_KEY. Подумайте об этом: что если бы у вас было дюжина других указателей на один и тот же widget, некоторые из них из разных NUMBER_KEY объектов? Как он может отслеживать это, не сохраняя список всех указателей внутри объекта widget? Если вам это действительно нужно, это то, что вам нужно сделать - указать widget на то, что он владеет NUMBER_KEY.

4 голосов
/ 31 июля 2009

Учитывая, что только widgit *, а не widgit ** передается в dialKey, нет никакого способа сделать то, что вы хотите (значение widgit * не имеет отношения к структуре NUMBER_KEY). Предполагая, что вы действительно имеете в виду что-то вроде:

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);
0 голосов
/ 31 июля 2009
Стандарт

C определяет макрос offsetof() (определенный в заголовке stddef.h), что удобно в вашем случае.

#include <stddef.h>

void DialKey(widget** foo) {
    printf("%d", *((char *)foo - offsetof(struct nKey, widget)));
}

Обратите внимание, что вы должны передать адрес поля структуры, а не значение!

0 голосов
/ 31 июля 2009

Макет памяти структуры определяется вашим компилятором, ваш код будет работать, только если у вас есть 0-байтовые отступы между членами структуры. Обычно это не рекомендуется, поскольку в 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'
0 голосов
/ 30 июля 2009

Самое безопасное решение - сохранить указатель на связанную структуру nKey в виджете

 printf("%d", widget->myKey->num);

Все остальные методы небезопасны

...