C Путаница с мелкой копией

Редактировать: Исправлена ​​ошибка, которая упоминалась в комментариях, и проблема все еще существует, так что этот вопрос на самом деле не является дубликатом - только то, что мои навыки программирования на C не очень хороши на данный момент, решение, приведенное ниже, отвечает на вопрос и не 't исправить ошибку локальной переменной.

Предупреждение: довольно новый для C

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

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

typedef struct {
    uint8_t someVal;
} Type1

typedef struct {
    Type1 grid[3][3];
} Type2

//Constructor for Type2 "objects"
Type2 Type2_Constructor(void) {
    Type1 empty = {.value = o}
    Type2 newType2;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) {
            //Shallow copy empty struct
            newType2.grid[i][j] = empty;
        }
    return newType2;
}

int main (void) {    
    Type2 type2Object = Type2_Constructor();   
    for (int i = 0; i < 3; i ++) 
        for (int j = 0; j < 3; j++){
            printf("%u",type2OBject.grid[i][j].value);
            printf("\n\r%p\n\r",&(type2Object.grid[i][j].value));
            printf("\n\r");
        }   
    return 0;
}

Я ожидаю увидеть:

0
0xMemoryLocation

0
0xMemoryLocation

0
0xMemoryLocation

.
.
.

На самом деле, я вижу что-то вроде этого, где адрес увеличивается на 2 байта:

0
0x7fff57eaca18

0
0x7fff57eaca1a

0
0x7fff57eaca1c

0
0x7fff57eaca1e

0
0x7fff57eaca20

.
.
.

Так как мелкое копирование должно копировать адрес напрямую, почему &(type2Object.grid[i][j].value) отличаются?

Спасибо за помощь!

3 ответа

Решение

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

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


Принимая вашу структуру:

typedef struct {
    Type1 grid[3][3];
} Type2

Если у вас есть

Type2 a;
Type2 b = { ... };  // Some initialization, not relevant exactly what

затем назначение

a = b;   // Copy structure b to a

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

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

typedef struct {
    char *pointer;
} Type3;

Type3 a;
Type3 b = { "abcd" };

a = b;

printf("a.pointer = %s\n", a.pointer);
printf("b.pointer = %s\n", b.pointer);

Приведенный выше код будет печатать одну и ту же строку для обоих a.pointer а также b.pointer, Но это тот же указатель. Если мы добавим новую распечатку указателей:

printf("a.pointer = %p\n", (void *) a.pointer);
printf("b.pointer = %p\n", (void *) b.pointer);

Две вышеупомянутые строки после присвоения выдают одно и то же значение. Оба указателя будут указывать на одну и ту же память. Это мелкая копия.

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

memcpy(&a, &b, sizeof a);

На самом деле, я думаю, что ваша путаница заключается в том, что вы думаете, что ссылки на другую структуру похожи на ссылки в Java, C# или C++, а это не так.

Каждый элемент в grid массив в Type2 структура является уникальным примером Type1 состав. И как уникальные экземпляры, они все занимают разную память, что приводит к тому, что вы печатаете адреса, все разные.

Когда вы делаете

newType2.grid[i][j] = empty;

вы копируете содержимое empty экземпляр структуры, в экземпляр структуры newType2.grid[i][j], С использованием memcpy вызов, показанный выше, что назначение действительно

memcpy(&newType2.grid[i][j], &empty, sizeof newType2.grid[i][j]);

Это делает побитовую копию содержимого empty,


О разнице между указателем и массивом рассмотрим следующие определения:

int a[] = { 1, 2, 3, 4 };
int *p = a;

В памяти это будет выглядеть примерно так:

+ --- + --- + --- + --- +
| 1 | 2 | 3 | 4 |
+---+ --- + --- + --- +
^
|
+---+     
| р |
+---+

То есть массив и его содержимое занимают достаточно места для четырех int ценности. Тогда у вас есть указатель p который указывает на первый элемент в массиве (массивы распадаются на указатели на их первый элемент, в случае массива a выше, используя a как указатель равен &a[0]).

Вы, кажется, неправильно понимаете, что такое мелкая / глубокая копия. Мелкая копия будет копировать каждого члена (или делать побитовую копию, если хотите). Глубокая копия также будет копировать собственные ресурсы структуры.

Давайте начнем с простого примера:

struct X {
   int val;
};

X x1 = {24};
X x2 = {42};

x1 = x2;

Да, оператор встроенного оператора присваивания = выполняет мелкую копию. Это означает, что вышеизложенное эквивалентно:

x1.val = x2.val;

В этом случае как X не имеет внешних ресурсов, которыми он владеет, поэтому он также считается глубокой копией, потому что каждый x2 собственные копируется в x1, Это является следствием того факта, что X не владеет никакими внешними ресурсами.

Важность мелкой / глубокой копии вступает в игру, когда X будет владеть внешним ресурсом / Например:

struct X {
   int *my_very_own_external_value;
};

X x1, x2;
x1.my_very_own_external_value = malloc(sizeof(int));
*x1.my_very_own_external_value = 24;

x2.my_very_own_external_value = malloc(sizeof(int));
*x2.my_very_own_external_value = 42;

Мелкая копия будет такой:

x1 = x2;

Это было бы эквивалентно этому:

x1.my_very_own_external_value  = x2.my_very_own_external_value;

Что, если вы понимаете указатели, означает, что x1 потерял адрес выделенной памяти (кто держит 24) и теперь оба x1 а также x2 иметь указатели на память, выделенную для x2 то есть тот, который хранит 42;

Если вам нужна глубокая копия, вам нужно написать:

free(x1.my_very_own_external_value);
x1.my_very_own_external_value  = malloc(sizeof(int));
*x1.my_very_own_external_value  = *x2.my_very_own_external_value;

Теперь вы скопировали память (внешний ресурс), которая x2 владеет внешне.


отказ от ответственности: я мог бы написать в стиле C++

В вашем коде есть ошибка:

//Constructor for Type2 "objects"
Type2 * Type2_Constructor(void) {
    Type1 empty = ...;
    Type2 newType2;
    ... do something ...
    Type2 * newType2Ptr = &newType2;
    return newType2Ptr;
}

newType2 является локальной переменной - структурой, которая определена в стеке. Затем вы возвращаете его адрес вызывающей функции. Как только вы выйдете из функции Type2_Constructor, ее стек больше не будет в ваших руках.

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

Другие вопросы по тегам