Contact++ v0.2 - 控制台通訊錄管理系統,核心特性:
contact.ctt)
#define C_NAME_SIZE 10
#define C_SEX_SIZE 3
#define C_AGE_SIZE 5
#define C_PHONE_SIZE 12
#define C_QQ_SIZE 12
#define C_PROVINCE_SIZE 20
#define C_CITY_SIZE 20
#define C_COMMENT_SIZE 100
struct Contact {
char c_name[C_NAME_SIZE];
char c_sex[C_SEX_SIZE];
char c_age[C_AGE_SIZE];
char c_phone[C_PHONE_SIZE];
char c_qq[C_QQ_SIZE];
char c_province[C_PROVINCE_SIZE];
char c_city[C_CITY_SIZE];
char c_comment[C_COMMENT_SIZE];
struct Contact *next; // 單鏈表指標
};
struct Contact *head = NULL, *last = NULL;
#define KEY 'x' // 密鑰字符
// 加密/解密同一函數:c ^ KEY ^ KEY = c
void xor_crypt(FILE *in, FILE *out) {
int ch;
while ((ch = fgetc(in)) != EOF) {
fputc(ch ^ KEY, out);
}
}
// 讀取並解密到鏈表
void getInfo() {
FILE *sfp = fopen("contact.ctt", "rb");
FILE *tfp = fopen("tmp.ctt", "wb"); // 臨時明文文件
xor_crypt(sfp, tfp); // 解密寫入臨時文件
fclose(sfp); fclose(tfp);
// 從臨時文件讀取構建鏈表
tfp = fopen("tmp.ctt", "rb");
while (!feof(tfp)) {
struct Contact *node = malloc(sizeof(struct Contact));
fread(node, sizeof(struct Contact), 1, tfp);
// 插入鏈表尾部...
}
fclose(tfp);
remove("tmp.ctt"); // 刪除明文臨時文件
}
// 加密保存鏈表
void saveRecords() {
FILE *tfp = fopen("tmp.ctt", "wb");
struct Contact *p = head;
while (p) {
fwrite(p, sizeof(struct Contact), 1, tfp);
p = p->next;
}
fclose(tfp);
// 加密寫入正式文件
tfp = fopen("tmp.ctt", "rb");
FILE *fp = fopen("contact.ctt", "wb");
xor_crypt(tfp, fp);
fclose(tfp); fclose(fp);
remove("tmp.ctt");
}
安全性註記:XOR 單字節密鑰極弱,僅防誤讀/輕度混淆。生產環境必須用 AES-GCM、ChaCha20-Poly1305 等認證加密。
// 尾插法插入節點
struct Contact* linkNode(struct Contact *node, struct Contact *top) {
if (!top) return node; // 空表
struct Contact *p = top;
while (p->next) p = p->next;
p->next = node;
node->next = NULL;
return top;
}
// 按姓名查找 (返回前驅節點便於刪除)
struct Contact* find(char *name) {
struct Contact *p = head, *prev = NULL;
while (p) {
if (strcmp(p->c_name, name) == 0) return prev;
prev = p; p = p->next;
}
return NULL;
}
// 刪除節點
void delRecord() {
char name[C_NAME_SIZE];
printf("輸入要刪除的姓名:"); scanf("%s", name);
struct Contact *prev = find(name);
if (!prev) { puts("未找到"); return; }
struct Contact *del = prev ? prev->next : head;
if (prev) prev->next = del->next;
else head = del->next;
free(del);
saveRecords(); // 即時持久化
}
void inputs(char *prompt, char *buf, int maxlen) {
printf("%s", prompt);
fgets(buf, maxlen, stdin);
buf[strcspn(buf, "\n")] = 0; // 去除換行符
}
int selectMenu() {
int c;
printf("\n1.添加 2.刪除 3.查找 4.修改 5.顯示全部 6.關於 7.保存 0.退出\n選擇:");
scanf("%d", &c); getchar(); // 吸收換行
return c;
}
int main() {
getInfo(); // 啟動時載入
while (1) {
switch (selectMenu()) {
case 1: insRecord(); break;
case 2: delRecord(); break;
case 3: seaRecord(); break;
case 4: editRecord(); break;
case 5: disRecords(); break;
case 6: about(); break;
case 7: saveRecords(); break;
case 0: return 0;
}
}
}
| 亮點 | 不足/風險 |
|---|---|
| 鏈表動態擴展,無固定容量限制 | 固定長度字符數組,超長輸入會截斷/溢出 |
| XOR 加密邏輯正確,臨時文件防止明文殘留 | XOR 單字節密鑰極不安全,無完整性校驗 |
| 模塊化函數設計,職責清晰 | 全局變量 head/last 耦合度高,難多實例 |
| 即時保存策略,數據不易丟失 | 頻繁整文件重寫,大數據量性能差 |
| 菜單交互友好 | 無輸入驗證 (年齡、電話格式等) |
libsodium 或 OpenSSL,使用 crypto_secretbox (XChaCha20-Poly1305) + 隨機 nonce + 金鑰派生 (Argon2id)。char* + len/cap) 替換固定數組。ContactBook 結構體,API 形式:cb_add(ContactBook*, Contact*),支援多實例、線程安全 (鎖/無鎖)。
gcc contact.c -o contact -Wall -Wextra
./contact
# 首次運行 contact.ctt 不存在,會創建空文件
Windows 下需鏈接 -lws2_32 (若用網絡功能) 或直接用 MSVC 編譯。