структура c, захватывающая данные по смещению

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

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

Редактировать:ищу решение проблемы, а не другой метод.

Это было полезно?

Решение

Учитывая, что только 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);

Другие советы

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

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

Обратите внимание на стрелки. Они односторонние - вы можете "гулять" от указателя к указанному значению, но вы не можете " ходить " назад. Таким образом, вы можете задержать аргумент widget своей функции, чтобы получить доступ к объекту widget , но, находясь там, невозможно определить, кто еще указывает на это, включая любые экземпляры < code> NUMBER_KEY . Подумайте об этом: что если у вас есть дюжина других указателей на один и тот же виджет , некоторые из них из разных объектов 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);

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top