Segmentation fault при выполнении сортировки qsort - C (СИ)

Узнай цену своей работы

Формулировка задачи:

Пишу для изучения си программку, типа базы данных. Данные хранятся в массиве структур, и когда я делаю qsort для этого массива, происходит segmetation fault. Когда проверяю в gdb для массива из 2-х элементов функция сравнения вызывается почему-то два раза, и во второй раз последний элемент массива сравнивается с несуществующим. Собственно нужна подсказка, почему так происходит ? Вот код:
#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;
}
Если что-то не понятно или нужно дополнить вопрос, пишите p.s. под линукс с gcc компилируется без ошибок и ворнингов

Решение задачи: «Segmentation fault при выполнении сортировки qsort»

textual
Листинг программы
   free_row(u);
   u->set = 0;

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

7   голосов , оценка 4.143 из 5
Похожие ответы