要求
現在有一個雙向鏈表,里面要保存歌曲的名字;例如 蔡琴/渡口.mp3
我們把它定義在一個link.h文件中。
#ifndef LINK_H
#define LINK_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>typedef struct Node
{//保存歌曲的名字 char music_name[64];struct Node* next;struct Node* prior;
}Node;
#endif
這里的 #ifndef、#define 和 #endif 是 C/C++ 中的預處理指令,用于防止頭文件被重復包含。 如果 link.h 被 a.c 和 b.c 同時包含,或者在一個文件中被間接包含兩次,沒有頭文件保護會導致編譯錯誤。 #ifndef LINK_H 檢查是否沒有定義過 LINK_H 這個宏,如果沒有定義過,則執行下面的代碼;如果已經定義過,則跳過整個塊(直到 #endif)。 #define LINK_H 定義 LINK_H 這個宏。
現在我們要做的,是在這個link.h對應的link.c中實現以下的操作,(為了方便,我先給出各個操作的方法名)
1.初始化鏈表 init_link() ==》創建一個新節點,并初始化指針前后指針為NULL
2.插入節點 int insert_link(const char *name)
3.遍歷鏈表 void traverse_link()==>使用尾插法
4.根據當前歌曲找到下一首歌 find_next_music(char *cur, char *next)
==》如果歌曲存在,返回0,如果歌曲不存在,返回-1
5.根據當前歌曲找到上一首歌 void find_prior_music(char *cur, char *prior)
6.從鏈表中提取歌手的名字 void get_singer(char *s)
7.清空列表 void clear_link()
在link.c中我們需要先寫下這兩個語句,
#include "link.h"
//把main.c中華定義的頭指針引進來。
extern Node *head;
接下來開始我們的練習。。。。。。。。。。。。
初始化鏈表
創建一個新節點,并初始化指針前后指針為NULL。
注意head指針所指頭結點沒有值;
/*
返回值:創建成功返回0,否則返回-1
*/
int init_link() {head = (Node*)malloc(sizeof(Node));if (head== NULL) {printf("初始化失敗");return -1;}head->prior = NULL;head->next = NULL;return 0;
?
}
?
插入節點
思路:使用指針p遍歷鏈表至最后一個節點,新建一個節點,并對之賦值;最后將之插在鏈表尾部;
/*
參數:音樂名
返回值:操作成功返回0,否則返回-1
處理:使用尾插法插入節點
*/
int insert_link(const char* name) {Node* p = head;while (p->next) {p = p->next;}//新建一個節點Node* newNode = (Node*)malloc(sizeof(Node));if (newNode== NULL) {printf("創建節點失敗");return -1;}strcpy_s(newNode->music_name, sizeof(newNode->music_name), name);newNode->prior = p;newNode->next = NULL;p->next = newNode;return 0;
}
知識點:
-
strcpy(newNode->music_name, name);
是 C 語言中用于 字符串拷貝 的標準庫函數,其作用是將源字符串name
的內容復制到目標字符數組newNode->music_name
中。
遍歷鏈表
v
oid traverse_link() {Node* p = head->next;while (p) {printf("%s\n", p->music_name);p = p->next;}
}
根據當前歌曲找到下一首歌
思路:遍歷鏈表,找到music_name為cur的節點,返回其下一個節點中的歌曲名
int find_next_music(char *cur, char *next)
{Node *p = head->next;while (p){if (strstr(p->music_name, cur) != NULL){break;}p = p->next;}if (p->next != NULL){strcpy(next, p->next->music_name);return 0;}else {return -1;}}
}
知識點:
strstr()
是 C 語言標準庫中的一個字符串處理函數,用于在一個字符串中查找另一個字符串的首次出現位置。
提取當前鏈表中的歌手名
void get_singer(char *s)
{if (head->next == NULL)return;
?// ? 其他/以后的以后.mp3char *begin = head->next->music_name;char *p = begin;while (*p != '/')p++;
?strncpy(s, begin, p - begin);s[p - begin] = '\0'; ?// 確保字符串終止
}
從鏈表節點中提取歌手名稱并存儲到 s 指向的緩沖區中 begin 指針指向音樂文件名字符串的開頭;循環結束后,p 指向 '/' 字符的位置 使用 strncpy 從 begin 開始拷貝 p - begin 個字符到 s;p - begin 計算的是從字符串開頭到 '/' 的字符數(即歌手名的長度)
清空鏈表
void clear_link()
{Node *p = head->next;while (p){head->next = p->next;free(p);p = head->next;}
}
測試==》main.h和main.c
main.h如下:#ifndef MAIN_H
#define MAIN_H
#include "link.h"
#include<stdlib.h>
#include <stdio.h>
#endif
?
main.c如下:#include "main.h"
Node* head = NULL; ? ? ?//鏈表頭指針
int main() {//初始化鏈表if (init_link() == -1){printf("鏈表初始化失敗\n");}insert_link("蔡琴/渡口.mp3");insert_link("蔡琴/你的眼神.mp3");insert_link("蔡琴/綠島小夜曲.mp3");insert_link("蔡琴/南屏晚鐘.mp3");insert_link("蔡琴/給電影人的情書.mp3");traverse_link();char singerName[64];get_singer(singerName);printf("%s", singerName);clear_link();return 0;
}
知識點:
1.在main.c中我定義了Node* head = NULL; 在link.c。player.c等要用到鏈表的文件中我使用extern Node* head;這樣做的用意是什么?
==》
為了實現 全局變量的跨文件共享,同時避免重復定義。
ain.c 中定義:這里是全局變量的 定義(分配內存),整個程序只有這一處定義。 其他文件中聲明:告訴編譯器:"head 已經在別處定義了,直接使用它"。 如果多個 .c 文件都直接定義 Node* head,鏈接時會報錯(重復定義)。
通過 extern 聲明,確保全局變量 只定義一次,但可被多個文件訪問。
2.為什么在link.h中我沒有引入main.h,extern Node* head;還是有效,即編譯器怎么知道main.c中定義了Node* head = NULL;?
1.extern 是聲明,不是定義:它不分配內存,只是承諾“這個變量會在別處定義”。
2.類型信息來自 Node 的定義:link.h 中的 typedef struct Node {...} 提供了 Node* 的類型信息。
3.符號匹配由鏈接器完成:鏈接器通過變量名(head)匹配定義,與頭文件無關。
4.不需要 #include "main.h":main.c 的定義和 link.c 的聲明通過鏈接器關聯,無需頭文件介入。