Почему моя структура, созданная в функции, перезаписывается? - PullRequest
0 голосов
/ 18 ноября 2018

Как я могу создать структуры в функции?Я пытаюсь так:

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

typedef struct { char name[10]; } Item;

typedef struct { Item *item; } MyStruct;

MyStruct make(char *name) {
    MyStruct st;

    Item item;
    strcpy(item.name, name);
    st.item = &item;
    printf("name: %s\n", st.item->name);

    return st;
}

int main() {
    MyStruct s1, s2;
    s1 = make("hi");
    s2 = make("hey");

    printf("\nname: %s\n", s1.item->name);
    printf("name: %s\n", s2.item->name);
}

Вывод:

name: hi
name: hey

name: hey
name: hey

Структура s1 перезаписывается при создании s2.Похоже, что адрес структуры st в функции make одинаков при каждом вызове функции.Это правильно?Я хотел бы избежать использования malloc, если это возможно.

Редактировать : результат отличается на macOS и Linux, вывод выше из Linux, ниже - из macOS.Почему это отличается?

name: hi
name: hey

name: p
name: p

Ответы [ 3 ]

0 голосов
/ 18 ноября 2018

Когда вы объявляете Item item;, вы выделяете это локально, и оно выходит из области видимости.Следующий вызов функции по совпадению перезапускает ту же самую позицию в памяти.

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

0 голосов
/ 19 ноября 2018

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

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

typedef struct { char name[10]; } Item;

typedef struct { Item item; } MyStruct; // <--- Removed the *

MyStruct make(char *name) {
    MyStruct st;

    Item item;
    strncpy(item.name, name, 10);  // <--- strncpy because name[] is a fixed length
    st.item = item;                // <-- Removed the &; *copies* item into st
    printf("name: %s\n", st.item.name); // <-- . rather than ->

    return st;
}

int main() {
    MyStruct s1, s2;
    s1 = make("hi");
    s2 = make("hey");

    printf("\nname: %s\n", s1.item.name); // <-- . rather than ->
    printf("name: %s\n", s2.item.name);   // <-- . rather than ->
}

В этом коде я избавился от указателей, и все должно работать.Item - это 10 символов, а MyStruct - также 10 символов.Когда вы возвращаете его, эти 10 символов памяти копируются, как int.

Но вы этого не делали;Вы добавили указатели.Поскольку item является локальной переменной, она находится в текущем кадре стека, а не в куче.Кадр стека исчезает в конце текущей области.(Но это не на самом деле исчезает. Это просто неопределенное поведение, чтобы ссылаться на него из области видимости, поэтому он может возвращать что угодно.)

Выполнение этого без указателей означает больше копирования (хотя на самом деле это не так)это намного больше копирования, в 64-битной системе указатель составляет 8 байт).Это также означает, что изменения в одной копии не влияют на другие.Это либо хорошо, либо плохо в зависимости от вашей проблемы.Избавление от указателей таким способом может быть очень хорошим дизайном, если структура не становится слишком большой.После того, как вы добавите указатели, вам нужно будет управлять памятью и принимать решение о владении, а также о том, когда следует освободить память и все другие головные боли.От вашей проблемы зависит, какой подход лучше.

Как примечание, если вы идете этим путем, нет причин создавать промежуточное звено item (и это неэффективно, потому что он делает две копиистрока).Вместо этого вы просто скопируете прямо в st:

MyStruct make(char *name) {
    MyStruct st;

    strncpy(st.item.name, name, 10);
    printf("name: %s\n", st.item.name);

    return st;
}
0 голосов
/ 18 ноября 2018

MyStruct st; уничтожается, как только функция возвращается, так что это неопределенное поведение, поэтому вам придется использовать malloc и вернуть указатель на MyStruct.

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

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

Вот почему ваши данные перезаписываются при втором вызове. Итак, если вы хотите сохранить Item item, вам следует выделить it , а не возвращаемое значение вашей функции (хотя вы, конечно, тоже можете это сделать, но это не решит проблему, поскольку item будет уничтожен в любом случае), в куче с malloc.

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