Копирование одной структуры в другую - PullRequest
51 голосов
/ 08 февраля 2011

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

Желательно ли это сделать?

В моей структуре у меня также есть строка, которую я должен скопировать в другую структуру, имеющую такой же член. Как мне это сделать?

Ответы [ 7 ]

62 голосов
/ 08 февраля 2011

Лучше всего копировать с помощью простого присваивания, поскольку оно короче, проще для чтения и имеет более высокий уровень абстракции. Вместо того чтобы сказать (человеку, читающему код) «скопировать эти биты отсюда туда» и попросить читателя подумать об аргументе размера для копии, вы просто делаете простое присваивание («скопируйте это значение из здесь, чтобы здесь "). Там не может быть никаких сомнений относительно того, правильный размер или нет.

Кроме того, если структура сильно заполнена, присваивание может заставить компилятор генерировать что-то более эффективное, поскольку ему не нужно копировать заполнение (и оно знает, где оно), но mempcy() не делает этого всегда будет копировать точное количество байтов, которые вы указали для копирования.

Если ваша строка является фактическим массивом, то есть ::1006*

struct {
  char string[32];
  size_t len;
} a, b;

strcpy(a.string, "hello");
a.len = strlen(a.string);

Тогда вы все еще можете использовать простое назначение:

b = a;

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

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

Для этих ситуаций «глубокая копия» действительно является единственным выбором, и для этого необходимо использовать функцию.

38 голосов
/ 08 февраля 2011

Начиная с C90, вы можете просто использовать:

dest_struct = source_struct;

, пока строка запоминается в массиве:

struct xxx {
    char theString[100];
};

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

struct xxx {
    char* theString;
};

dest_struct = source_struct;
dest_struct.theString = malloc(strlen(source_struct.theString) + 1);
strcpy(dest_struct.theString, source_struct.theString);
16 голосов
/ 08 февраля 2011

Если структуры имеют совместимые типы, да, вы можете, с чем-то вроде:

memcpy (dest_struct, source_struct, sizeof (*dest_struct));

Единственное, что вам нужно знать, это то, что это мелкая копия.Другими словами, если у вас есть char *, указывающий на конкретную строку, обе структуры будут указывать на одну и ту же строку.

И изменение содержимого одного из этих строковых полей (данные, на которые указывает char *, а не сам char *), также изменят другие.

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

memcpy (dest_struct, source_struct, sizeof (*dest_struct));
dest_struct->strptr = strdup (source_struct->strptr);

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

И, если ваша реализация C не имеет strdup (это не является частью стандарта ISO), получите его здесь .

5 голосов
/ 08 февраля 2011

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

struct {int a, b;} c, d;
c.a = c.b = 10;
d = c;
1 голос
/ 03 октября 2018

В C memcpy только глупо рискованно. До тех пор, пока вы получите все три параметра абсолютно правильно, ни один из членов структуры не является указателем (или вы явно намереваетесь делать поверхностную копию), и в структуре нет больших пробелов выравнивания, из-за которых memcpy будет тратить время на цикл (или производительность никогда не имеет значения), то, конечно, memcpy. Вы не получаете ничего, кроме кода, который труднее читать, хрупок для будущих изменений и должен быть проверен вручную в обзорах кода (потому что компилятор не может), но эй, да, конечно, почему бы и нет.

В C ++ мы продвигаемся до нелепо рискованного. У вас могут быть члены типов, которые не являются безопасными для memcpyable, такие как std :: string, что приведет к тому, что получающая структура станет опасным оружием, случайным образом повреждая память при каждом использовании. Вы можете получить сюрпризы, связанные с виртуальными функциями при эмуляции слайс-копий. Оптимизатор, который может сделать для вас удивительные вещи, потому что он гарантирует полное знание типов при компиляции =, ничего не может сделать для вашего вызова memcpy.

В C ++ есть эмпирическое правило - если вы видите memcpy или memset, что-то не так. Есть редкие случаи, когда это не так, но они не связаны со структурами. Вы используете memcpy тогда и только тогда, когда у вас есть причина вслепую копировать байт .

Назначение, с другой стороны, легко читается, проверяет правильность во время компиляции и затем интеллектуально перемещает значения во время выполнения. Недостатков нет.

0 голосов
/ 21 сентября 2018

Вы можете использовать следующее решение для достижения своей цели:

struct student 
{
    char name[20];
    char country[20];
};
void main()
{
    struct student S={"Wolverine","America"};
    struct student X;
    X=S;
    printf("%s%s",X.name,X.country);
}
0 голосов
/ 09 августа 2016
  1. Вы можете использовать структуру для чтения записи в файл. Вам не нужно разыгрывать его как `char *. Размер структуры также будет сохранен. (Этот пункт не ближе всего к теме, но угадайте это: поведение на жесткой памяти часто аналогично оперативной памяти.)

  2. Чтобы переместить (в / из) одно строковое поле, вы должны использовать strncpy и временный буфер строки '\0' завершается. Где-то вы должны запомнить длину строкового поля записи.

  3. Для перемещения других полей вы можете использовать точечную запись, например: NodeB->one=intvar; floatvar2=(NodeA->insidebisnode_subvar).myfl;

    struct mynode {
        int one;
        int two;
        char txt3[3];
        struct{char txt2[6];}txt2fi;
        struct insidenode{
            char txt[8];
            long int myl;
            void * mypointer;
            size_t myst;
            long long myll;
             } insidenode_subvar;
        struct insidebisnode{
            float myfl;
             } insidebisnode_subvar;
    } mynode_subvar;
    
    typedef struct mynode* Node;
    
    ...(main)
    Node NodeA=malloc...
    Node NodeB=malloc...
    
  4. Вы можете встроить каждую строку в соответствующие ей структуры, уклониться от пункта-2 и вести себя как Кобол NodeB->txt2fi=NodeA->txt2fi ... но вам все еще понадобится временная строка плюс один strncpy, как указано в пункте 2 для scanf, printf в противном случае оператор длиннее ввода (короче), не было бы усечено (пробелами).

  5. (NodeB->insidenode_subvar).mypointer=(NodeA->insidenode_subvar).mypointer создаст псевдоним указателя.
  6. NodeB.txt3=NodeA.txt3 заставляет компилятор отклонить: error: incompatible types when assigning to type ‘char[3]’ from type ‘char *’
  7. point-4 работает только потому, что NodeB->txt2fi & NodeA->txt2fi принадлежат одному и тому же typedef !!

    Правильный и простой ответ на эту тему я нашел на В C, почему я не могу присвоить строку массиву char после ее объявления? «Массивы (также с символами) являются гражданами второго сорта в C» !!!

...