Как поменять местами части двух объектов, используя memcpy? - PullRequest
2 голосов
/ 23 марта 2011

У меня есть два объекта одного типа, например:

typedef struct s_Object 
{
  signed int a[3]; 
  double float b;
  unsigned short int c[30];
  struct s_Object *next, 
                  *prev;
} t_Object;
t_Object A, B;

Мне нужна общая функция, например:

swap_vars(&A, &B);

, который не будет менять указатели, но все другие данные могут содержать объекты. Возможно ли это в C?

Предполагая, что указатели расположены по самым высоким адресам, моя первая идея была:

void swap_vars(t_Object *pA, t_Object *pB){
   t_Object C;
   memcpy(&C, pA, sizeof(t_Object)-sizeof(t_Object *)*2;
   memcpy(pA, pB, sizeof(t_Object)-sizeof(t_Object *)*2;
   memcpy(pB, &C, sizeof(t_Object)-sizeof(t_Object *)*2;
}

но я не знаю, насколько она портативна (или, возможно, вообще не так)?

Я ищу самое портативное решение.

1 Ответ

3 голосов
/ 23 марта 2011

Использование offsetof:

memcpy(&C, pA, offsetof(t_Object, next));

Причина в том, что t_Object может иметь заполнение в конце, поэтому, если вы копируете все, кроме последних 8 байтов, вы можете копировать часть или всеуказатели.

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

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

На практике это довольно переносимо.В частности, элементы должны появляться в том порядке, в котором они определены, поэтому offsetof(t_Object, next) определенно захватывает часть объекта вплоть до (но не включая) указателей.

Если вы можете изменитьструктура, тогда вы могли бы построить его из замененной части, а также не замененной части:

typedef struct payload {
    signed int a[3]; 
    double float b;
    unsigned short int c[30];
} payload;

typedef struct s_Object {
    payload data;
    struct s_Object *next, *prev;
} t_Object;

Тогда поменять местами:

payload C = pA->data;
pA->data = pB->data;
pB->data = C;

Редактировать: подсказка memcpy против назначенияв C - должно быть memmove?

Последний также имеет то преимущество, что по стандарту гарантируется работа при pA == pB.Тот, у которого memcpy нет, потому что для второго экземпляра регионы перекрываются.Ни один из них не является оптимальным в случае самостоятельной замены, но, вообще говоря, они, вероятно, настолько редки, что не стоит проверять указатели, если это не требуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...