Получить указатель на структуру по указателю на ее член [дубликат]

Я считаю, что тег будет в основном до вас.

Я не понимаю, почему это не должно быть h1, учитывая, что это обычный текст.

Но, анализируя ваше дело , это то, что я бы сделал.

Вариант 1. Используйте h1 и определите его как тег, который будет использоваться для отображения логотипа. Никогда еще он не должен использоваться в dom.

Вариант 2. Создайте новый тег для логотипа, например. Они называются «Пользовательские элементы». Это должно выглядеть примерно так:

var logo = document.registerElement('com-logo', {
    prototype: Object.create(HTMLElement.prototype)
});

Затем вам нужно отформатировать логотип с помощью CSS.

Прочитайте этот , чтобы узнать немного больше о пользовательских элементах! :)

12
задан AoeAoe 22 April 2012 в 17:25
поделиться

3 ответа

Как заметил Оуа, выражение оператора ({ ... }) является расширением GNU; вы не сможете это использовать. Ваше основное выражение близко к тому, что требуется, но не хватает скобок:

#define container_of(ptr, type, member) \
                      ((type *) ((char *)(ptr) - offsetof(type, member)))

Это выглядит чисто для меня. Он распространяется только через две строки для SO.

15
ответ дан Jonathan Leffler 19 August 2018 в 05:30
поделиться
  • 1
    В самом деле, и следует упомянуть, что единственной причиной использования ядром Linux нестандартного C является улучшенная проверка ошибок компиляции. – R.. 22 April 2012 в 17:59
  • 2
    Звучит неплохо для меня, я думаю, что я объединить оба ответа в свой фрагмент реализации. Спасибо. – AoeAoe 22 April 2012 в 21:54
  • 3
    Являются ли эти макросы действительно портативными? Вы накладываете указатель произвольного типа на указатель на char, а затем выполняете арифметику указателя с указателем на указатель, а затем приводите результат к другому типу указателя. Это выглядит довольно взломанным. Гарантируется ли работа над любым совместимым с ISO компилятором? – anton_rh 31 May 2018 в 04:28
  • 4
    Есть несколько факторов, которые означают, что он будет работать; если вы пожелаете, вы можете добавить несколько промежуточных void * бросков в последовательность, но это не обязательно для любой существующей реализации. Одним из факторов является C11 §6.2.5 ¶28 Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа.48) и в сноске 48 говорится: Те же требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращают значения из функций и членов объединений. – Jonathan Leffler 31 May 2018 в 04:37
  • 5
    Основное существенное отличие состоит в том, что вы не можете увеличивать или уменьшать значение void * (n стандартного C, потому что для sizeof(void) нет значения. GCC рассматривает sizeof(void) как 1. – Jonathan Leffler 31 May 2018 в 04:40

Макрос записывается так, как он выполняет проверку типа на ptr. Можно использовать составной литерал вместо выражения оператора и вернуться к простой проверке указателей вместо использования __typeof__, если компилятор не совместим с gcc:

#ifdef __GNUC__
#define member_type(type, member) __typeof__ (((type *)0)->member)
#else
#define member_type(type, member) const void
#endif

#define container_of(ptr, type, member) ((type *)( \
    (char *)(member_type(type, member) *){ ptr } - offsetof(type, member)))
12
ответ дан Christoph 19 August 2018 в 05:30
поделиться
  • 1
    +1: Вы хорошо сохранили проверку типов typeof, когда они доступны, и все же правильны, когда это не так. – Jonathan Leffler 22 April 2012 в 21:59
  • 2
    Действительно ли эти скобки хороши? {} – AoeAoe 22 April 2012 в 23:00
  • 3
    @AoeAoe: фигурные скобки суть суть - мы не бросаем ptr, но инициализируем новое значение с помощью синтаксиса буквенного соединения C99; в частности, это означает, что будут выполняться только неявные преобразования, как работает проверка типа – Christoph 23 April 2012 в 00:21
  • 4
    @ Кристоф Спасибо вам большое. – AoeAoe 23 April 2012 в 11:33
  • 5
    Просто небольшое предложение: void следует изменить на const void, так что, если ptr является const-квалифицированным, тогда компилятор не будет жаловаться на отбрасывание квалификатора const. – Alexandros 29 September 2013 в 23:09

Совместимость с ISO C90 с проверкой типа. (Однако предостережение: две оценки ptr!) [/ ​​G0]

#define container_of(ptr, type, member) \
   ((type *) ((char *) (ptr) - offsetof(type, member) + \
              (&((type *) 0)->member == (ptr)) * 0))

struct container {
  int dummy;
  int memb;
};


#include <stddef.h>
#include <stdio.h>

int main()
{
  struct container c;
  int *p = &c.memb;
  double *q = (double *) p;
  struct container *pc = container_of(p, struct container, memb);
  struct container *qc = container_of(q, struct container, memb);
  return 0;
}

Тест:

$ gcc -Wall containerof.c
containerof.c: In function ‘main’:
containerof.c:20:26: warning: comparison of distinct pointer types lacks a cast
containerof.c:20:21: warning: unused variable ‘qc’
containerof.c:19:21: warning: unused variable ‘pc’

Мы получаем предупреждение distinct pointer types для 26, но не 25. Это наша диагностика о неправильном использовании указателей.

Сначала я попытался поставить проверку типа в левую сторону оператора запятой, gcc жалуется на то, что это не имеет никакого эффекта, что является неприятностью. Но, сделав его операндом, мы гарантируем, что он используется.

Трюк &((type *) 0)->member не совсем определен ISO C, но он широко используется для определения offsetof. Если ваш компилятор использует этот трюк с нулевым указателем для offsetof, он почти наверняка будет себя вести в вашем собственном макросе.

3
ответ дан Kaz 19 August 2018 в 05:30
поделиться
  • 1
    Поэтому, если последнее выражение не оптимизировано, программа получит доступ к памяти в 0 + offsetof(type, member) и, вероятно, segfault. Правильно? – Jo So 10 May 2017 в 10:57
  • 2
    &((type *) 0)->member - AFAIK, используя выражение deference для NULL-указателя, является UB, даже если он не создает фактический доступ к памяти. – anton_rh 31 May 2018 в 04:32
  • 3
  • 4
    ru.wikipedia.org/wiki/Offsetof : Хотя эта реализация корректно работает во многих компиляторах, она имеет неопределенное поведение в соответствии со стандартом C, поскольку она включает разыменование нулевого указателя (хотя , можно утверждать, что разыменование не происходит, потому что все выражение вычисляется во время компиляции). – anton_rh 31 May 2018 в 05:08
  • 5
    @anton_rh Если эта реализация offsetof действительно найдена в компиляторе, то она не может быть неопределенной. Если мы обнаружим, что компилятор использует этот трюк в собственном заголовочном файле, это более или менее легитимизирует использование этого трюка в другом месте (под этим компилятором). Если только он не контролирует такие виды использования, основанные на происхождении (из которого загорается макрос). – Kaz 31 May 2018 в 17:16
Другие вопросы по тегам:

Похожие вопросы: