Достаточно ли мелкого копирования для структур с char []? - PullRequest
4 голосов
/ 11 февраля 2010

У меня есть структура, содержащая символьные массивы без каких-либо других функций-членов. Я делаю операцию присваивания между двумя экземплярами этих структур. Если я не ошибаюсь, это делает мелкую копию. Безопасна ли мелкая копия в этом случае?

Я пробовал это в C ++, и это работало, но я просто хотел бы подтвердить, безопасно ли это поведение.

Ответы [ 2 ]

9 голосов
/ 11 февраля 2010

Если под «мелкой копией» вы подразумеваете, что после присвоения struct, содержащего массив, массив будет указывать на исходные данные struct, тогда: это невозможно. Каждый элемент массива должен быть скопирован в новый struct. «Мелкая копия» появляется на картинке, если в вашей структуре есть указатели. Если этого не произойдет, вы не сможете сделать поверхностную копию.

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

Рассмотрим:

#include <stdio.h>

struct data {
    char message[6];
};

int main(void)
{
    struct data d1 = { "Hello" };
    struct data d2 = d1; /* struct assignment, (almost) equivalent to
                            memcpy(&d2, &d1, sizeof d2) */

    /* Note that it's illegal to say d2.message = d1.message */

    d2.message[0] = 'h';
    printf("%s\n", d1.message);
    printf("%s\n", d2.message);
    return 0;
}

Выше будет напечатано:

Hello
hello

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct data {
    char *message;
};

int main(void)
{
    struct data d1, d2;
    char *str = malloc(6);
    if (str == NULL) {
        return 1;
    }
    strcpy(str, "Hello");
    d1.message = str;
    d2 = d1;

    d2.message[0] = 'h';
    printf("%s\n", d1.message);
    printf("%s\n", d2.message);
    free(str);
    return 0;
}

Выше будет напечатано:

hello
hello

Как правило, данные struct T d1, d2;, d2 = d1; эквивалентны memcpy(&d2, &d1, sizeof d2);, но если структура имеет заполнение, это может быть или не быть скопировано.

Редактировать : В C вы не можете назначить массивы . Дано:

int data[10] = { 0 };
int data_copy[10];

data_copy = data;

незаконно. Итак, как я сказал выше, если у вас есть массив в struct, присваивание структуре должно копировать данные по элементам в массиве. Вы не получите мелкую копию в этом случае: нет смысла применять термин «мелкая копия» к случаю, подобному этому.

2 голосов
/ 11 февраля 2010

Назначение структур выполняет членское назначение, а для массивов это означает назначение каждого элемента. (И это делается рекурсивно для «многомерных» массивов, которые на самом деле являются просто массивами массивов.)

Вы правы, что он делает мелкую копию, даже на массивах. (Я предполагаю, что вы не перегружены op = по отношению к C ++; если вы перегружаете его, вы можете делать все, что захотите.)

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

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

struct S {
  int n;
  int* p;
  int a[2];
  int* ap[2];
  int xy[2][2];
};

void f() {
  S c, d;

  c = d;
  // equivalent to:
  c.n = d.n;
  c.p = d.p;

  c.a[0] = d.a[0];  // S::a is similar to your situation, only using
  c.a[1] = d.a[1];  // int instead of char.

  c.ap[0] = d.ap[0];
  c.ap[1] = d.ap[1];
  c.xy[0][0] = d.xy[0][0];
  c.xy[0][1] = d.xy[0][1];
  c.xy[1][0] = d.xy[1][0];
  c.xy[1][1] = d.xy[1][1];
}

То, что я использовал выше, ничего не меняет в семантике, оно работает одинаково для массивов символов, копируя каждый символ. Это S :: ситуация в моем коде.

Обратите внимание, что p и ap копируются поверхностно (как и любой другой элемент). Если эти указатели «владеют» памятью, на которую они указывают, то это может быть небезопасно. («Безопасный» в вашем вопросе является расплывчатым и действительно зависит от того, что вы ожидаете и как вы справляетесь с ситуацией.)

Для интересного поворота рассмотрим boost :: shared_ptr и другие умные указатели в C ++. Их можно копировать поверхностно, даже если возможно глубокое копирование, и это все еще может быть безопасно.

...