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;
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д