Hi again guys,
Thanks for the interesting discussion. I have a follow up question.
I'm using a modified version of the allocation function in the OP shown below. Sorry in advance for any weird indentations. The post box seems to hate my properly indented code (being pasted from a linux VM) and fixing it requires modifiying every line. Hopefully it's not too difficult to parse.
Code:
struct Connection *Database_open(const char *filename, char mode, int numrows, int stringsize)
{
struct Connection *conn = malloc(sizeof(struct Connection));
if(!conn) die("Memory error");
conn->db = malloc(sizeof(struct Database));
conn->db->numrows = numrows;
conn->db->stringsize = stringsize;
if(!conn->db) die("Memory error");
//Allocate memory for a set of Address pointers
conn->db->rows = malloc(sizeof(struct Address)*numrows);
if(!conn->db->rows) die("Memory error in row creation");
//Allocate the memory for each string field of the each Address struct and
//assign.
int i = 0;
for(i = 0; i < numrows; i++){
conn->db->rows[i].name = malloc(sizeof(char)*stringsize);
printf("The pointer to name is %p\n",conn->db->rows[i].name);
if(!conn->db->rows[i].name) die("Memory error in name field creation");
conn->db->rows[i].email = malloc(sizeof(char)*stringsize);
printf("The pointer to email is %p\n",conn->db->rows[i].email);
if(!conn->db->rows[i].email) die("Memory error in email field creation");
conn->db->rows[i].id = i;
conn->db->rows[i].set = 0;
}
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;
}
For the purposes of my question, we're not interested in the branch that calls Database_load. Assume that the function is always being called with "c" being the active case.
Database_open is called from the following main:
Code:
int main(int argc, char *argv[])
{
if(argc < 3) die("USAGE: ex17 <dbfile> <action> [action params]");
//Test constants
int numrows = 5;
int stringsize = 5;
char *filename = argv[1];
char action = argv[2][0];
struct Connection *conn = Database_open(filename, action, numrows, stringsize);
int id = 0;
if(argc > 3) id = atoi(argv[3]);
if(id >= numrows) 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;
}
I'm always calling this with the "c" argument. Now, for the source of the problem, the Database_write function:
Code:
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 db to database.\n");
int sc = fwrite(conn->db->rows, sizeof(struct Address)*conn->db->numrows,1,conn->file);
if(sc !=1) die("Failed to write rows to database.\n");
int i =0;
for(i = 0; conn->db->numrows; i++){
printf("Name addr:\t%p\n",conn->db->rows[i].name);
printf("Email addr:\t%p\n",conn->db->rows[i].email);
/*
int rowname = fwrite(conn->db->rows[i].name, (sizeof(char*)*(conn->db->stringsize)), 1, conn->file);
if(rowname != 1) die("Failed to write name row");
int rowemail = fwrite(conn->db->rows[i].email, (sizeof(char*)*(conn->db->stringsize)), 1, conn->file);
if(rowemail != 1) die("Failed to write email row");
*/
}
rc = fflush(conn->file);
if(rc == -1) die("Cannot flush database.");
}
I've replaced the functional code with print statements to see what was going on, because I was getting a segfault and it seemed that each fwrite operation was seeing the "conn->db->rows[i].email/name" pointer as being null.
When I run it with functional code active and valgrind I get a segfault and the notification that address 0x0 is not stack'd malloc'd or recently freed.
When I run it with print codes active, it goes into an infinite loop printing "Name addr: (nil)" etc.
Now, the truly confusing thing. In an earlier stage of development, I ran the main code without calling any of the switch stuff (commented out), it just went straight from Database_open to Database_close (shown below).
Code:
void Database_close(struct Connection *conn)
{
int i = 0;
if(conn) {
if(conn->file) fclose(conn->file);
if(conn->db->rows){
for(i=0;i<conn->db->numrows;i++){
if(conn->db->rows[i].name) free(conn->db->rows[i].name);
if(conn->db->rows[i].email) free(conn->db->rows[i].email);
}
free(conn->db->rows);
}
if(conn->db) free(conn->db);
free(conn);
}
}
Database_close had absolutely no trouble freeing the allocated memory, which would imply to me that it wasn't seeing nil pointers. So what does the write function see nil pointers (how is nil different from null).
Sorry for the big code bomb, does anyone have any insight as to why I'm getting segfault errors?