Некоторые вещи о структурах в C - которые я не понимаю - PullRequest
0 голосов
/ 15 декабря 2018

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

struct Date {
    int sec;
    int min;
    int hour;
};

struct Date d1={1,2,3};
struct Date d2={10,20,4};

Так что я знаю, что это действительно:

d1=d2;

Но это не так (потому что мы не можем назначить элементы структуры)

d1.sec=d2.sec

Но теперь мне интересно, а что, если элементы одной структуры - это другая структура, например:

struct DateTime {
struct Date d;
struct Time t;
};

struct DateTime dt1;
struct DateTime dt2;

Итак, если мы сделаем это:

dt1=dt2;

назначаем ли мы структуры (Дата и Время) из dt2 структурам (Дата и Время) в dt1?

Также сейчас (когдаэлементы одной структуры - это другие структуры) мы можем назначить элементы, как это:

dt1.d = dt2.d

1 Ответ

0 голосов
/ 15 декабря 2018

Совершенно нормально и обычно назначать struct поля друг другу.Вы не можете присвоить массиву, но struct s предоставляют своего рода исключение из этого, потому что назначение одного struct другому приводит к тому, что пункт назначения получает копию первого struct.В приведенном ниже коде вы не можете назначить copy_my_a.str = my_a.str;, потому что это поля массива.Вы могли бы назначить copy_my_a = my_a;, хотя.

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

Если struct содержит поле массива, этот массив копируется в место назначения struct.Поскольку это копия, она может быть изменена независимо от исходного массива.Но если struct содержит указатель на какое-то выделение (массив, динамическое выделение и т. Д.), То копируется указатель, а не содержимое выделения (т. Е. Массив и т. Д.).Обычно это называется мелкая копия (хотя это не термин, используемый в Стандарте C).

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

В приведенном ниже примере my_a имеет поле массива, а my_b имеет поле char *.my_a.str инициализируется для хранения строки ("my_a"), а my_b.str инициализируется указателем на первый элемент строки ("my_b").Копии каждого из двух struct создаются путем назначения.

В копии my_a, copy_my_a.str является копией my_a.str, поэтому весь массив был скопирован в новыйstruct.Изменение исходной строки здесь не повлияет на копию.Аналогично, в копии my_b, copy_my_b.str является копией my_b.str, но здесь это не скопированный массив, а только указатель.Изменение указанной строки будет видно в обоих struct s, поскольку они совместно используют один и тот же массив через указатель.

Это может сбить вас с толку, если вы работаете с копиями таких struct s и изменитеразделяемая строка через указатель без понимания того, что строка является общей, как в последнем примере в коде:

#include <stdio.h>

struct st {
    char c;
    int y;
};

struct exa {
    int x;
    struct st s;
    char str[100];
};

struct exb {
    int x;
    struct st s;
    char *str;
};

int main(void)
{
    // .str holds an array: can't assign an array to an array
    struct exa my_a = { .x = 1,
                        .s.c = 'x',
                        .s.y = 2,
                        .str = "my_a"
    };

    // .str holds a pointer to char: assign a pointer to arr[0]
    char arr[100] = "my_b";
    struct exb my_b = { .x = 1,
                        .s.c = 'x',
                        .s.y = 2,
                        .str = arr
    };

    struct exa copy_my_a = my_a;
    struct exb copy_my_b = my_b;

    puts("Nested structs are copied: ");
    printf("my_a.s.c:      %c\n", my_a.s.c);
    printf("copy_my_a.s.c: %c\n", copy_my_a.s.c);
    putchar('\n');

    puts("copy_my_a.str is a copy of my_a.str, which is an array:");
    printf("my_a.str:      %s\n", my_a.str);
    printf("copy_my_a.str: %s\n", copy_my_a.str);
    putchar('\n');

    puts("Changing the original array is not visible in the copy:");
    for (size_t i = 0; i < sizeof "changed"; i++) {
        my_a.str[i] = "changed"[i];
    }

    printf("my_a.str:      %s\n", my_a.str);
    printf("copy_my_a.str: %s\n", copy_my_a.str);
    putchar('\n');

    puts("copy_my_b.str is a copy of my_b.str, which is a pointer to arr[0]:");

    printf("my_b.str:      %s\n", my_b.str);
    printf("copy_my_b.str: %s\n", copy_my_b.str);
    putchar('\n');

    puts("Changing the original array is visible in both structs:");
    for (size_t i = 0; i < sizeof "changed"; i++) {
        arr[i] = "changed"[i];
    }

    printf("my_b.str:      %s\n", my_b.str);
    printf("copy_my_b.str: %s\n", copy_my_b.str);
    putchar('\n');

    puts("But changing the array through .str is also visible in both structs:");
    for (size_t i = 0; i < sizeof "oops!"; i++) {
        copy_my_b.str[i] = "oops!"[i];
    }

    printf("my_b.str:      %s\n", my_b.str);
    printf("copy_my_b.str: %s\n", copy_my_b.str);

    return 0;
}

Вывод программы:

Nested structs are copied: 
my_a.s.c:      x
copy_my_a.s.c: x

copy_my_a.str is a copy of my_a.str, which is an array:
my_a.str:      my_a
copy_my_a.str: my_a

Changing the original array is not visible in the copy:
my_a.str:      changed
copy_my_a.str: my_a

copy_my_b.str is a copy of my_b.str, which is a pointer to arr[0]:
my_b.str:      my_b
copy_my_b.str: my_b

Changing the original array is visible in both structs:
my_b.str:      changed
copy_my_b.str: changed

But changing the array through .str is also visible in both structs:
my_b.str:      oops!
copy_my_b.str: oops!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...