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;