Зачем использовать адрес первого элемента структуры, а не структуры?


Я только что наткнулся на еще одну базу кода на работе, где разработчики последовательно используют адрес первого элемента структур при копировании/сравнении / настройке, а не саму структуру. Вот простой пример.

сначала есть структура типа:

typedef struct {
    int a;
    int b;
} foo_t;

тогда есть функция, которая делает копию такой структуры:

void bar(foo_t *inp)
{
    foo_t l;
    ...
    memcpy(&l.a, &inp->a, sizeof(foo_t));
    ...
}

Я бы сам не стал писать звонок memcpy таким образом, и я начал с подозрения, что оригинал разработчики просто не совсем понимали указатели и структуры в C. Однако теперь я видел это в двух несвязанных кодовых базах, без общих разработчиков, поэтому я начинаю сомневаться в себе.

зачем нужно использовать этот стиль?

6 59

6 ответов:

никто не должен этого делать. Если вы переставляете элементы структуры, вы в беде.

вместо этого:

memcpy(&l.a, &inp->a, sizeof(foo_t));

вы можете сделать это:

memcpy(&l, inp, sizeof(foo_t));

хотя это может быть опасно и вводит в заблуждение, оба утверждения на самом деле делают то же самое, что и C гарантирует отсутствие заполнения перед первым элементом структуры.

но лучше всего просто скопировать объекты структуры с помощью простого оператора присваивания:

l = *inp;

почему нужно использовать этот стиль?

мое предположение: невежество или плохо дисциплина.

никто не. Если вас когда-нибудь переезжал a в структуре или вы вставили член(ы) перед ним, вы бы ввести память разбивая ошибку.

этот код небезопасен, потому что перестановка элементов структуры может привести к memcpy доступ за пределы структуры, если член a больше не является первым членом.

однако вполне возможно, что члены преднамеренно упорядочены в структуре, и программист хочет скопировать только их подмножество,начало С и до конца структуры. Если это так, то код можно сделать безопасным с помощью следующее изменение:

    memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));

теперь члены структуры могут быть переставлены в любом порядке и это memcpy никогда не выйдет за пределы.

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

другие уже заметили это; тот, который меня беспокоит, это:

struct Foo rgFoo [3];
struct Foo *pfoo = &rgFoo [0];

вместо

struct Foo *pfoo = rgfoo;

Почему deref массив по индексу, а затем снова взять адрес? Это уже адрес, единственное отличие состоит в том, что pfoo технически

struct Foo *const, 

не

struct Foo *.  

тем не менее, я видел первый все время.

на самом деле, есть один законный вариант использования для: построения иерархии классов.

при обработке структур как экземпляров класса первый член (т. е. смещение 0) обычно будет экземпляром супертипа... если супертип существует. Это позволяет простому приведению перемещаться между использованием подтипа и супертипа. Очень полезно.

на заметке Даррена Стоуна о намерении, это ожидается при выполнении OO в C язык.

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