day24 目錄遍歷、雙向鏈表、棧
顯示指定目錄下的所有 .h
文件
功能描述
遍歷指定目錄(遞歸進入子目錄),查找所有以 .h
為后綴的頭文件,將其完整路徑(路徑 + 文件名
)存儲到雙向鏈表中,并正向或反向打印輸出。
核心技術點
- 使用
opendir
/readdir
/closedir
進行目錄遍歷 - 使用
DT_DIR
判斷是否為目錄,實現遞歸 - 使用
sprintf
拼接路徑字符串 - 使用
strcmp
判斷文件后綴是否為.h
- 使用雙向鏈表動態存儲匹配的文件路徑
雙向鏈表結構定義(DouLink.h
)
#ifndef _DOULINK_H_
#define _DOULINK_H_// 定義通用數據類型,用于存儲文件路徑
typedef struct person {char path[512]; // 存儲文件的完整路徑(含目錄)
} DATATYPE;// 雙向鏈表節點
typedef struct dou_node {DATATYPE data; // 數據域struct dou_node *prev; // 指向前一個節點struct dou_node *next; // 指向后一個節點
} DouLinkNode;// 雙向鏈表管理結構
typedef struct list {DouLinkNode *head; // 指向鏈表頭節點int clen; // 當前鏈表長度
} DouLinkList;// 遍歷方向枚舉
typedef enum {DIR_FORWARD, // 正向遍歷DIR_BACKWARD // 反向遍歷
} DIRECT;// 函數指針類型,用于查找、刪除等操作
typedef int (*PFUN)(DATATYPE* data, void* arg);// 函數聲明
DouLinkList *CreateDouLinkList();
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos);
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int ModifyDouLinkList(DouLinkList *dl, char *name, DATATYPE *newdata);
int DeleteDouLinkList(DouLinkList *dl, char *name);
int GetSizeDouLinkList(DouLinkList *dl);
int IsEmptyDouLinkList(DouLinkList *dl);
int DestroyDouLinkList(DouLinkList *dl);
int ShowDouLinkList(DouLinkList *dl, DIRECT direct);#endif // _DOULINK_H_
? 說明:該頭文件定義了通用的雙向鏈表結構,適用于多種數據類型。當前用于存儲
.h
文件路徑。
雙向鏈表實現(DouLink.c
)
#include "DouLink.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 創建空雙向鏈表
DouLinkList *CreateDouLinkList() {DouLinkList *dl = malloc(sizeof(DouLinkList));if (NULL == dl) {perror("CreateDouLinkList malloc");return NULL;}dl->head = NULL;dl->clen = 0;return dl;
}// 頭插法插入節點
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data) {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertHeadDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;if (IsEmptyDouLinkList(dl)) {dl->head = newnode;} else {newnode->next = dl->head;dl->head->prev = newnode;dl->head = newnode;}dl->clen++;return 0;
}// 正向或反向打印鏈表
int ShowDouLinkList(DouLinkList *dl, DIRECT direct) {DouLinkNode *tmp = dl->head;if (DIR_FORWARD == direct) {while (tmp) {printf("%s\n", tmp->data.path); // 輸出文件路徑tmp = tmp->next;}} else {// 找到最后一個節點while (tmp && tmp->next) {tmp = tmp->next;}// 從尾向前遍歷while (tmp) {printf("%s\n", tmp->data.path);tmp = tmp->prev;}}return 0;
}// 判斷鏈表是否為空
int IsEmptyDouLinkList(DouLinkList *dl) {return 0 == dl->clen;
}// 尾插法插入節點
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data) {if (IsEmptyDouLinkList(dl)) {return InsertHeadDouLinkList(dl, data);} else {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertTailDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;DouLinkNode *tmp = dl->head;while (tmp->next) {tmp = tmp->next;}newnode->prev = tmp;tmp->next = newnode;}dl->clen++;return 0;
}// 在指定位置插入節點
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos) {int size = GetSizeDouLinkList(dl);if (pos < 0 || pos > size) {printf("InsertPosDouLinkList pos error\n");return 1;}if (0 == pos) {return InsertHeadDouLinkList(dl, data);} else if (size == pos) {return InsertTailDouLinkList(dl, data);} else {DouLinkNode *newnode = malloc(sizeof(DouLinkNode));if (NULL == newnode) {perror("InsertPosDouLinkList malloc");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;newnode->prev = NULL;DouLinkNode *tmp = dl->head;while (pos--) {tmp = tmp->next;}newnode->next = tmp;newnode->prev = tmp->prev;tmp->prev = newnode;newnode->prev->next = newnode;dl->clen++;}return 0;
}// 通用查找函數:通過函數指針匹配數據
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg) {DouLinkNode* tmp = dl->head;while (tmp) {if (fun(&tmp->data, arg)) {return tmp;}tmp = tmp->next;}return NULL;
}// 獲取鏈表長度
int GetSizeDouLinkList(DouLinkList *dl) {return dl->clen;
}// 銷毀鏈表(釋放所有節點和鏈表結構)
int DestroyDouLinkList(DouLinkList *dl) {DouLinkNode *tmp = NULL;while (dl->head) {tmp = dl->head;dl->head = dl->head->next;free(tmp);}free(dl);return 0;
}
? 理想運行結果:
CreateDouLinkList()
返回一個空鏈表指針,head=NULL
,clen=0
- 插入操作后鏈表長度遞增,
ShowDouLinkList
可按正/反順序輸出路徑DestroyDouLinkList
釋放所有內存,無泄漏
目錄遍歷與頭文件查找(main.c
)
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DouLink.h"// 指定起始路徑(可修改)
char path[] = "/home/linux/20250818cd";// 判斷文件是否為 .h 后綴
int is_headfile(char* path) {if (strlen(path) < 3) return 0;char* tmp = &path[strlen(path) - 2]; // 取最后兩個字符前的位置if (0 == strcmp(tmp, ".h")) {return 1;}return 0;
}// 遞歸遍歷目錄,將 .h 文件路徑插入鏈表
int do_ls(char* path, DouLinkList* dl) {DIR* dir = opendir(path);if (NULL == dir) {perror("opendir");return 1;}struct dirent* info;while ((info = readdir(dir)) != NULL) {char newpath[512] = {0};sprintf(newpath, "%s/%s", path, info->d_name); // 拼接完整路徑if (info->d_type == DT_DIR) {// 忽略 . 和 ..if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {continue;}// 遞歸進入子目錄do_ls(newpath, dl);} else {// 非目錄,檢查是否為 .h 文件DATATYPE data = {0};if (is_headfile(newpath)) {strcpy(data.path, newpath);InsertHeadDouLinkList(dl, &data); // 頭插存儲}}}closedir(dir);return 0;
}// 主函數:創建鏈表、遍歷目錄、打印結果
int main(int argc, char** argv) {DouLinkList* dl = CreateDouLinkList();do_ls("/home/linux", dl); // 從 /home/linux 開始遞歸查找printf("=== 所有 .h 文件路徑(正向輸出)===\n");ShowDouLinkList(dl, DIR_FORWARD);DestroyDouLinkList(dl);return 0;
}
? 理想運行結果示例:
=== 所有 .h 文件路徑(正向輸出)=== /home/linux/20250818cd/test.h /home/linux/20250818cd/include/utils.h /home/linux/20250818cd/module/config.h
- 成功遞歸進入所有子目錄
- 正確識別
.h
文件并存儲完整路徑- 輸出順序取決于插入方式(頭插 → 逆序)
雙向鏈表(通用版)
功能說明
該雙向鏈表支持插入、刪除、查找、修改、遍歷等操作,適用于存儲結構化數據(如學生信息)。
數據結構定義(DouLink.h
)
#ifndef _DOULINK_H_
#define _DOULINK_H_typedef struct person {char name[32];char sex;int age;int score;
} DATATYPE;typedef struct dou_node {DATATYPE data;struct dou_node *prev;struct dou_node *next;
} DouLinkNode;typedef struct list {DouLinkNode *head;int clen;
} DouLinkList;typedef enum {DIR_FORWARD,DIR_BACKWARD
} DIRECT;typedef int (*PFUN)(DATATYPE* data, void* arg);// 函數聲明(略去重復部分)
DouLinkList *CreateDouLinkList();
int InsertHeadDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertTailDouLinkList(DouLinkList *dl, DATATYPE *data);
int InsertPosDouLinkList(DouLinkList *dl, DATATYPE *data, int pos);
DouLinkNode *FindDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int ModifyDouLinkList(DouLinkList *dl, PFUN fun, void* arg, DATATYPE *newdata);
int DeleteDouLinkList(DouLinkList *dl, PFUN fun, void* arg);
int GetSizeDouLinkList(DouLinkList *dl);
int IsEmptyDouLinkList(DouLinkList *dl);
int DestroyDouLinkList(DouLinkList *dl);
int ShowDouLinkList(DouLinkList *dl, DIRECT direct);#endif
? 說明:此版本
DATATYPE
包含姓名、性別、年齡、成績,適用于人員管理。
鏈表操作實現(DouLink.c
)
CreateDouLinkList
、InsertHead/InsertTail/InsertPos
、ShowDouLinkList
、IsEmpty
、GetSize
實現同上(略)- 新增
ModifyDouLinkList
、DeleteDouLinkList
、DestroyDouLinkList
// 根據條件修改節點數據
int ModifyDouLinkList(DouLinkList *dl, PFUN fun, void *arg, DATATYPE *newdata) {DouLinkNode *tmp = FindDouLinkList(dl, fun, arg);if (NULL == tmp) {printf("ModifyDouLinkList failure...\n");return 1;}memcpy(&tmp->data, newdata, sizeof(DATATYPE));return 0;
}// 根據條件刪除節點
int DeleteDouLinkList(DouLinkList *dl, PFUN fun, void *arg) {DouLinkNode *tmp = FindDouLinkList(dl, fun, arg);if (NULL == tmp) {printf("del failure...\n");return 1;}if (tmp == dl->head) {dl->head = dl->head->next;if (dl->head) dl->head->prev = NULL;} else {if (tmp->next) tmp->next->prev = tmp->prev;tmp->prev->next = tmp->next;}free(tmp);dl->clen--;return 0;
}// 銷毀整個鏈表
int DestroyDouLinkList(DouLinkList *dl) {DouLinkNode *tmp;while (dl->head) {tmp = dl->head;dl->head = dl->head->next;free(tmp);}free(dl);return 0;
}
測試程序(test.c
)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "DouLink.h"// 按姓名查找
int findperbyname(DATATYPE* data, void* arg) {return 0 == strcmp(data->name, (char*)arg);
}// 按年齡查找
int findperbyage(DATATYPE* data, void* arg) {return data->age == *(int*)arg;
}int main(int argc, char** argv) {DouLinkList* dl = CreateDouLinkList();DATATYPE data[] = {{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92},{"zhangfei", 'm', 50, 80},};InsertHeadDouLinkList(dl, &data[0]);InsertHeadDouLinkList(dl, &data[1]);InsertHeadDouLinkList(dl, &data[2]);printf("------------正向輸出--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);printf("------------反向輸出--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);InsertTailDouLinkList(dl, &data[3]);printf("------------尾插后--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);InsertPosDouLinkList(dl, &data[4], 2);printf("------------位置插入后--------------------\n");ShowDouLinkList(dl, DIR_FORWARD);int age = 50;DouLinkNode* tmp = FindDouLinkList(dl, findperbyage, &age);if (tmp) {printf("找到年齡為50的人: %s, 成績: %d\n", tmp->data.name, tmp->data.score);}ModifyDouLinkList(dl, findperbyname, "lisi", &data[5]);printf("------------修改后--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);DeleteDouLinkList(dl, findperbyname, "liubei");printf("------------刪除后--------------------\n");ShowDouLinkList(dl, DIR_BACKWARD);DestroyDouLinkList(dl);return 0;
}
? 理想運行結果:
- 正確插入、查找、修改、刪除
- 輸出格式:
name:xxx sex:x age:xx score:xx
- 修改后
lisi
變為zhangfei
,liubei
被刪除
順序表與鏈表對比
對比項 | 順序表 | 鏈表 |
---|---|---|
存儲方式 | 連續內存空間 | 物理上不連續,邏輯上連續 |
查找性能 | O(1) —— 支持隨機訪問 | O(n) —— 需遍歷 |
插入/刪除 | O(n) —— 需移動元素 | O(1) —— 只需修改指針(已知位置) |
空間分配 | 靜態/固定大小,易浪費或溢出 | 動態分配,靈活,無空間浪費 |
循環鏈表
- 將單鏈表最后一個節點的
next
指針指向頭節點或第一個有效節點,形成環狀結構。 - 空表判斷:
head == NULL
- 非空表遍歷條件:
p->next != head
(代替p->next != NULL
) - 常用于約瑟夫環、循環任務調度等場景。
雙向鏈表(結構簡寫版)
typedef struct DulNode {ElemType data; // 數據域struct DulNode *pri; // 指向前驅struct DulNode *next; // 指向后繼
} DulNode, *DuLinkList;
? 注意:原始代碼中
sturct
拼寫錯誤,已修正為struct
棧(Stack)
定義
限定僅在表尾進行插入和刪除操作的線性表,遵循 “先進后出”(LIFO) 原則。
核心概念
- 棧頂:允許操作的一端(插入/刪除)
- 棧底:不允許操作的一端
- 基本操作:
- 入棧(Push)
- 出棧(Pop)
- 獲取棧頂元素(GetTop)
分類
- 順序棧(數組實現)
- 鏈式棧(鏈表實現)
鏈式棧結構定義(LinkStack.h
)
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_typedef struct {char name[50];char sex;int age;int score;
} DATATYPE;typedef struct _linkstacknode {DATATYPE data;struct _linkstacknode *next;
} LinkStackNode;typedef struct {LinkStackNode* top; // 棧頂指針int clen; // 元素個數
} LinkStack;// 函數聲明
LinkStack* CreateLinkStack();
int PushLinkStack(LinkStack* ls, DATATYPE* data);
int PopLinkStack(LinkStack* ls);
DATATYPE* GetTopLinkStack(LinkStack* ls);
int IsEmptyLinkStack(LinkStack* ls);
int GetSizeLinkStack(LinkStack* ls);
int DestroyLinkStack(LinkStack* ls);#endif
鏈式棧實現(LinkStack.c
)
#include "LinkStack.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>LinkStack* CreateLinkStack() {LinkStack* ls = malloc(sizeof(LinkStack));if (!ls) {perror("CreateLinkStack malloc error");return NULL;}ls->top = NULL;ls->clen = 0;return ls;
}int PushLinkStack(LinkStack* ls, DATATYPE* data) {LinkStackNode* newnode = malloc(sizeof(LinkStackNode));if (!newnode) {perror("PushLinkStack malloc error");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = ls->top;ls->top = newnode;ls->clen++;return 0;
}int PopLinkStack(LinkStack* ls) {if (IsEmptyLinkStack(ls)) {printf("linkstack is empty\n");return 1;}LinkStackNode* tmp = ls->top;ls->top = ls->top->next;free(tmp);ls->clen--;return 0;
}DATATYPE* GetTopLinkStack(LinkStack* ls) {if (IsEmptyLinkStack(ls)) return NULL;return &ls->top->data;
}int IsEmptyLinkStack(LinkStack* ls) {return 0 == ls->clen;
}int GetSizeLinkStack(LinkStack* ls) {return ls->clen;
}int DestroyLinkStack(LinkStack* ls) {while (!IsEmptyLinkStack(ls)) {PopLinkStack(ls);}free(ls);return 0;
}
測試程序(test_stack.c
)
#include <stdio.h>
#include "LinkStack.h"int main() {LinkStack* ls = CreateLinkStack();DATATYPE data[] = {{"zhangsan", 'm', 20, 80},{"lisi", 'f', 23, 84},{"wangmaizi", 'f', 32, 90},{"guanerge", 'm', 50, 91},{"liubei", 'm', 51, 92}};for (int i = 0; i < 5; i++) {PushLinkStack(ls, &data[i]);}printf("出棧順序(LIFO):\n");while (!IsEmptyLinkStack(ls)) {DATATYPE* tmp = GetTopLinkStack(ls);printf("name:%s score:%d\n", tmp->name, tmp->score);PopLinkStack(ls);}DestroyLinkStack(ls);return 0;
}
? 理想運行結果:
出棧順序(LIFO): name:liubei score:92 name:guanerge score:91 name:wangmaizi score:90 name:lisi score:84 name:zhangsan score:80
使用棧實現 C 源文件符號匹配
原理
- 遇到
(
、[
、{
入棧 - 遇到
)
、]
、}
檢查棧頂是否匹配,匹配則出棧,否則報錯 - 文件結束時棧應為空,否則有未閉合符號
實現代碼(check_brackets.c
)
#include <stdio.h>
#include <string.h>
#include "LinkStack.h"// 記錄符號位置的結構
typedef struct {char c;int row;int col;
} SymData;int do_check(char* linebuf, LinkStack* ls, int num) {char* tmp = linebuf;SymData data = {0};SymData* top = NULL;int col = 1;while (*tmp) {switch (*tmp) {case '(':case '[':case '{':data.c = *tmp;data.row = num;data.col = col;PushLinkStack(ls, (DATATYPE*)&data);break;case ')':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '(') {printf("括號不匹配: ) at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;case ']':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '[') {printf("括號不匹配: ] at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;case '}':top = (SymData*)GetTopLinkStack(ls);if (!top || top->c != '{') {printf("括號不匹配: } at row %d col %d\n", num, col);return 1;}PopLinkStack(ls);break;}tmp++;col++;}return 0;
}int main() {LinkStack* ls = CreateLinkStack();FILE* fp = fopen("/home/linux/2.c", "r");if (!fp) {printf("fopen error\n");return 1;}int num = 1;char buf[1024];while (fgets(buf, sizeof(buf), fp)) {if (do_check(buf, ls, num)) {fclose(fp);DestroyLinkStack(ls);return 1;}num++;}if (IsEmptyLinkStack(ls)) {printf("? 所有括號匹配成功!\n");} else {SymData* top = (SymData*)GetTopLinkStack(ls);printf("? 存在未閉合符號: %c at row %d col %d\n", top->c, top->row, top->col);}fclose(fp);DestroyLinkStack(ls);return 0;
}
? 理想運行結果:
- 若括號全部匹配:輸出
? 所有括號匹配成功!
- 若有不匹配:輸出具體錯誤位置
- 支持跨行匹配
棧的核心要點總結
- 本質:受限制的線性表,僅允許在一端進行操作
- 操作特性:先進后出(LIFO)
- 兩端定義:
- 棧頂:可進行插入/刪除
- 棧底:固定,不可操作
- 主要作用:
- 遞歸調用管理
- 表達式求值
- 回溯算法
- 符號匹配檢查
- 基本操作:
- 入棧(Push)
- 出棧(Pop)
- 獲取棧頂(GetTop)