当前位置: 首页 > news >正文

链表操作练习

要求

现在有一个双向链表,里面要保存歌曲的名字;例如 蔡琴/渡口.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;
}
知识点:
  1. 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 的声明通过链接器关联,无需头文件介入。

相关文章:

  • 【C++】WSL常用语法
  • 电子商务商家后台运营专员模板
  • Android工厂模式
  • 设一个测试情境,新用户注册后显示的名字不完整,测试思路是怎么样的?
  • 【C/C++】inline关键词
  • 用网页显示工控仪表
  • 精益数据分析(40/126):移动应用商业模式的关键指标与盈利策略
  • 常见小模型的实现原理及使用示例:Android端
  • 黑马点评day02(缓存)
  • 常用CPU、GPU、NPU、DSP、ASIC等芯片区别介绍
  • IL2CPP 技术深度解析
  • 三星SMT贴片机选型与效能提升指南
  • 学习路线(c++)
  • 使用Mathematica绘制Sierpinski地毯
  • ZYNQ笔记(十七):IP核封装与接口定义
  • Java抽象类与接口详解
  • 关于string类的构造函数
  • 基于 jQuery 实现灵活可配置的输入框验证功能
  • 效整理文件信息!一键生成文件夹目录的工具
  • 简单理解MCP:AI如何使用工具
  • 这个五一假期,外贸拓内销好货和识货人在上海“双向奔赴”
  • 马上评|子宫肌瘤惊现男性患者,如此论文何以一路绿灯?
  • 今年五一档电影票房已破7亿
  • 国铁集团:铁路五一假期旅客发送量累计已超1亿人次
  • 谢承祥已任自然资源部总工程师
  • 人民日报今日谈:坚决克服麻痹思想,狠抓工作落实