Thanks for the help, Zed and Guus. As requested, I’ve posted my complete source code below. After reading Zed’s response I went back into ex17.c and replaced the malloc() call with calloc(1, sizeof(struct Connection)) but still get the same error message.
#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); //terminate program
}
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 = calloc(1, sizeof(struct Connection));
printf("sizeof calloc = %lu\n", sizeof(struct Connection));
printf("sizeof *conn = %lu\n", sizeof(conn));
if(!conn){
die("Memory error");
}
if(mode == 'c'){
conn->file = fopen(filename, "w");
printf("I wrote a file!\n");
}
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){
printf("TOP OF Database_write!!\n");
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;
printf("Does conn exist in Database_create()? sizeof Connection = %lu, sizeof conn->db->rows = %lu\n", sizeof(struct Connection), sizeof(conn->db->rows));
printf("Location of conn->db->rows = %p\n", conn->db->rows);
for(i = 0; i < MAX_ROWS; i++){
//make a prototype to initialize it
struct Address addr = {.id = i, .set = 0};
//then just assign it
/*printf("addr[%d] ; id=%d, set=%d i, addr.id, addr.set);*/
/*printf("conn->db->rows[i].id = %d\n\n", conn->db->rows[i].id);*/
conn->db->rows[i] = addr;
}
}
void Database_set(struct Connection *conn, int id, const char *name, const char *email){
struct Address *addr = &conn->db->rows[id];
if(addr->set){
die("Already set, delete it first.");
}
addr->set = 1;
//WARNING: bug, read the "How To Break It" and fix this
char *res = strncpy(addr->name, name, MAX_DATA);
//attempt to fix strncpy() bug
res[(MAX_DATA - 1)] = '\0';//always set the end of res[] to a null bit with \0
//demonstrate the strncpy bug
if(!res){
die("Name copy failed.");
}
res = strncpy(addr->email, email, MAX_DATA);
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);
printf("Does conn exist in main()? sizeof Connection = %lu, sizeof conn = %lu\n", sizeof(struct Connection), sizeof(conn));
printf("conn->db = %p\n", conn->db);
printf("conn->db (sizeof) = %lu\n", sizeof(conn->db));
printf("conn->db->rows (sizeof)= %lu\n", sizeof(conn->db->rows));
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: c=create, g=get, s=set, d=delete, l=list");
}
Database_close(conn);
return 0;
}
When I use lldb to debug I see the following info (breakpoint set at Database_create(), stepping into problematic line 133):
- thread #1: tid = 0x124c9, 0x000000010000164a ex17
Database_create(conn=0x00000001001005b0) + 186 at ex17.c:133, queue = 'com.apple.main-thread', stop reason = step in frame #0: 0x000000010000164a ex17
Database_create(conn=0x00000001001005b0) + 186 at ex17.c:133
130 /printf("addr[%d] ; id=%d, set=%d i, addr.id, addr.set);/
131
132 /printf(“conn->db->rows[i].id = %d\n\n”, conn->db->rows[i].id);/
-> 133 conn->db->rows[i] = addr;
134
135 }
136 }
(lldb) step
Process 1489 stopped
- thread #1: tid = 0x124c9, 0x00007fff8d62d5ea libsystem_platform.dylib
_platform_memmove$VARIANT$Nehalem + 138, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x00007fff8d62d5ea libsystem_platform.dylib
_platform_memmove$VARIANT$Nehalem + 138
libsystem_platform.dylib`_platform_memmove$VARIANT$Nehalem:
-> 0x7fff8d62d5ea <+138>: movups %xmm0, (%rax)
0x7fff8d62d5ed <+141>: movups 0x10(%rsi), %xmm2
0x7fff8d62d5f1 <+145>: movups 0x20(%rsi), %xmm3
0x7fff8d62d5f5 <+149>: movups 0x30(%rsi), %xmm4
I hope this helps, thank you!