Динамически изменяемые массивы в языке Си
Я пытаюсь выучить C, используя http://c.learncodethehardway.org / но я застрял на одном из дополнительных кредитных вопросов в главе 18 (http://c.learncodethehardway.org/book/learn-c-the-hard-waych18.html и я надеюсь, что кто-нибудь сможет мне помочь.
Специфическая проблема, с которой я сталкиваюсь, заключается в том, что есть несколько структур, определенных следующим образом:
#define MAX_ROWS = 500;
#define MAX_DATA = 512;
struct Address {
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
struct Database {
struct Address rows[MAX_ROWS];
};
struct Connection {
FILE *file;
struct Database *db;
};
Задача состоит в том, чтобы переработать это так, чтобы rows мог иметь переменный размер, который не зависит от этой константы.
Итак в моем методе Database_create я пытаюсь инициализировать rows следующим образом:
conn->db->rows = (struct Address*) malloc(max_rows * sizeof(struct Address));
Где conn->db указывает на экземпляр базы данных, а max_rows - это int, который передается функции.
Я также изменил структуру базы данных на
struct Database{
struct Address* rows;
}
Этот бит кода, кажется, работает нормально, но если я пытаюсь получить доступ к любому члену rows, я получаю ошибку сегментации, которая, по моему мнению, означает, что я пытаюсь получить доступ к битам памяти, которые не используются.
Я потратил на это добрых несколько часов. это, и я уверен, что не могу быть слишком далеко, но я действительно был бы признателен за любое руководство, чтобы направить меня на правильный путь.
EDIT: просто хотел добавить некоторые дополнительные детали к этому после запуска его с Valgrind, что вызывает ошибку:
==11972== Invalid read of size 4
==11972== at 0x100001578: Database_set (ex18.c:107)
==11972== by 0x100001A2F: main (ex18.c:175)
==11972== Address 0x7febac00140c is not stack'd, malloc'd or (recently) free'd
Строка кода, на которую он указывает:
struct Address *addr = &conn->db->rows[id];
if(addr->set) die("Already set, delete it first");
Строка 107-это if(addr->set), что, как я предполагаю, означает, что он пытается прочитать то, что не может
3 ответа:
Когда вы загружаете
void Database_load(struct Connection *conn) { int rc = fread(conn->db, sizeof(struct Database), 1, conn->file); if(rc != 1) die("Failed to load database"); }Или запись базы данных,
void Database_write(struct Connection *conn) { rewind(conn->file); int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file); if(rc != 1) die("Failed to write database"); rc = fflush(conn->file); if(rc == -1) die("Cannot flush database."); }Вы не читаете и не записываете содержимое (
struct Addresses), а только указатель на место в памяти. При чтении ранее записанной базы данных этот указатель не указывает на что-либо конкретное, это дикий указатель. Тогда, конечно, попытка разыменования очень вероятно вызовет ошибку сегментации, если, возможно, это не так, вы получите бессмысленные псевдоданные.Если вы измените свой
struct Databaseтак чтоrowsявляетсяstruct Address*, вам нужно сохранить количество элементов и изменить код чтения и записи, чтобы также обрабатывать данные, на которые указываетrows. Сначала напишите, сколько элементов у вас есть, а затем напишите остальные (max_data,max_rowsа при чтении считывайте, сколько у вас предметов, выделяйте для них место, читайтеmax_dataиmax_rowsи предметы.
Вы хотите
sizeof(struct Address)неsizeof(struct Address*)
sizeof(struct Address*)скорее всего, возвращается размер 4 (полностью зависит от целевой платформы), тогда как фактический размер структурыAddressближе к 1040 (предполагая 1 байт на char и 4 на int)
EDIT : Ну, похоже (с вашим последним редактированием), вы действительно делаете эту часть правильно.
На самом деле вы не выделяете достаточный размер для адресных структур. Вам нужно что-то вроде этого:
struct Database{ struct Address** rows; } //create array of pointers to Address structures // (each element has size of the pointer) conn->db->rows = (struct Address**) malloc(max_rows * sizeof(struct Address*)); for(int i=0; i < max_rows; i++) { conn->db->rows[i] = (struct Address*) malloc(sizeof(struct Address)); }Или вот это:
struct Database{ struct Address* rows; } //create array of Address structures // (each element has size of the structure) conn->db->rows = (struct Address*) malloc(max_rows * sizeof(struct Address));