Project 3: Address Book Program
Project 3: Address Book Program¶
Learning Objectives¶
What you will learn through this project: - Struct definition and usage - Struct arrays - File I/O (fopen, fwrite, fread, fprintf, fscanf) - CRUD functionality implementation (Create, Read, Update, Delete)
Program Requirements¶
1. Add contact
2. View contact list
3. Search contacts
4. Edit contact
5. Delete contact
6. Save/Load to file
Step 1: Struct Definition¶
Core Syntax: Structs¶
#include <stdio.h>
#include <string.h>
// Contact struct definition
typedef struct {
int id;
char name[50];
char phone[20];
char email[50];
} Contact;
int main(void) {
// Struct variable declaration
Contact c1;
// Assign values to members
c1.id = 1;
strcpy(c1.name, "John Doe");
strcpy(c1.phone, "010-1234-5678");
strcpy(c1.email, "john@email.com");
// Output
printf("ID: %d\n", c1.id);
printf("Name: %s\n", c1.name);
printf("Phone: %s\n", c1.phone);
printf("Email: %s\n", c1.email);
// Declaration with initialization
Contact c2 = {2, "Jane Smith", "010-9876-5432", "jane@email.com"};
printf("\n%s: %s\n", c2.name, c2.phone);
return 0;
}
Struct Arrays¶
#define MAX_CONTACTS 100
Contact contacts[MAX_CONTACTS];
int contact_count = 0;
// Add
contacts[contact_count].id = contact_count + 1;
strcpy(contacts[contact_count].name, "New Contact");
contact_count++;
// Iterate
for (int i = 0; i < contact_count; i++) {
printf("%s\n", contacts[i].name);
}
Step 2: Basic CRUD Implementation¶
// addressbook_v1.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_CONTACTS 100
#define NAME_LEN 50
#define PHONE_LEN 20
#define EMAIL_LEN 50
// Contact struct
typedef struct {
int id;
char name[NAME_LEN];
char phone[PHONE_LEN];
char email[EMAIL_LEN];
} Contact;
// Global variables
Contact contacts[MAX_CONTACTS];
int contact_count = 0;
int next_id = 1;
// Function declarations
void print_menu(void);
void add_contact(void);
void list_contacts(void);
void search_contact(void);
void delete_contact(void);
void clear_input_buffer(void);
int main(void) {
int choice;
printf("=== Address Book v1 ===\n");
while (1) {
print_menu();
printf("Choice: ");
if (scanf("%d", &choice) != 1) {
clear_input_buffer();
continue;
}
clear_input_buffer();
switch (choice) {
case 1:
add_contact();
break;
case 2:
list_contacts();
break;
case 3:
search_contact();
break;
case 4:
delete_contact();
break;
case 0:
printf("Exiting program.\n");
return 0;
default:
printf("Invalid choice.\n");
}
printf("\n");
}
return 0;
}
void print_menu(void) {
printf("\n");
printf("--------------------\n");
printf("| 1. Add Contact |\n");
printf("| 2. View List |\n");
printf("| 3. Search |\n");
printf("| 4. Delete |\n");
printf("| 0. Exit |\n");
printf("--------------------\n");
}
void add_contact(void) {
if (contact_count >= MAX_CONTACTS) {
printf("Address book is full.\n");
return;
}
Contact *c = &contacts[contact_count];
c->id = next_id++;
printf("\n[ Add New Contact ]\n");
printf("Name: ");
fgets(c->name, NAME_LEN, stdin);
c->name[strcspn(c->name, "\n")] = '\0'; // Remove newline
printf("Phone: ");
fgets(c->phone, PHONE_LEN, stdin);
c->phone[strcspn(c->phone, "\n")] = '\0';
printf("Email: ");
fgets(c->email, EMAIL_LEN, stdin);
c->email[strcspn(c->email, "\n")] = '\0';
contact_count++;
printf("\nContact added. (ID: %d)\n", c->id);
}
void list_contacts(void) {
printf("\n[ Contact List ] (Total: %d)\n", contact_count);
printf("----------------------------------------\n");
if (contact_count == 0) {
printf("No saved contacts.\n");
return;
}
for (int i = 0; i < contact_count; i++) {
Contact *c = &contacts[i];
printf("ID: %d\n", c->id);
printf(" Name: %s\n", c->name);
printf(" Phone: %s\n", c->phone);
printf(" Email: %s\n", c->email);
printf("----------------------------------------\n");
}
}
void search_contact(void) {
char keyword[NAME_LEN];
int found = 0;
printf("\n[ Search Contact ]\n");
printf("Search term (name): ");
fgets(keyword, NAME_LEN, stdin);
keyword[strcspn(keyword, "\n")] = '\0';
printf("\nSearch Results:\n");
printf("----------------------------------------\n");
for (int i = 0; i < contact_count; i++) {
// Substring search (strstr)
if (strstr(contacts[i].name, keyword) != NULL) {
Contact *c = &contacts[i];
printf("ID: %d | %s | %s\n", c->id, c->name, c->phone);
found++;
}
}
if (found == 0) {
printf("No results found.\n");
} else {
printf("\n%d result(s) found\n", found);
}
}
void delete_contact(void) {
int id, found = -1;
printf("\n[ Delete Contact ]\n");
printf("ID to delete: ");
scanf("%d", &id);
clear_input_buffer();
// Find by ID
for (int i = 0; i < contact_count; i++) {
if (contacts[i].id == id) {
found = i;
break;
}
}
if (found == -1) {
printf("Contact with that ID not found.\n");
return;
}
// Confirmation
printf("Delete '%s'? (y/n): ", contacts[found].name);
char confirm;
scanf(" %c", &confirm);
clear_input_buffer();
if (confirm != 'y' && confirm != 'Y') {
printf("Deletion cancelled.\n");
return;
}
// Delete: shift elements forward
for (int i = found; i < contact_count - 1; i++) {
contacts[i] = contacts[i + 1];
}
contact_count--;
printf("Contact deleted.\n");
}
void clear_input_buffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
Step 3: Adding File Save/Load¶
Core Syntax: File I/O¶
#include <stdio.h>
// Text file writing
FILE *fp = fopen("data.txt", "w");
if (fp == NULL) {
printf("Failed to open file\n");
return;
}
fprintf(fp, "Hello, File!\n");
fprintf(fp, "Number: %d\n", 42);
fclose(fp);
// Text file reading
FILE *fp = fopen("data.txt", "r");
char line[100];
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line);
}
fclose(fp);
// Binary file writing (useful for struct storage)
FILE *fp = fopen("data.bin", "wb");
Contact c = {1, "John Doe", "010-1234", "john@test.com"};
fwrite(&c, sizeof(Contact), 1, fp);
fclose(fp);
// Binary file reading
FILE *fp = fopen("data.bin", "rb");
Contact c;
fread(&c, sizeof(Contact), 1, fp);
fclose(fp);
File Modes¶
| Mode | Description |
|---|---|
"r" |
Read (fails if file doesn't exist) |
"w" |
Write (overwrites file) |
"a" |
Append (adds to end of file) |
"rb" |
Binary read |
"wb" |
Binary write |
Step 4: Final Version¶
// addressbook.c (Final)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_CONTACTS 100
#define NAME_LEN 50
#define PHONE_LEN 20
#define EMAIL_LEN 50
#define FILENAME "contacts.dat"
// Contact struct
typedef struct {
int id;
char name[NAME_LEN];
char phone[PHONE_LEN];
char email[EMAIL_LEN];
} Contact;
// Address book struct
typedef struct {
Contact contacts[MAX_CONTACTS];
int count;
int next_id;
} AddressBook;
// Function declarations
void init_addressbook(AddressBook *ab);
void print_menu(void);
void add_contact(AddressBook *ab);
void list_contacts(AddressBook *ab);
void search_contact(AddressBook *ab);
void edit_contact(AddressBook *ab);
void delete_contact(AddressBook *ab);
int save_to_file(AddressBook *ab);
int load_from_file(AddressBook *ab);
void clear_input_buffer(void);
int find_by_id(AddressBook *ab, int id);
int main(void) {
AddressBook ab;
int choice;
init_addressbook(&ab);
// Load data from file
if (load_from_file(&ab) == 0) {
printf("Loaded existing data. (%d contacts)\n", ab.count);
}
printf("\n===============================\n");
printf("| Address Book |\n");
printf("===============================\n");
while (1) {
print_menu();
printf("Choice: ");
if (scanf("%d", &choice) != 1) {
clear_input_buffer();
printf("Please enter a number.\n");
continue;
}
clear_input_buffer();
switch (choice) {
case 1:
add_contact(&ab);
break;
case 2:
list_contacts(&ab);
break;
case 3:
search_contact(&ab);
break;
case 4:
edit_contact(&ab);
break;
case 5:
delete_contact(&ab);
break;
case 6:
if (save_to_file(&ab) == 0) {
printf("Saved to file.\n");
}
break;
case 0:
// Save confirmation before exit
printf("Save changes? (y/n): ");
char save_confirm;
scanf(" %c", &save_confirm);
if (save_confirm == 'y' || save_confirm == 'Y') {
save_to_file(&ab);
printf("Save complete.\n");
}
printf("Exiting program.\n");
return 0;
default:
printf("Invalid choice.\n");
}
printf("\n");
}
return 0;
}
void init_addressbook(AddressBook *ab) {
ab->count = 0;
ab->next_id = 1;
memset(ab->contacts, 0, sizeof(ab->contacts));
}
void print_menu(void) {
printf("\n-------------------------\n");
printf("| 1. Add Contact |\n");
printf("| 2. View List |\n");
printf("| 3. Search |\n");
printf("| 4. Edit |\n");
printf("| 5. Delete |\n");
printf("| 6. Save to File |\n");
printf("| 0. Exit |\n");
printf("-------------------------\n");
}
void add_contact(AddressBook *ab) {
if (ab->count >= MAX_CONTACTS) {
printf("Address book is full. (Max %d)\n", MAX_CONTACTS);
return;
}
Contact *c = &ab->contacts[ab->count];
c->id = ab->next_id++;
printf("\n=== Add New Contact ===\n\n");
printf("Name: ");
fgets(c->name, NAME_LEN, stdin);
c->name[strcspn(c->name, "\n")] = '\0';
if (strlen(c->name) == 0) {
printf("Name is required. Addition cancelled.\n");
return;
}
printf("Phone: ");
fgets(c->phone, PHONE_LEN, stdin);
c->phone[strcspn(c->phone, "\n")] = '\0';
printf("Email: ");
fgets(c->email, EMAIL_LEN, stdin);
c->email[strcspn(c->email, "\n")] = '\0';
ab->count++;
printf("\nContact '%s' added. (ID: %d)\n", c->name, c->id);
}
void list_contacts(AddressBook *ab) {
printf("\n=== Contact List === (Total: %d)\n", ab->count);
if (ab->count == 0) {
printf("\nNo saved contacts.\n");
return;
}
printf("\n%-4s | %-15s | %-15s | %-20s\n", "ID", "Name", "Phone", "Email");
printf("-----|-----------------|-----------------|---------------------\n");
for (int i = 0; i < ab->count; i++) {
Contact *c = &ab->contacts[i];
printf("%-4d | %-15s | %-15s | %-20s\n",
c->id, c->name, c->phone, c->email);
}
}
void search_contact(AddressBook *ab) {
char keyword[NAME_LEN];
int found = 0;
printf("\n=== Search Contact ===\n\n");
printf("Search term: ");
fgets(keyword, NAME_LEN, stdin);
keyword[strcspn(keyword, "\n")] = '\0';
if (strlen(keyword) == 0) {
printf("Please enter a search term.\n");
return;
}
printf("\nSearch Results:\n");
printf("-----------------------------------------------------\n");
for (int i = 0; i < ab->count; i++) {
Contact *c = &ab->contacts[i];
// Search in name, phone, email
if (strstr(c->name, keyword) != NULL ||
strstr(c->phone, keyword) != NULL ||
strstr(c->email, keyword) != NULL) {
printf("ID: %d\n", c->id);
printf(" Name: %s\n", c->name);
printf(" Phone: %s\n", c->phone);
printf(" Email: %s\n", c->email);
printf("-----------------------------------------------------\n");
found++;
}
}
if (found == 0) {
printf("No results for '%s'.\n", keyword);
} else {
printf("%d result(s) found\n", found);
}
}
void edit_contact(AddressBook *ab) {
int id;
char input[EMAIL_LEN];
printf("\n=== Edit Contact ===\n\n");
printf("Contact ID to edit: ");
scanf("%d", &id);
clear_input_buffer();
int idx = find_by_id(ab, id);
if (idx == -1) {
printf("Contact with that ID not found.\n");
return;
}
Contact *c = &ab->contacts[idx];
printf("\nCurrent info:\n");
printf(" Name: %s\n", c->name);
printf(" Phone: %s\n", c->phone);
printf(" Email: %s\n", c->email);
printf("\nEnter new info (leave blank to keep current):\n");
printf("Name [%s]: ", c->name);
fgets(input, NAME_LEN, stdin);
input[strcspn(input, "\n")] = '\0';
if (strlen(input) > 0) {
strcpy(c->name, input);
}
printf("Phone [%s]: ", c->phone);
fgets(input, PHONE_LEN, stdin);
input[strcspn(input, "\n")] = '\0';
if (strlen(input) > 0) {
strcpy(c->phone, input);
}
printf("Email [%s]: ", c->email);
fgets(input, EMAIL_LEN, stdin);
input[strcspn(input, "\n")] = '\0';
if (strlen(input) > 0) {
strcpy(c->email, input);
}
printf("\nContact updated.\n");
}
void delete_contact(AddressBook *ab) {
int id;
printf("\n=== Delete Contact ===\n\n");
printf("Contact ID to delete: ");
scanf("%d", &id);
clear_input_buffer();
int idx = find_by_id(ab, id);
if (idx == -1) {
printf("Contact with that ID not found.\n");
return;
}
printf("Delete '%s'? (y/n): ", ab->contacts[idx].name);
char confirm;
scanf(" %c", &confirm);
clear_input_buffer();
if (confirm != 'y' && confirm != 'Y') {
printf("Deletion cancelled.\n");
return;
}
// Delete: shift elements forward
for (int i = idx; i < ab->count - 1; i++) {
ab->contacts[i] = ab->contacts[i + 1];
}
ab->count--;
printf("Contact deleted.\n");
}
int save_to_file(AddressBook *ab) {
FILE *fp = fopen(FILENAME, "wb");
if (fp == NULL) {
printf("Save failed: Could not open file.\n");
return -1;
}
// Save metadata
fwrite(&ab->count, sizeof(int), 1, fp);
fwrite(&ab->next_id, sizeof(int), 1, fp);
// Save contacts
fwrite(ab->contacts, sizeof(Contact), ab->count, fp);
fclose(fp);
return 0;
}
int load_from_file(AddressBook *ab) {
FILE *fp = fopen(FILENAME, "rb");
if (fp == NULL) {
// No file means fresh start
return -1;
}
// Read metadata
fread(&ab->count, sizeof(int), 1, fp);
fread(&ab->next_id, sizeof(int), 1, fp);
// Read contacts
fread(ab->contacts, sizeof(Contact), ab->count, fp);
fclose(fp);
return 0;
}
int find_by_id(AddressBook *ab, int id) {
for (int i = 0; i < ab->count; i++) {
if (ab->contacts[i].id == id) {
return i;
}
}
return -1;
}
void clear_input_buffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
Compile and Run¶
gcc -Wall -Wextra -std=c11 addressbook.c -o addressbook
./addressbook
Example Output¶
Loaded existing data. (3 contacts)
===============================
| Address Book |
===============================
-------------------------
| 1. Add Contact |
| 2. View List |
| 0. Exit |
-------------------------
Choice: 2
=== Contact List === (Total: 3)
ID | Name | Phone | Email
-----|-----------------|-----------------|---------------------
1 | John Doe | 010-1234-5678 | john@email.com
2 | Jane Smith | 010-9876-5432 | jane@email.com
3 | Bob Wilson | 010-5555-5555 | bob@email.com
Summary¶
| Concept | Description |
|---|---|
typedef struct |
Struct type definition |
strcpy, strstr |
String copy, search |
fopen, fclose |
File open/close |
fread, fwrite |
Binary read/write |
fprintf, fscanf |
Text read/write |
| Struct pointer | Access members with -> operator |
Exercises¶
-
Sort feature: Add sorting by name or ID
-
CSV export: Export contacts to CSV file
c // contacts.csv // id,name,phone,email // 1,John Doe,010-1234-5678,john@email.com -
Group feature: Add group property to contacts (Family, Friends, Work)
Next Step¶
06_Project_Dynamic_Array.md -> Let's learn about dynamic memory allocation!