动态大小的结构 – 学习C艰难的方式Ex17

我在学习C艰难的方式练习时遇到了麻烦。 该练习提供了一个简单的数据库程序,它具有固定的大小和行数。 您可以在下面看到构成数据库的结构。

#define MAX_DATA 512 #define MAX_ROWS 100 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; }; 

任务是更改代码以接受MAX_DATA的参数,MAX_ROWS将它们存储在数据库结构中,并将其写入文件,从而创建一个可以任意resize的数据库。

我理解如何接受来自用户的MAX_DATA和MAX_ROWS,作为命令行参数 – 在文件中定义的函数中。 一旦我有这些值,我不知道如何将它们存储在数据库结构中并写入文件。

感谢任何能够提供帮助的人。 您可以在此处找到其余代码: http : //c.learncodethehardway.org/book/ex17.html

一种方法是将数组更改为指针。 然后你可以写一个alloc_db函数,它将使用max_row和max_data值来分配所需的内存。

 struct Address { int id; int set; char* name; char* email; }; struct Database { struct Address* rows; unsigned int max_row; unsigned int max_data; }; struct Connection { FILE *file; struct Database *db; }; 

好吧,我设法最终使这个程序工作,我在下面总结。 我希望这可能有助于某人也坚持使用ex17。

首先,我删除了MAX_DATA和MAX_ROWS常量并更改了结构,如下所示:

 struct Address { int id; int set; char *name; char *email; }; struct Database { int max_data; int max_rows; struct Address **rows; }; struct Connection { FILE *file; struct Database *db; }; 

我将max_datamax_rows分配给max_data中的新变量,然后将它们写入文件。

 conn->db->max_data = max_data; conn->db->max_rows = max_rows; int rc = fwrite(&conn->db->max_data, sizeof(int), 1, conn->file); rc = fwrite(&conn->db->max_rows, sizeof(int), 1, conn->file); 

现在我可以运行我的程序并用conn->db->max_rowsconn->db->max_data替换MAX_ROWSMAX_DATA

 #include  #include  #include  #include  #include  struct Address { int id; int set; char* name; char* email; }; struct Database { int MAX_ROWS; int MAX_DATA; struct Address* rows; }; struct Connection { FILE* file; struct Database* db; }; void Database_close(struct Connection* conn) { if(conn) { for(size_t i = 0; i < conn->db->MAX_ROWS; i++) { struct Address* row = &conn->db->rows[i]; if(row->name) free(row->name); if(row->email) free(row->email); } if(conn->db->rows) free(conn->db->rows); if(conn->db) free(conn->db); if(conn->file) fclose(conn->file); free(conn); } } void die(char* message, struct Connection* conn) { if(errno) { perror(message); } else { printf("ERROR: %s\n", message); } Database_close(conn); exit(1); } void diec(const char* message) { if(errno) { perror(message); } else { printf("ERROR: %s\n", message); } printf("Something Wrong\n"); exit(1); } void Address_print(struct Address* addr) { printf("%d %s %s\n", addr->id, addr->name, addr->email); } void Database_load(struct Connection* conn) { assert(conn->db && conn->file); if(fread(conn->db, sizeof(struct Database), 1, conn->file) != 1) die("Failed to load database.", conn); conn->db->rows = (struct Address*)malloc(sizeof(struct Address) * conn->db->MAX_ROWS); for(size_t i = 0; i < conn->db->MAX_ROWS; i++) { struct Address* row = &conn->db->rows[i]; if(fread(&row->id, sizeof(int), 1, conn->file) != 1) die("Failed to load id.", conn); if(fread(&row->set, sizeof(int), 1, conn->file) != 1) die("Failed to load set.", conn); row->name = malloc(conn->db->MAX_DATA); row->email = malloc(conn->db->MAX_DATA); if(fread(row->name, conn->db->MAX_DATA, 1, conn->file) != 1) die("Faile to load name", conn); if(fread(row->email, conn->db->MAX_DATA, 1, conn->file) != 1) die("Faile to load email", conn); } } struct Connection* Database_open(const char* filename, char mode) { struct Connection* conn = malloc(sizeof(struct Connection)); if(!conn) die("Memory error", conn); conn->db = malloc(sizeof(struct Database)); if(!conn->db) die("Memory error", conn); if(mode == 'c') { conn->file = fopen(filename, "w"); } else { conn->file = fopen(filename, "r+"); if(conn->file) { Database_load(conn); } } if(!conn->file) die("Failed to open the file", conn); return conn; } void Database_create(struct Connection* conn) { printf("MAX_ROWS: "); scanf("%d", &conn->db->MAX_ROWS); if (conn->db->MAX_ROWS<=0) die("MAX_ROWS must be positive", conn); printf("MAX_DATA: "); scanf("%d", &conn->db->MAX_DATA); if (conn->db->MAX_DATA<=0) die("MAX_DATA must be positive", conn); conn->db->rows = (struct Address*)malloc(sizeof(struct Address)*conn->db->MAX_ROWS); for(size_t i = 0; i < conn->db->MAX_ROWS; i++) { char* a = (char*)malloc(conn->db->MAX_DATA); memset(a, 0, conn->db->MAX_DATA); char* b = (char*)malloc(conn->db->MAX_DATA); memset(b, 0, conn->db->MAX_DATA); struct Address addr = {.id = i, .set = 0, .name = a, .email = b}; conn->db->rows[i] = addr; } } void Database_write(struct Connection* conn) { rewind(conn->file); if(fwrite(conn->db, sizeof(struct Database), 1, conn->file) != 1) die("Failed to write database.", conn); for (size_t i = 0; i < conn->db->MAX_ROWS; i++) { if(fwrite(&((conn->db->rows[i]).id), sizeof(int), 1, conn->file) != 1) die("Failed to write id.", conn); if(fwrite(&((conn->db->rows[i]).set), sizeof(int), 1, conn->file) != 1) die("Failed to write set.", conn); if(fwrite((conn->db->rows[i]).name, conn->db->MAX_DATA, 1, conn->file) != 1) die("Failed to write name.", conn); if(fwrite((conn->db->rows[i]).email, conn->db->MAX_DATA, 1, conn->file) != 1) die("Failed to write email.", conn); } fflush(conn->file); } void Database_set(struct Connection* conn, int id, char* name, char* email) { struct Address* addr = &conn->db->rows[id]; if(addr->set) die("Already set, delete it first", conn); addr->set = 1; name[conn->db->MAX_DATA - 1] = '\0'; email[conn->db->MAX_DATA - 1] = '\0'; strncpy(addr->name, name, conn->db->MAX_DATA); strncpy(addr->email, email, conn->db->MAX_DATA); } void Database_get(struct Connection* conn, int id) { struct Address* addr = &conn->db->rows[id]; if(addr->set) { Address_print(addr); } else { die("ID is not set", conn); } } void Database_delete(struct Connection* conn, int id) { char* a = (char*)malloc(conn->db->MAX_DATA); char* b = (char*)malloc(conn->db->MAX_DATA); free(conn->db->rows[id].name); free(conn->db->rows[id].email); struct Address addr = {.id = id, .set = 0, .name = a, .email = b}; conn->db->rows[id] = addr; } void Database_list(struct Connection* conn) { for(size_t i = 0; i < conn->db->MAX_ROWS; i++) { if(conn->db->rows[i].set) { Address_print(&(conn->db->rows[i])); } } } void Database_find(struct Connection* conn, char* keyword) { int i=0; int count=0; while (i < conn->db->MAX_ROWS) { while ( idb->MAX_ROWS) { if(conn->db->rows[i].set==1){ if(strstr(conn->db->rows[i].name, keyword) != NULL || strstr(conn->db->rows[i].email, keyword) != NULL){ break; } } i++; } if(i >= conn->db->MAX_ROWS) break; Address_print(&(conn->db->rows[i])); count++; i++; } if (count==0) { printf("Try some other words\n"); } } int main(int argc, char* argv[]) { if(argc < 3) diec("USAGE: ex17   [action params]"); int id = 0; if(argc > 3) id = atoi(argv[3]); char* filename = argv[1]; char action = argv[2][0]; struct Connection* conn = Database_open(filename, action); switch(action) { case 'c': Database_create(conn); Database_write(conn); printf("\nDone\n"); break; case 'g': if(argc != 4) die("Need an id to get", conn); Database_get(conn, id); break; case 's': if(argc != 6) die("Need id, name, email to set", conn); Database_set(conn, id, argv[4], argv[5]); Database_write(conn); break; case 'd': if(argc != 4) die("Need id to delete", conn); Database_delete(conn, id); Database_write(conn); break; case 'l': Database_list(conn); break; case 'f': if(argc != 4) die("Need keyword to search.", conn); Database_find(conn, argv[3]); break; default: die("Invalid action, only: c=create, g=get, s=set, d=del, l=list, f=find", conn); } Database_close(conn); return 0; }