链表操作练习

要求

现在有一个双向链表,里面要保存歌曲的名字;例如 蔡琴/渡口.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 中。

遍历链表

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;
​
    //    其他/以后的以后.mp3
    char *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 的声明通过链接器关联,无需头文件介入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值