Segmentation fault при выполнении сортировки qsort - C (СИ)
Формулировка задачи:
Пишу для изучения си программку, типа базы данных. Данные хранятся в массиве структур, и когда я делаю qsort для этого массива, происходит segmetation fault. Когда проверяю в gdb для массива из 2-х элементов функция сравнения вызывается почему-то два раза, и во второй раз последний элемент массива сравнивается с несуществующим. Собственно нужна подсказка, почему так происходит ?
Вот код:
Если что-то не понятно или нужно дополнить вопрос, пишите
p.s. под линукс с gcc компилируется без ошибок и ворнингов
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <stdbool.h> #include <stddef.h> typedef struct { int id; int set; char* name; char* email; } userdata; typedef struct { size_t max_rows; size_t max_strlen; userdata* rows; } database; typedef struct { FILE* file; database* db; char mode; } connection; bool id_too_big(size_t id, size_t max_row) { return id >= max_row; } bool is_legal_act(const char action) { return (action == 'c' || action == 's' || action == 'g' || action == 'l' || action == 'd' || action == 'f'); } void free_row(userdata* u) { if(u && u->set) { if(u->name) free(u->name); if(u->email) free(u->email); } } void free_db(database* db) { if(!db) return; if(!db->rows) return; for(size_t i = 0; i < db->max_rows; i++) { free_row(&db->rows[i]); } free(db->rows); free(db); } void connection_close(connection* c) { if(!c) return; if(c->file) fclose(c->file); free_db(c->db); free(c); } void die(const char* msg, connection *c) { connection_close(c); if(errno) { perror(msg); } else { printf("ERROR: %s\n", msg); } exit(EXIT_FAILURE); } void database_init(connection* c, size_t max_rows, size_t max_strlen) { c->db->max_rows = max_rows; c->db->max_strlen = max_strlen; c->db->rows = calloc(max_rows, sizeof(userdata)); for(size_t i = 0; i < max_rows; i++) { userdata *u = &c->db->rows[i]; u->id = i; u->set = 0; } } bool database_row_load(userdata *u, size_t str_len, FILE* f) { size_t rc = fread(u, sizeof(userdata), 1, f); if(rc != 1) return false; if(!u->set) return true; u->name = malloc(str_len); memset(u->name, 0, str_len); rc = fread(u->name, str_len, 1, f); if(rc != 1) return false; u->name[str_len-1] = '\0'; u->email = malloc(str_len); memset(u->email, 0, str_len); rc = fread(u->email, str_len, 1, f); if(rc != 1) return false; u->email[str_len-1] = '\0'; return true; } void database_load(connection* c) { size_t readed = fread(c->db, sizeof(database), 1, c->file); if(readed != 1) die("Unable to read file", c); database_init(c, c->db->max_rows, c->db->max_strlen); for(size_t i = 0; i < c->db->max_rows; i++) { if(!database_row_load(&c->db->rows[i], c->db->max_strlen, c->file)) die("Unable to read db content", c); } } bool database_row_save(userdata *u, size_t str_len, FILE* f) { size_t wc = fwrite(u, sizeof(userdata), 1 , f); if(wc != 1) return false; if(!u->set) return true; wc = fwrite(u->name, str_len, 1, f); if(wc != 1) return false; wc = fwrite(u->email, str_len, 1, f); if(wc != 1) return false; return true; } void database_save(connection *c) { c->file = freopen(NULL, "w", c->file); size_t written = fwrite(c->db, sizeof(database), 1, c->file); if(written != 1) die("Unable to save database", c); for(size_t i = 0; i < c->db->max_rows; i++) { if(!database_row_save(&c->db->rows[i], c->db->max_strlen, c->file)) die("Unable to save database", c); } } void database_row_print(userdata *row) { if(!row->set) return; printf("Row info:\tid: %2d name: %s email: %s\n", row->id, row->name, row->email); } void database_list(connection *c) { for(size_t i = 0; i < c->db->max_rows; i++) { database_row_print(&c->db->rows[i]); } } userdata* database_row_get(connection *c, size_t id) { if(id_too_big(id, c->db->max_rows)) die("No such ID in database", c); return &c->db->rows[id]; } void database_row_set(connection *c, size_t id, const char *name, const char *email) { if(id >= c->db->max_rows) die("No such ID in database", c); userdata *u = &c->db->rows[id]; if(u->set == 1) die("ID already exist. Delete it first",c); u->set = 1; u->name = malloc(c->db->max_strlen); strncpy(u->name, name, c->db->max_strlen); u->name[c->db->max_strlen-1] = '\0'; u->email = malloc(c->db->max_strlen); strncpy(u->email, email, c->db->max_strlen); u->email[c->db->max_strlen-1] = '\0'; if(!u->name || !u->email) die("Unable to set data", c); } connection* connection_open(const char* filename, const char mode) { if(!is_legal_act(mode)) die("Wrong action !", NULL); connection* c = malloc(sizeof(connection)); if(!c) { die("Memory error", c); } c->db = malloc(sizeof(database)); if(!c->db) die("Unable to initialise database", c); c->mode = mode; if(mode == 'c') { c->file = fopen(filename, "w"); } else { c->file = fopen(filename, "r+"); database_load(c); } return c; } void database_create(int argc, char *argv[], connection *c) { if(argc != 5) die("Wrong parameter list", c); size_t max_rows = atoi(argv[3]); size_t max_strlen = atoi(argv[4]); database_init(c, max_rows, max_strlen); database_save(c); printf("Database: %s, successfully created !\n", argv[1]); } void database_set(int argc, char *argv[], connection *c) { if(argc != 6) die("Wrong parameter list", c); size_t id = atoi(argv[3]); if(id_too_big(id, c->db->max_rows)) die("Too big ID", c); database_row_set(c, id, argv[4], argv[5]); database_save(c); } void database_get(int argc, char *argv[], connection *c) { if(argc != 4) die("Wrong parameter list", c); size_t id = atoi(argv[3]); database_row_print(database_row_get(c, id)); } void database_delete(int argc, char *argv[], connection *c) { if(argc != 4) die("Wrong parameter list", c); size_t id = atoi(argv[3]); if(id_too_big(id, c->db->max_rows)) die("Too big ID", c); userdata *u = &c->db->rows[id]; if(!u->set) die("Record is empty", c); free_row(u); u->set = 0; database_save(c); } bool field_exist(const char *fld) { return (strcmp(fld, "name") == 0 || (strcmp(fld, "email") == 0)); } int userdata_name_compare(const void* u1, const void* u2) { const userdata *tmp1 = (userdata *)u1; const userdata *tmp2 = (userdata *)u2; return strcmp(tmp1->name, tmp2->name); } userdata *database_row_find(connection *c, const char *search_field, const char *search_value) { if(!field_exist(search_field)) die("Field you trying to search not exist!", c); size_t rows_size = sizeof(userdata)*(c->db->max_rows); userdata *row; userdata k; if(strcmp(search_field, "name") == 0) { qsort(c->db->rows, c->db->max_rows, sizeof(userdata), userdata_name_compare); k.name = malloc(c->db->max_strlen); strncpy(k.name, search_value, c->db->max_strlen); k.name[c->db->max_strlen-1] = '\0'; row = bsearch(&k, c->db->rows, c->db->max_rows, rows_size, userdata_name_compare); free(k.name); } if(strcmp(search_field, "email") == 0) { } return row; } void database_find(int ac, char *av[], connection *c) { if(ac < 5) die("Search string example: db <db file> f <field_name> <value>", c); userdata *u = database_row_find(c, av[3], av[4]); if(!u) { printf("Row with \"%s\" like %s not found", av[3], av[4]); return; } database_row_print(u); } int main(int argc, char *argv[]) { if(argc < 3) die("Usage: db <db file> <action> [action params]", NULL); char action = argv[2][0]; connection *c = connection_open(argv[1],action); switch(action) { case 'c': database_create(argc, argv, c); break; case 's': database_set(argc, argv, c); break; case 'g': database_get(argc, argv, c); break; case 'l': database_list(c); break; case 'd': database_delete(argc, argv, c); break; case 'f': database_find(argc, argv, c); break; default: die("Available commands: c - create, s - set, g - get, l - list, f - find", c); break; } connection_close(c); return EXIT_SUCCESS; }
Решение задачи: «Segmentation fault при выполнении сортировки qsort»
textual
Листинг программы
free_row(u); u->set = 0;
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д