Структуры с членом массива - PullRequest
0 голосов
/ 14 декабря 2018

Я изучаю структуры и натолкнулся на какое-то недопонимание.Я написал пару примеров:

#include <stdio.h>

struct test{
    char field[16];
};


int main(int argc, char * argv[]){
    const char *string = "some string";
    struct test t1 = {.field = *string};
    struct test t2 = {.field = string};
    struct test t3 = {.field = "some string"};
    struct test t4 = {{'s', 'o', 'm', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0'}};


    printf("t1::field = %s\n", t1.field); //prints s
    printf("t2::field = %s\n", t2.field); //prints garbage character
    printf("t3::field = %s\n", t3.field); //prints some string
    printf("t4::field = %s\n", t4.field); //prints some string
}

Вывод:

t1::field = s
t2::field = 4
t3::field = some string
t4::field = some string

ideone

Другой пример -

#include <stdio.h>

struct test{
    char field[16];
};

int main(void) {
    const char *string = {'s', 'o', 'm', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0'};
    struct test t = {.field = *string};

    printf("t::field = %s\n", t.field); //segfault
}

Вывод пуст.

ideone

Поведение мне не совсем понятно.При выделении памяти массива мы можем принять его n-й элемент как arr[n], который расширяется до *(arr + n).Поэтому я ожидал, что мы инициализируем char field[16] некоторыми char *, и этого будет достаточно, чтобы рассматривать объект, на который указывает указатель, как первый элемент некоторого массива.

Но, судя по поведению, мое пониманиебыл совершенно неправНе могли бы вы дать краткое объяснение этого?

Ответы [ 3 ]

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

Расширить ответ Дэвида С. :

По любой причине C был разработан, чтобы иметь (в основном) одинаковый синтаксис для объявления, определения и использования переменных.Это вечный источник путаницы, и особенно это касается массивов.

Доступ к выделенной памяти можно получить как с помощью записи массива, так и записи указателя;фактически запись массива «разлагается» компилятором на запись указателя.

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

Два основных различия:

1) инициализация обоих типов принципиально различна

2)

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

  • массив больше похож на директиву компилятора.Пока память выделяется во время ее определения, нет отдельного места, где хранится адрес этой памяти.Вместо этого компилятор создает смещения памяти по указателю стека (базы) в машинном коде для любого доступа к массиву.При использовании чего-то вроде arr_name или & arr_name [0] создается временный указатель (в некотором регистре процессора), который можно использовать для доступа к массиву или для копирования в переменную-указатель, но сам по себе не является отдельной сущностью.Это то, что люди имеют в виду, когда говорят, что «массив распадается» на указатель при использовании таким образом.

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

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

В первой программе struct test t2 = {.field = string}; - ошибка, так как указатель не является допустимым инициализатором для массива char.

Во второй программе const char *string = {'s', 'o', 'm', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0'}; - ошибка, поскольку не может быть более 1 инициализатора.для указателя.

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

0 голосов
/ 14 декабря 2018
struct test{
    char field[16];
};

Содержит массив символов field с 16 символами.Массив может быть инициализирован или скопирован, но не может быть назначен .

Действительный

    struct test t1 = {.field = *string};

(инициализировано t1.field до string[0] (например, *(string + 0) или просто *string))

Недействительно

    struct test t2 = {.field = string};

(попыткичтобы назначить указатель на строковый литерал массиву, см. C11 Standard - 6.3.2.1 Другие операнды - L-значения, массивы и указатели функций (p3) )

Действительный

    struct test t3 = {.field = "some string"};

(использует строковый литерал для инициализации массива)

Допустимый

    struct test t4 = {{'s', 'o', 'm', 'e', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0'}};

(t4 совпадает с t3 за исключением того, что используется заключенный в скобки инициализатор и с именем-initializer .field опущен, что диктует заполнение элементов, начиная с первого, см. C11 Standard § 6.7.9 Инициализация (p19) )

Your "ДругойПример " терпит неудачу по той же причине t2 терпит неудачу.

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