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

C++ 結構體動態內存管理與文件持久化完整實戰教程

     編輯:C++ 入門

專案背景

本教程演示如何在 C++ 中設計包含動態成員(字符串、字符串陣列)的結構體,實現完整的**內存申請、數據輸入、文件寫入、鍵值讀取、內存釋放**全生命周期管理。案例模擬「教師-學生」關係:固定 5 位教師,每位教師 3 名學生,數據持久化至文本文件並支持鍵值查詢。

核心數據結構設計

#define TEACHER_COUNT 5
#define STUDENT_PER_TEACHER 3
#define FILE_NAME "f:/teacher_data.txt"
#define MAX_LINE_LEN 1024
#define MAX_KEY_LEN 20

struct AdvTeacher {
    char* name;        // 教師姓名
    char* title;       // 職稱
    int  age;          // 年齡
    char* addr;        // 地址
    char** student;    // 學生姓名陣列(動態二維)
};

所有字符串採用 char* 動態分配,student 為二級指針指向字符串指針陣列。

四大核心模塊實現

1. 內存管理:創建與銷毀

// 創建結構體陣列及所有動態成員
int CreateStructArray(AdvTeacher** t, int teacherCnt, int studentCnt) {
    if (!t) return -1;
    AdvTeacher* arr = (AdvTeacher*)malloc(teacherCnt * sizeof(AdvTeacher));
    if (!arr) return -2;

    for (int i = 0; i < teacherCnt; ++i) {
        arr[i].name  = (char*)malloc(256);
        arr[i].title = (char*)malloc(256);
        arr[i].addr  = (char*)malloc(256);
        if (!arr[i].name || !arr[i].title || !arr[i].addr) return -3;

        arr[i].student = (char**)malloc(studentCnt * sizeof(char*));
        if (!arr[i].student) return -4;
        for (int j = 0; j < studentCnt; ++j) {
            arr[i].student[j] = (char*)malloc(256);
            if (!arr[i].student[j]) return -5;
        }
    }
    *t = arr;
    return 0;
}

// 遞歸銷毀:逆序釋放,防洩漏
int FreeStructArray(AdvTeacher** t, int teacherCnt, int studentCnt) {
    if (!t || !*t) return 0;
    AdvTeacher* arr = *t;
    for (int i = 0; i < teacherCnt; ++i) {
        for (int j = 0; j < studentCnt; ++j)
            free(arr[i].student[j]);
        free(arr[i].student);
        free(arr[i].name);
        free(arr[i].title);
        free(arr[i].addr);
    }
    free(arr);
    *t = nullptr;  // 置空防野指針
    return 0;
}

關鍵點:雙重指針參數 AdvTeacher** 實現「輸出參數」語義;嚴格逆序釋放;malloc 失敗即時返回錯誤碼並清理已分配內存(生產代碼建議用 RAII 封裝)。

2. 文件寫入:結構化文本格式

int StructWriteFile(const char* fileName, const AdvTeacher* t, int teacherCnt, int studentCnt) {
    FILE* fp = fopen(fileName, "w+");
    if (!fp) return -1;

    char buf[1024];
    for (int i = 0; i < teacherCnt; ++i) {
        fprintf(fp, "name%d = %s\n", i+1, t[i].name);
        fprintf(fp, "age%d = %d\n", i+1, t[i].age);
        fprintf(fp, "title%d = %s\n", i+1, t[i].title);
        fprintf(fp, "addr%d = %s\n", i+1, t[i].addr);
        for (int j = 0; j < studentCnt; ++j) {
            fprintf(fp, "%dstudentname%d = %s\n", i+1, j+1, t[i].student[j]);
        }
    }
    fclose(fp);
    return 0;
}

採用 key = value 行式格式,鍵名編碼層級關係(如 2studentname3 表示第 2 位教師的第 3 名學生),便於後續解析。

3. 文件讀取:鍵值精確匹配

int StructFileRead(const char* fileName, const char* key, char* value, int* valueLen) {
    FILE* fp = fopen(fileName, "r");
    if (!fp) return -2;

    char line[MAX_LINE_LEN];
    while (fgets(line, sizeof(line), fp)) {
        char* eq = strchr(line, '=');
        if (!eq) continue;                    // 非鍵值行
        if (!strstr(line, key)) continue;     // 鍵不匹配

        // 定位 value 起始(跳過 '=' 及空格)
        char* p = eq + 1;
        while (*p == ' ') ++p;
        if (*p == '\n') { fclose(fp); return -3; } // 無值

        // 定位 value 結束(空格或換行)
        char* end = p;
        while (*end && *end != ' ' && *end != '\n') ++end;

        *valueLen = end - p;
        memcpy(value, p, *valueLen);
        value[*valueLen] = '\0';
        fclose(fp);
        return 0;
    }
    fclose(fp);
    return -4; // 未找到鍵
}

單次掃描文件,strstr 匹配鍵名,手動解析值邊界,避免正則表達式依賴。

4. 交互式查詢菜單

void ReadFileMenu() {
    char key[256], value[1024];
    int  valueLen;
    while (true) {
        system("cls");
        puts("=== 讀取文件菜單 ===");
        puts("格式示例: name1 (第1位教師姓名)");
        puts("格式示例: 1studentname2 (第1位教師第2名學生)");
        puts("輸入 quit 退出");
        printf("請輸入查詢鍵: ");
        scanf("%s", key);
        if (strcmp(key, "quit") == 0) break;
        if (strlen(key) < 4) { puts("鍵名太短"); system("pause"); continue; }

        strcpy(value, "不存在匹配項"); // 預設值
        if (StructFileRead(FILE_NAME, key, value, &valueLen) == 0)
            printf("\n%s = %s\n\n", key, value);
        else
            puts("查詢失敗");
        system("pause");
    }
}

主流程串聯

int main() {
    AdvTeacher* teachers = nullptr;
    if (CreateStructArray(&teachers, TEACHER_COUNT, STUDENT_PER_TEACHER) != 0) return -1;

    // 用戶輸入數據
    for (int i = 0; i < TEACHER_COUNT; ++i) {
        printf("教師 %d 姓名: ", i+1); scanf("%s", teachers[i].name);
        printf("教師 %d 年齡: ", i+1); scanf("%d", &teachers[i].age);
        printf("教師 %d 職稱: ", i+1); scanf("%s", teachers[i].title);
        printf("教師 %d 地址: ", i+1); scanf("%s", teachers[i].addr);
        for (int j = 0; j < STUDENT_PER_TEACHER; ++j) {
            printf("  學生 %d 姓名: ", j+1); scanf("%s", teachers[i].student[j]);
        }
    }

    StructWriteFile(FILE_NAME, teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
    ReadFileMenu();
    FreeStructArray(&teachers, TEACHER_COUNT, STUDENT_PER_TEACHER);
    return 0;
}

現代化改造建議(C++11/17/20)

原始做法現代替代優勢
char* + malloc/freestd::string自動內存管理、避免洩漏/越界
char** 學生陣列std::vector<std::string>動態擴容、範圍檢查、範基 for
FILE* + fprintf/fscanfstd::ofstream/ifstream + nlohmann::json類型安全、結構化序列化、跨平台
手工鍵值解析std::unordered_map<std::string, std::string>O(1) 查詢、自動處理轉義
錯誤碼返回std::expected (C++23) / 異常 / std::optional強制處理錯誤、上下文豐富

總結

本教程完整展示了 C 風格手動內存管理與文件持久化的全流程,涵蓋雙重指針參數、分級分配/釋放、文本鍵值格式設計、流式解析等核心技術。雖然教學價值高,但**生產環境強烈建議使用現代 C++ 標準庫組件**,將精力集中在業務邏輯而非內存細節上。理解底層機制,是為了更好地駕馭高層抽象。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved