首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言數據結構實戰:單向鏈表實現通訊錄(一)

     編輯:關於C語言

專案背景

「菜鳥修煉C語言」系列第三彈,使用單向鏈表實現通訊錄核心功能。重點練習 typedef、巨集定義、鏈表節點增刪查、多檔案模組化編譯。

功能需求

  • 聯系人:代號(6 字元英數混合)、電話(18 字元含符號)
  • 支援:新增(尾插)、刪除(按姓名)、顯示全部、清空、選單操作
  • 巨集動態分配節點記憶體
  • 三模組劃分:main.c(主程式/選單)、record.h(資料結構/巨集)、opre.c(鏈表操作)

資料結構與巨集設計

/* record.h */
#ifndef _RECORD_H_
#define _RECORD_H_

typedef struct {
    char name[8];
    char tel[20];
} DATA;

typedef struct node {
    DATA data;
    struct node *next;
} ADDR;

/* 巨集:安全分配節點 */
#define ASK(p) do { \
    p = (ADDR *)malloc(sizeof(ADDR)); \
    if (!p) { printf("malloc failed!"); exit(-1); } \
} while(0)

#endif

主程式邏輯

/* main.c */
#include 
#include "record.h"

int menu_select(void);
void hand_menu(int cmd, ADDR *list_head);

int main() {
    int cmd = 0;
    ADDR *list_head;
    ASK(list_head);      // 建立頭節點
    list_head->next = NULL;

    while (1) {
        cmd = menu_select();
        if (cmd == '0') return 0;
        hand_menu(cmd, list_head);
    }
}

int menu_select() {
    int select;
    printf("\n<------通訊錄------>\n");
    printf("1:新增  2:刪除  3:顯示  0:退出\n輸入: ");
    select = getchar(); while (getchar()!='\n');
    while (select < '0' || select > '3') {
        printf("錯誤,重新輸入: ");
        select = getchar(); while (getchar()!='\n');
    }
    return select;
}

void hand_menu(int cmd, ADDR *list_head) {
    switch (cmd) {
        case '1': add_person(list_head); break;
        case '2': list_head = del_person(list_head); break;  // 可能刪除頭節點
        case '3': dis_person(list_head); break;
    }
}

鏈表核心操作實現

尾插法新增聯系人

/* opre.c */
#include 
#include 
#include "record.h"

void add_person(ADDR *node) {
    ADDR *new_p;
    ASK(new_p);
    new_p->next = NULL;

    printf("姓名: "); scanf("%s", new_p->data.name);
    printf("電話: "); scanf("%s", new_p->data.tel);

    while (node->next) node = node->next;  // 找尾節點
    node->next = new_p;
}

按姓名刪除節點


ADDR *del_person(ADDR *node) {  // 返回新頭節點
    char name[8];
    ADDR *pre = NULL;

    printf("輸入刪除姓名: "); scanf("%s", name);

    while (node) {
        if (!strcmp(node->data.name, name)) {
            if (pre) pre->next = node->next;
            free(node);
            printf("刪除成功!\n");
            return (pre) ? node : node->next;  // 簡化:實際需傳回頭指標
        }
        pre = node;
        node = node->next;
    }
    printf("未找到!\n");
    return node;  // 原版邏輯有缺陷,見下文改進
}

遞迴顯示(逆序輸出)


void dis_person(ADDR *node) {
    if (!node) return;
    node = node->next;
    dis_person(node);          // 先遞迴到尾部
    if (node)
        printf("姓名:%s 號碼:%s\n", node->data.name, node->data.tel);
}

代碼審查與改進點

問題原代碼改進建議
刪除頭節點處理pre 初始為 NULL,刪頭節點時 pre->next 解引用空指標傳入 ADDR **head 或返回新頭指標
遞迴顯示風險資料量大時棧溢位改用迴圈遍歷
輸入緩衝區scanf("%s") 無長度限制用 scanf("%7s", name) 防溢位
記憶體洩漏程式退出未釋放鏈表加入 free_list 函式

多檔案編譯指令

gcc main.c opre.c -o address_book
# 或
cl main.c opre.c /Fe address_book.exe  (MSVC)

延伸練習

  1. 修正刪除函式,正確處理頭節點刪除
  2. 新增「查詢聯系人」功能(按姓名/電話)
  3. 將遞迴顯示改為迴圈,並支援正序/逆序切換
  4. 引入動態陣列實現同樣功能,比較優劣
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved