2013-04-08 123 views
0

我是C新手,现在正在学习结构。 这试图创建一个完整的小程序来管理数据库。分割错误(核心转储)

#include <stdio.h> 
#include <assert.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 

#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; 
}; 

void die(const char *message) 
{ 
    if(errno) { 
     perror(message); 
    } else { 
     printf("ERROR: %s\n", message); 
    } 

    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) 
{ 
    int rc = fread(conn->db, sizeof(struct Database), 1, conn->file); 
    if(rc != 1) die("Failed to load database."); 
} 

struct Connection *Database_open(const char *filename, char mode) 
{ 
    struct Connection *conn = malloc(sizeof(struct Connection)); 
    if(!conn) die("Memory error"); 

    conn->db = malloc(sizeof(struct Database)); 
    if(!conn->db) die("Memory error"); 

    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"); 

    return conn; 
} 

void Database_close(struct Connection *conn) 
{ 
    if(conn) { 
     if(conn->file) fclose(conn->file); 
     if(conn->db) free(conn->db); 
     free(conn); 
    } 
} 

void Database_write(struct Connection *conn) 
{ 
    rewind(conn->file); 

    int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file); 
    if(rc != 1) die("Failed to write database."); 

    rc = fflush(conn->file); 
    if(rc == -1) die("Cannot flush database."); 
} 

void Database_create(struct Connection *conn) 
{ 
    int i = 0; 

    for(i = 0; i < MAX_ROWS; i++) { 
     // make a prototype to initialize it 
     struct Address addr = {.id = i, .set = 0}; 
     // then just assign it 
     conn->db->rows[i] = addr; 
    } 
} 

void Database_set(struct Connection *conn, int id, const char *name, const char *email) 
{ 
    addr->set = 1; 

    char *res = strncpy(addr->name, name, MAX_DATA); 

    addr->name[MAX_DATA] = '\n'; 
    if(!res) die("Name copy failed"); 

    res = strncpy(addr->email, email, MAX_DATA); 
    addr->email[MAX_DATA] = '\n'; 
    if(!res) die("Email copy failed"); 

} 

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"); 
    } 
} 

void Database_delete(struct Connection *conn, int id) 
{ 
    struct Address addr = {.id = id, .set = 0}; 
    conn->db->rows[id] = addr; 
} 

void Database_list(struct Connection *conn) 
{ 
    int i = 0; 
    struct Database *db = conn->db; 

    for(i = 0; i < MAX_ROWS; i++) { 
     struct Address *cur = &db->rows[i]; 

     if(cur->set) { 
      Address_print(cur); 
     } 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    if(argc < 3) die("USAGE: ex17 <dbfile> <action> [action params]"); 

    char *filename = argv[1]; 
    char action = argv[2][0]; 
    struct Connection *conn = Database_open(filename, action); 
    int id = 0; 

    if(argc > 3) id = atoi(argv[3]); 
    if(id >= MAX_ROWS) die("There's not that many records."); 

    switch(action) { 
     case 'c': 
      Database_create(conn); 
      Database_write(conn); 
      break; 

     case 'g': 
      if(argc != 4) die("Need an id to get"); 

      Database_get(conn, id); 
      break; 

     case 's': 
      if(argc != 6) die("Need id, name, email to set"); 

      Database_set(conn, id, argv[4], argv[5]); 
      Database_write(conn); 
      break; 

     case 'd': 
      if(argc != 4) die("Need id to delete"); 

      Database_delete(conn, id); 
      Database_write(conn); 
      break; 

     case 'l': 
      Database_list(conn); 
      break; 
     default: 
      die("Invalid action, only: c=create, g=get, s=set, d=del, l=list"); 
    } 

    Database_close(conn); 

    return 0; 
} 

它应该是这样的:

$ make ex17 
cc -Wall -g ex17.c -o ex17 
$ ./ex17 db.dat c 
$ ./ex17 db.dat s 1 someone someemail 
$ 
$ ./ex17 db.dat l 
1 someone someemail 

但我收到此错误消息:分段故障(核心转储)。

$ make ex17 
cc -Wall -g ex17.c -o ex17 
$ ./ex17 db.dat c 
$ ./ex17 db.dat s 1 someone someemail 
Segmentation fault (core dumped) 

我想我在“Database_set”中出了问题。我想解决这个问题,但我不能。我想知道我做错了什么。感谢有关此错误的任何帮助。

+6

尝试将问题追踪到代码的特定区域并发布最简单的示例。这不是一个用于调试整个代码的站点,但可以帮助您解决特定的问题。 – nemo 2013-04-08 03:26:45

+2

'addr-> name [MAX_DATA] ='\ n';'最有可能是最后一个。 – chris 2013-04-08 03:27:45

+1

这不会编译:'main.c:106 col 5:错误:'addr'未声明(首次在此函数中使用)' – 2013-04-08 03:31:44

回答

1

在行

void Database_set(struct Connection *conn, int id, const char *name, const char *email) 
{ 
    addr->set = 1; 

你在哪里定义addr?我甚至不知道如何编译...

大概你需要保持在你的数据库中有效记录的数量,并指出addr第一个未使用的记录,当你添加一个新的。这需要更新数据库结构以及用于添加和删除的功能。或者,您可以通过数据库循环访问第一个不是set的地址。无论哪种方式,你的函数需要声明addr并将其设置为有用之前使用它作为指针。

的下面几行代码可以帮助(这将是Database_set函数的第一行):

struct Address *addr; 
int ii=0; 
while(conn->db->rows[ii].set) ii++; 
addr = conn->db->rows + ii; 
addr->set = 1; 
addr->id = id; 

您还需要使由craig65535所示的变化。你的代码很可能存在其他问题,但是通过这些添加,我可以执行你在你的问题中给出的指示;它编译,它运行,它不抱怨。这是一个开始。 噢 - 它能够与l命令列出数据库...

0

假设addrstruct Address *型的,这是完全错误的:

addr->name[MAX_DATA] = '\n'; 

addr->名称仅包含MAX_DATA字节,所以您可以写入的最大索引是addr->name[MAX_DATA-1]。此外,您还希望该字符串是空终止的,所以你想:

addr->name[MAX_DATA-2] = '\n'; 
addr->name[MAX_DATA-1] = '\0'; 

这同样适用于addr->email

+0

它必须是'struct Address *'类型,因为它使用' - >'而不是'。' – Floris 2013-04-08 03:51:17

+0

注意到并更正了 – craig65535 2013-04-08 03:57:56