Учитывая указатель на член a в структуре, напишите подпрограмму, которая возвращает указатель на структуру - PullRequest
9 голосов
/ 20 июня 2010

Вот вопрос интервью, который я видел на каком-то форуме. Я пытался понять, как это работает, но я не совсем понимаю. Может кто-нибудь объяснить, как это работает?

В: Учитывая указатель на член a в структуре, напишите подпрограмму, которая возвращает указатель на структуру.

struct s 
{
   ...
   int a;
   …
};

struct s *get_s_ptr(int *a_ptr)
{
   // implement this.
}

Ответ:

struct s* get_s_ptr(int *a_ptr)
{
   return (struct s*)((char*)a_ptr - (int)&((struct s*)0)->a);
}

Ответы [ 4 ]

12 голосов
/ 20 июня 2010

Как это работает?

Основное уравнение здесь (все арифметические в байтах):

address of struct member s->a == s + byte offset of a

Учитывая тип s, один компилятор и один целевой компьютер, они определили смещение в байтах a - оно одинаково для каждой структуры типа s.

Вы получили левую сторону, и ваш интервьюер попросил вас восстановить s. Вы можете сделать это, получив новое уравнение; вычтите смещение байта с обеих сторон:

address of struct member s->a - byte offset of a == s

В задаче вы дали адрес s->a, но вам нужно выяснить смещение байта. Для этого вы используете исходное уравнение снова с s равным нулю:

address of struct member s->a where s is zero == zero + byte offset of a 
                                              == byte offset of a

Левая сторона в C построена следующим образом

struct pointer s where s is zero                            (struct s *)0
struct member s->a where s is zero                          ((struct s*)0)->a
address of s->a where s is zero                             &((struct s*)0)->a

Заключительные шаги:

  1. Чтобы сделать арифметику допустимой C, это смещение байта приводится к целому числу.
  2. Чтобы убедиться, что вычитание выполняется в байтах, a_ptr приведено к char *.
  3. Для получения правильного результата разность приведена к struct s *.

Приложение: Как отмечает Эли Бендерский, вам следует избегать ситуаций, когда этот код был бы необходим. Почти всегда есть лучший способ.

6 голосов
/ 20 июня 2010

Ответ: это не так.Это не работает, даже если может показаться, что «работает» с первого взгляда.«Ответ» делает попытку разыменования нулевого указателя, что приводит к неопределенному поведению.Таким образом, если ваша идея «работы» не включает неопределенное поведение, этот ответ не работает.

Есть и другие проблемы с этим решением, кроме попытки разыменования нулевого указателя (хотя одного этого вполне достаточно дляэтот "ответ" на мусорное ведро).Другая проблема заключается в том, что результатом (struct s*) 0 является нулевой указатель типа struct s *.Язык не дает никаких гарантий относительно фактического физического значения нулевого указателя.Если бы это могло быть что-то вроде 0xBAADFOOD, то это сразу бы испортило предполагаемую функциональность «ответа».

Надлежащая реализация подразумеваемой техники включала бы стандартный макрос offsetof (уже предложенный в Nyanотвечу, но я повторю еще раз)

struct s* get_s_ptr(int *a_ptr)
{
   return (struct s*) ((char *) a_ptr - offsetof(struct s, a));
}
4 голосов
/ 20 июня 2010

Вы можете использовать смещение макроса.

struct s* get_s_ptr(int *a_ptr)
{
   return (struct s*)((char*)a_ptr - offsetof(struct s,a) );
}

Я опоздал. у меня медленное интернет соединение.

1 голос
/ 06 августа 2010

Думаю, это будет полезно,

/* offsetof example */
#include <stdio.h>
#include <stddef.h>

struct mystruct {
    char singlechar;
    char arraymember[10];
    char anotherchar;
};

int main ()
{
    printf ("offsetof(mystruct,singlechar) is %d\n",offsetof(mystruct,singlechar));
    printf ("offsetof(mystruct,arraymember) is %d\n",offsetof(mystruct,arraymember));
    printf ("offsetof(mystruct,anotherchar) is %d\n",offsetof(mystruct,anotherchar));

    return 0;
}

Выход:

offsetof(mystruct,singlechar) is 0
offsetof(mystruct,arraymember) is 1
offsetof(mystruct,anotherchar) is 11

Так что в вашем случае,

return (struct s*) ((char *) a_ptr - offsetof(struct s, a));
  • приведение aptr к символу *
  • вычесть смещение a относительно struct s
  • приведение к struct s*
  • вернуть resultant ptr
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...