Вот вопрос об интервью, который я видел на некотором форуме. Я пытался выяснить, как это работает, но я не вполне получаю его. Кто-то мог объяснить, как это работает?
Q: Данный подсказка участнику в структуре, запишите стандартную программу, которая возвращает указатель на структуру.
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);
}
Как это работает?
Основное уравнение здесь (вся арифметика в байтах):
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
Заключительные шаги:
a_ptr
приводится к char *
. struct s *
. Приложение: Как указывает Эли Бендерски, вам следует избегать ситуаций, в которых этот код был бы необходим. Почти всегда есть лучший способ.
Вы можете использовать макрос offsetof.
struct s* get_s_ptr(int *a_ptr)
{
return (struct s*)((char*)a_ptr - offsetof(struct s,a) );
}
Я опаздываю. мое интернет соединение медленное.
Ответ: не работает. Это не работает, даже если на первый взгляд может показаться, что "работает". В "ответе" делается попытка разыменовать нулевой указатель, что приводит к неопределенному поведению. Поэтому, если ваше представление о "работе" не включает неопределенное поведение, этот ответ не работает.
В этом решении есть и другие проблемы, кроме попытки разыменовать нулевой указатель (хотя одного этого вполне достаточно, чтобы выбросить этот "ответ" в мусорную корзину). Еще одна проблема заключается в том, что результатом (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));
}
Думал, что это будет полезно,
/* 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
к char * a
относительно struct s
struct s *
результирующий ptr